diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index f1415f61a..8fc77d4d7 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -105,7 +105,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.npm - key: desktop-npm-cache-${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('packages/slimclaw/runtime-seed/package-lock.json', 'apps/controller/static/runtime-plugins/openclaw-weixin/package-lock.json') }} + key: desktop-npm-cache-${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('packages/slimclaw/runtime-seed/package-lock.json', 'packages/slimclaw/runtime-plugins/openclaw-weixin/package.json') }} restore-keys: | desktop-npm-cache-${{ runner.os }}-${{ matrix.arch }}- desktop-npm-cache-${{ runner.os }}- diff --git a/.github/workflows/desktop-ci-dev.yml b/.github/workflows/desktop-ci-dev.yml index 8181ba63c..f0f64110f 100644 --- a/.github/workflows/desktop-ci-dev.yml +++ b/.github/workflows/desktop-ci-dev.yml @@ -96,7 +96,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.npm - key: desktop-npm-cache-${{ runner.os }}-arm64-${{ hashFiles('packages/slimclaw/runtime-seed/package-lock.json', 'apps/controller/static/runtime-plugins/openclaw-weixin/package-lock.json') }} + key: desktop-npm-cache-${{ runner.os }}-arm64-${{ hashFiles('packages/slimclaw/runtime-seed/package-lock.json', 'packages/slimclaw/runtime-plugins/openclaw-weixin/package.json') }} restore-keys: | desktop-npm-cache-${{ runner.os }}-arm64- desktop-npm-cache-${{ runner.os }}- diff --git a/.github/workflows/desktop-ci-dist-full.yml b/.github/workflows/desktop-ci-dist-full.yml index 201226eb9..c05f212a7 100644 --- a/.github/workflows/desktop-ci-dist-full.yml +++ b/.github/workflows/desktop-ci-dist-full.yml @@ -70,7 +70,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.npm - key: desktop-npm-cache-${{ runner.os }}-arm64-${{ hashFiles('packages/slimclaw/runtime-seed/package-lock.json', 'apps/controller/static/runtime-plugins/openclaw-weixin/package-lock.json') }} + key: desktop-npm-cache-${{ runner.os }}-arm64-${{ hashFiles('packages/slimclaw/runtime-seed/package-lock.json', 'packages/slimclaw/runtime-plugins/openclaw-weixin/package.json') }} restore-keys: | desktop-npm-cache-${{ runner.os }}-arm64- desktop-npm-cache-${{ runner.os }}- @@ -151,7 +151,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.npm - key: desktop-npm-cache-${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('packages/slimclaw/runtime-seed/package-lock.json', 'apps/controller/static/runtime-plugins/openclaw-weixin/package-lock.json') }} + key: desktop-npm-cache-${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('packages/slimclaw/runtime-seed/package-lock.json', 'packages/slimclaw/runtime-plugins/openclaw-weixin/package.json') }} restore-keys: | desktop-npm-cache-${{ runner.os }}-${{ matrix.arch }}- desktop-npm-cache-${{ runner.os }}- @@ -328,7 +328,7 @@ jobs: uses: actions/cache@v4 with: path: ~\AppData\Local\npm-cache - key: desktop-npm-cache-${{ runner.os }}-x64-${{ hashFiles('packages/slimclaw/runtime-seed/package-lock.json', 'apps/controller/static/runtime-plugins/openclaw-weixin/package-lock.json') }} + key: desktop-npm-cache-${{ runner.os }}-x64-${{ hashFiles('packages/slimclaw/runtime-seed/package-lock.json', 'packages/slimclaw/runtime-plugins/openclaw-weixin/package.json') }} restore-keys: | desktop-npm-cache-${{ runner.os }}-x64- desktop-npm-cache-${{ runner.os }}- diff --git a/.github/workflows/desktop-ci-dist-lite.yml b/.github/workflows/desktop-ci-dist-lite.yml index ebf711aa7..e8bb36721 100644 --- a/.github/workflows/desktop-ci-dist-lite.yml +++ b/.github/workflows/desktop-ci-dist-lite.yml @@ -78,7 +78,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.npm - key: desktop-npm-cache-${{ runner.os }}-arm64-${{ hashFiles('packages/slimclaw/runtime-seed/package-lock.json', 'apps/controller/static/runtime-plugins/openclaw-weixin/package-lock.json') }} + key: desktop-npm-cache-${{ runner.os }}-arm64-${{ hashFiles('packages/slimclaw/runtime-seed/package-lock.json', 'packages/slimclaw/runtime-plugins/openclaw-weixin/package.json') }} restore-keys: | desktop-npm-cache-${{ runner.os }}-arm64- desktop-npm-cache-${{ runner.os }}- diff --git a/.github/workflows/desktop-release.yml b/.github/workflows/desktop-release.yml index b7b3c62e0..fee745be9 100644 --- a/.github/workflows/desktop-release.yml +++ b/.github/workflows/desktop-release.yml @@ -58,7 +58,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.npm - key: desktop-npm-cache-${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('packages/slimclaw/runtime-seed/package-lock.json', 'apps/controller/static/runtime-plugins/openclaw-weixin/package-lock.json') }} + key: desktop-npm-cache-${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('packages/slimclaw/runtime-seed/package-lock.json', 'packages/slimclaw/runtime-plugins/openclaw-weixin/package.json') }} restore-keys: | desktop-npm-cache-${{ runner.os }}-${{ matrix.arch }}- desktop-npm-cache-${{ runner.os }}- diff --git a/AGENTS.md b/AGENTS.md index f655b0936..634822437 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,7 +10,7 @@ Nexu is a desktop-first OpenClaw platform. Users create AI bots, connect them to - `apps/controller` — Single-user local control plane for Nexu config, OpenClaw sync, and runtime orchestration - `apps/desktop` — Electron desktop runtime shell and sidecar orchestrator - `apps/web` — React + Ant Design + Vite -- `packages/slimclaw` — Repo-local Nexu-owned OpenClaw runtime contract, prepared runtime root, and staging/patch ownership for local dev and desktop packaging +- `packages/slimclaw` — Repo-local Nexu-owned OpenClaw runtime contract and single source of truth for prepared runtime, builtin runtime assets, and staging/patch ownership for local dev and desktop packaging - `packages/shared` — Shared Zod schemas - `packages/dev-utils` — TS-first reusable utilities for local script tooling @@ -188,6 +188,8 @@ The desktop test suite includes real launchd integration tests that run on macOS - Config generator output must match `specs/references/openclaw-config-schema.md`. - Do not add dependencies without explicit approval. - Do not modify OpenClaw source code. +- **Historical spec/plan docs are snapshots, not living docs.** Treat `specs/**`, `docs/plans/**`, and other dated plan/spec artifacts as committed historical snapshots. Do not keep mutating them during normal documentation evolution; if the current truth changes, update live docs (`AGENTS.md`, `ARCHITECTURE.md`, `CONTRIBUTING.md`, `docs/**`) or create a new snapshot artifact instead. +- **Slimclaw is the single runtime source of truth.** `packages/slimclaw` is the only canonical owner for Nexu's embedded OpenClaw capability packaging, prepared runtime artifacts, builtin runtime plugins, and staging contract. Runtime changes should land through slimclaw by default. Any exception must be justified through a spec/plan PR and explicit team discussion before implementation. - Never commit code changes until explicitly told to do so. - Desktop packaged app: never use `npx`, `npm`, `pnpm`, or any shell command that relies on the user's PATH. The packaged Electron app has no shell profile — resolve bin paths programmatically via `require.resolve()` and execute with `process.execPath`. The app must be fully self-contained. - Windows packaging split: use `pnpm dist:win` for the full installer/release path and keep it close to CI semantics. Use `pnpm dist:win:local` for local Windows validation when you need fast iteration; it is intentionally dir-only and reuse-first, so it is not a substitute for the full release build. @@ -217,7 +219,7 @@ See `ARCHITECTURE.md` for the full bird's-eye view. Key points: - Monorepo: `apps/controller` (Hono), `apps/web` (React), `apps/desktop` (Electron), `packages/shared` (Zod schemas), `nexu-skills/` (skill repo) - Type safety: Zod -> OpenAPI -> generated frontend SDK. Never duplicate types. - Config generator: `apps/controller/src/lib/openclaw-config-compiler.ts` builds OpenClaw config from local controller state -- Local runtime flow: `apps/controller` owns Nexu config/state, writes OpenClaw config/skills/templates, and manages the slimclaw-backed OpenClaw runtime contract directly; desktop wraps that controller-first stack with Electron + web sidecars +- Local runtime flow: `apps/controller` owns Nexu config/state and writes dynamic OpenClaw config/skills/state, while `packages/slimclaw` remains the sole owner of the prepared embedded OpenClaw runtime and builtin runtime artifacts; desktop wraps that controller-first stack with Electron + web sidecars - Key data flows: local config compilation, desktop runtime boot, channel sync, file-based skill catalog ## Code style (quick reference) @@ -271,7 +273,13 @@ See `ARCHITECTURE.md` for the full bird's-eye view. Key points: ## Documentation maintenance -After significant code changes, verify documentation is current. +After significant code changes, verify the live documentation is current. + +### Live docs vs historical snapshots + +- Treat `AGENTS.md`, `ARCHITECTURE.md`, `CONTRIBUTING.md`, and `docs/**` as the living documentation set. +- Treat `specs/**`, `docs/plans/**`, and other dated design/plan artifacts as historical snapshots. Once committed, they are frozen by default and should not be used as the target for routine doc drift cleanup. +- If guidance changes, update the live docs or add a new snapshot doc that supersedes the old one. Do not silently rewrite old snapshots to look current. ### Diff baseline @@ -283,25 +291,27 @@ git diff --name-only $(git merge-base HEAD origin/main)...HEAD | Changed area | Affected docs | |---|---| -| `apps/web/src/pages/` or routing | `specs/FRONTEND.md` | -| `apps/controller/src/routes/` | `specs/references/api-patterns.md`, `specs/product-specs/*.md` | -| `apps/controller/src/runtime/` | `ARCHITECTURE.md`, `specs/RELIABILITY.md` | -| `apps/web/src/components/channel-setup/` | `specs/FRONTEND.md` | +| `apps/web/src/pages/` or routing | `docs/**`, `AGENTS.md` if command/workflow guidance changed | +| `apps/controller/src/routes/` | `ARCHITECTURE.md`, `CONTRIBUTING.md`, `docs/**` if contributor-facing behavior changed | +| `apps/controller/src/runtime/` | `ARCHITECTURE.md`, `AGENTS.md`, `docs/**` if runtime workflow changed | +| `apps/web/src/components/channel-setup/` | `docs/**` | | `nexu-skills/` | `ARCHITECTURE.md` (monorepo layout) | | `packages/shared/src/schemas/` | `ARCHITECTURE.md` (type safety) | +| `packages/slimclaw/**` | `AGENTS.md`, `ARCHITECTURE.md`, `CONTRIBUTING.md`, `docs/**` when contributor/runtime guidance changed | | `package.json` scripts | `AGENTS.md` Commands section | | New/moved doc files | `AGENTS.md` Where to look | ### Cross-reference checklist 1. `AGENTS.md` Where to look table — all paths valid -2. `specs/DESIGN.md` <-> `specs/design-specs/` + `specs/designs/` (indexed) -3. `specs/product-specs/index.md` <-> actual spec files -4. `specs/FRONTEND.md` Pages <-> `apps/web/src/app.tsx` routes +2. `ARCHITECTURE.md` still reflects the active system shape and ownership boundaries +3. `CONTRIBUTING.md` and `docs/zh/guide/contributing.md` still reflect the active contributor workflow when changed +4. `docs/.vitepress/config.ts` still indexes any new or moved live docs pages ### Rules -- Regenerate `specs/generated/db-schema.md` fully from schema source +- Only touch snapshot docs under `specs/**` when creating/replacing a snapshot or correcting an objective factual breakage that cannot be left stale +- Regenerate `specs/generated/db-schema.md` fully from schema source when that generated snapshot itself is intentionally updated - Preserve original language (English/Chinese) - Do not auto-commit; present changes for review diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index bf5221632..9982b2a63 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,6 +1,6 @@ # Architecture -Nexu uses a controller-first local runtime model. In desktop/local mode, a single `apps/controller` process owns Nexu config, compiles OpenClaw config, materializes skills/templates, and orchestrates the OpenClaw runtime. +Nexu uses a controller-first local runtime model. In desktop/local mode, a single `apps/controller` process owns Nexu config, compiles dynamic OpenClaw config, materializes stateful skills/templates, and orchestrates the OpenClaw runtime. The embedded runtime artifact itself is owned by `packages/slimclaw`, which is the single source of truth for prepared OpenClaw runtime assets, builtin runtime plugins, and runtime staging. ## System diagram @@ -48,14 +48,15 @@ Never hand-write types that duplicate a schema. Use `z.infer`. - **`apps/web/`** — React frontend. Pages in `src/pages/`, generated SDK in `lib/api/`, auth client in `src/lib/auth-client.ts`. - **`apps/desktop/`** — Electron desktop runtime shell and sidecar orchestrator. The active local path launches `controller + web + openclaw` sidecars only. - **`packages/shared/`** — Shared Zod schemas in `src/schemas/`. Includes bot, channel, gateway, invite, model, skill, and OpenClaw config schemas. +- **`packages/slimclaw/`** — Nexu-owned embedded OpenClaw runtime contract. Owns prepare/install/prune flows, prepared runtime artifacts, builtin runtime plugin ownership, and staging/patch application. - **`nexu-skills/`** — Public skill repository. Each skill is a directory with `SKILL.md` frontmatter. `skills.json` is the built catalog index. -- **`specs/`** — Design docs, references, product specs, exec plans, generated artifacts. +- **`specs/`** — Historical design docs, references, product specs, exec plans, and generated snapshots. Useful for context, but not the living source of truth for day-to-day doc maintenance. ## Key data flows -**Desktop/local config generation:** Controller reads `~/.nexu/config.json` → compiles OpenClaw config JSON (agents, channels, bindings, models) → writes `OPENCLAW_CONFIG_PATH` and managed skills/templates → OpenClaw hot-reloads. +**Desktop/local config generation:** Controller reads `~/.nexu/config.json` → compiles OpenClaw config JSON (agents, channels, bindings, models) → writes `OPENCLAW_CONFIG_PATH` and managed dynamic skills/templates/state → OpenClaw hot-reloads against the slimclaw-owned prepared runtime. -**Desktop runtime boot:** Electron desktop starts the controller sidecar, waits for controller readiness/auth bootstrap, starts the web sidecar, and delegates OpenClaw process management to `apps/controller`. +**Desktop runtime boot:** Electron desktop resolves the slimclaw-owned runtime artifact, starts the controller sidecar, waits for controller readiness/auth bootstrap, starts the web sidecar, and delegates OpenClaw process management to `apps/controller`. **Proxy policy:** Desktop bootstrap computes one normalized proxy policy from `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, and `NO_PROXY`, applies it to Electron networking, propagates the normalized uppercase env into controller/web/OpenClaw child processes, and always merges loopback bypass entries (`localhost`, `127.0.0.1`, `::1`). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 578784fee..ad377ae70 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,6 +4,8 @@ This file is the **canonical English** contributing guide. The docs site embeds Thank you for helping improve nexu. The sections below cover **code**, **documentation**, and **how we review changes**. +For documentation maintenance, treat `AGENTS.md`, `ARCHITECTURE.md`, `CONTRIBUTING.md`, and `docs/**` as the live docs. Treat `specs/**`, `docs/plans/**`, and other dated design/plan artifacts as historical snapshots that are frozen by default once committed. + If you want a lower-friction entry point, we are actively looking for **Good First Issue** contributors. Start with the [good-first-issue label](https://github.com/nexu-io/nexu/labels/good-first-issue) or the [Chinese first-PR guide](https://docs.nexu.io/zh/guide/first-pr). ## Community standards @@ -54,11 +56,11 @@ nexu/ │ ├── desktop/ # Electron desktop shell │ └── controller/ # Hono backend + OpenClaw orchestration ├── packages/shared/ # Shared Zod schemas -├── packages/slimclaw/ # Repo-local OpenClaw runtime contract + prepared runtime ownership +├── packages/slimclaw/ # Single source of truth for the embedded OpenClaw runtime contract and prepared runtime artifacts ├── scripts/ # Dev/CI scripts (launchd, probes, e2e) ├── tests/ # Vitest test suites ├── docs/ # VitePress documentation site -└── specs/ # Design docs, product specs +└── specs/ # Historical design docs, product specs, and other snapshots ``` ## Common commands @@ -155,6 +157,12 @@ VitePress prints a local URL; use it to verify headings, links, and images. - Chinese pages: `docs/zh/` - New sidebar entries: update `docs/.vitepress/config.ts` - When you add or substantially change a guide, **keep English and Chinese in sync** when both locales exist. +- Do not use `specs/**` or dated plan docs as the target for routine doc cleanup. If the current truth changes, update the live docs above or add a new snapshot doc instead of mutating old ones. + +### Runtime ownership rule + +- `packages/slimclaw` is the single source of truth for Nexu's embedded OpenClaw runtime packaging, prepared artifacts, builtin runtime plugins, and staging contract. +- If your change affects embedded runtime ownership or packaging behavior, route it through slimclaw by default. Anything that intentionally bypasses slimclaw should be discussed with maintainers before implementation. ### Paste images into Markdown diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/package-lock.json b/apps/controller/static/runtime-plugins/openclaw-weixin/package-lock.json deleted file mode 100644 index 5f9b7457a..000000000 --- a/apps/controller/static/runtime-plugins/openclaw-weixin/package-lock.json +++ /dev/null @@ -1,2488 +0,0 @@ -{ - "name": "@tencent-weixin/openclaw-weixin", - "version": "1.0.2", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@tencent-weixin/openclaw-weixin", - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "qrcode-terminal": "0.12.0", - "zod": "4.3.6" - }, - "devDependencies": { - "@vitest/coverage-v8": "^3.1.0", - "typescript": "^5.8.0", - "vitest": "^3.1.0" - }, - "engines": { - "node": ">=22" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", - "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", - "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", - "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", - "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", - "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", - "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", - "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", - "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", - "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", - "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", - "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", - "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", - "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", - "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", - "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", - "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", - "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", - "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", - "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", - "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", - "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", - "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", - "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", - "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", - "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", - "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", - "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.1.tgz", - "integrity": "sha512-xB0b51TB7IfDEzAojXahmr+gfA00uYVInJGgNNkeQG6RPnCPGr7udsylFLTubuIUSRE6FkcI1NElyRt83PP5oQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.1.tgz", - "integrity": "sha512-XOjPId0qwSDKHaIsdzHJtKCxX0+nH8MhBwvrNsT7tVyKmdTx1jJ4XzN5RZXCdTzMpufLb+B8llTC0D8uCrLhcw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.1.tgz", - "integrity": "sha512-vQuRd28p0gQpPrS6kppd8IrWmFo42U8Pz1XLRjSZXq5zCqyMDYFABT7/sywL11mO1EL10Qhh7MVPEwkG8GiBeg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.1.tgz", - "integrity": "sha512-x6VG6U29+Ivlnajrg1IHdzXeAwSoEHBFVO+CtC9Brugx6de712CUJobRUxsIA0KYrQvCmzNrMPFTT1A4CCqNTg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.1.tgz", - "integrity": "sha512-Sgi0Uo6t1YCHJMNO3Y8+bm+SvOanUGkoZKn/VJPwYUe2kp31X5KnXmzKd/NjW8iA3gFcfNZ64zh14uOGrIllCQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.1.tgz", - "integrity": "sha512-AM4xnwEZwukdhk7laMWfzWu9JGSVnJd+Fowt6Fd7QW1nrf3h0Hp7Qx5881M4aqrUlKBCybOxz0jofvIIfl7C5g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.1.tgz", - "integrity": "sha512-KUizqxpwaR2AZdAUsMWfL/C94pUu7TKpoPd88c8yFVixJ+l9hejkrwoK5Zj3wiNh65UeyryKnJyxL1b7yNqFQA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.1.tgz", - "integrity": "sha512-MZoQ/am77ckJtZGFAtPucgUuJWiop3m2R3lw7tC0QCcbfl4DRhQUBUkHWCkcrT3pqy5Mzv5QQgY6Dmlba6iTWg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.1.tgz", - "integrity": "sha512-Sez95TP6xGjkWB1608EfhCX1gdGrO5wzyN99VqzRtC17x/1bhw5VU1V0GfKUwbW/Xr1J8mSasoFoJa6Y7aGGSA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.1.tgz", - "integrity": "sha512-9Cs2Seq98LWNOJzR89EGTZoiP8EkZ9UbQhBlDgfAkM6asVna1xJ04W2CLYWDN/RpUgOjtQvcv8wQVi1t5oQazA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.1.tgz", - "integrity": "sha512-n9yqttftgFy7IrNEnHy1bOp6B4OSe8mJDiPkT7EqlM9FnKOwUMnCK62ixW0Kd9Clw0/wgvh8+SqaDXMFvw3KqQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.1.tgz", - "integrity": "sha512-SfpNXDzVTqs/riak4xXcLpq5gIQWsqGWMhN1AGRQKB4qGSs4r0sEs3ervXPcE1O9RsQ5bm8Muz6zmQpQnPss1g==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.1.tgz", - "integrity": "sha512-LjaChED0wQnjKZU+tsmGbN+9nN1XhaWUkAlSbTdhpEseCS4a15f/Q8xC2BN4GDKRzhhLZpYtJBZr2NZhR0jvNw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.1.tgz", - "integrity": "sha512-ojW7iTJSIs4pwB2xV6QXGwNyDctvXOivYllttuPbXguuKDX5vwpqYJsHc6D2LZzjDGHML414Tuj3LvVPe1CT1A==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.1.tgz", - "integrity": "sha512-FP+Q6WTcxxvsr0wQczhSE+tOZvFPV8A/mUE6mhZYFW9/eea/y/XqAgRoLLMuE9Cz0hfX5bi7p116IWoB+P237A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.1.tgz", - "integrity": "sha512-L1uD9b/Ig8Z+rn1KttCJjwhN1FgjRMBKsPaBsDKkfUl7GfFq71pU4vWCnpOsGljycFEbkHWARZLf4lMYg3WOLw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.1.tgz", - "integrity": "sha512-EZc9NGTk/oSUzzOD4nYY4gIjteo2M3CiozX6t1IXGCOdgxJTlVu/7EdPeiqeHPSIrxkLhavqpBAUCfvC6vBOug==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.1.tgz", - "integrity": "sha512-NQ9KyU1Anuy59L8+HHOKM++CoUxrQWrZWXRik4BJFm+7i5NP6q/SW43xIBr80zzt+PDBJ7LeNmloQGfa0JGk0w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.1.tgz", - "integrity": "sha512-GZkLk2t6naywsveSFBsEb0PLU+JC9ggVjbndsbG20VPhar6D1gkMfCx4NfP9owpovBXTN+eRdqGSkDGIxPHhmQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.1.tgz", - "integrity": "sha512-1hjG9Jpl2KDOetr64iQd8AZAEjkDUUK5RbDkYWsViYLC1op1oNzdjMJeFiofcGhqbNTaY2kfgqowE7DILifsrA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.1.tgz", - "integrity": "sha512-ARoKfflk0SiiYm3r1fmF73K/yB+PThmOwfWCk1sr7x/k9dc3uGLWuEE9if+Pw21el8MSpp3TMnG5vLNsJ/MMGQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.1.tgz", - "integrity": "sha512-oOST61G6VM45Mz2vdzWMr1s2slI7y9LqxEV5fCoWi2MDONmMvgsJVHSXxce/I2xOSZPTZ47nDPOl1tkwKWSHcw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.1.tgz", - "integrity": "sha512-x5WgLi5dWpRz7WclKBGEF15LcWTh0ewrHM6Cq4A+WUbkysUMZNeqt05bwPonOQ3ihPS/WMhAZV5zB1DfnI4Sxg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.1.tgz", - "integrity": "sha512-wS+zHAJRVP5zOL0e+a3V3E/NTEwM2HEvvNKoDy5Xcfs0o8lljxn+EAFPkUsxihBdmDq1JWzXmmB9cbssCPdxxw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.1.tgz", - "integrity": "sha512-rhHyrMeLpErT/C7BxcEsU4COHQUzHyrPYW5tOZUeUhziNtRuYxmDWvqQqzpuUt8xpOgmbKa1btGXfnA/ANVO+g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/chai": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" - } - }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@vitest/coverage-v8": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", - "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "@bcoe/v8-coverage": "^1.0.2", - "ast-v8-to-istanbul": "^0.3.3", - "debug": "^4.4.1", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.6", - "istanbul-reports": "^3.1.7", - "magic-string": "^0.30.17", - "magicast": "^0.3.5", - "std-env": "^3.9.0", - "test-exclude": "^7.0.1", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@vitest/browser": "3.2.4", - "vitest": "3.2.4" - }, - "peerDependenciesMeta": { - "@vitest/browser": { - "optional": true - } - } - }, - "node_modules/@vitest/expect": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", - "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", - "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.2.4", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/pretty-format": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", - "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", - "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "3.2.4", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", - "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.4", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", - "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^4.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", - "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.4", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/ast-v8-to-istanbul": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.12.tgz", - "integrity": "sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.31", - "estree-walker": "^3.0.3", - "js-tokens": "^10.0.0" - } - }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/check-error": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", - "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/esbuild": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", - "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.4", - "@esbuild/android-arm": "0.27.4", - "@esbuild/android-arm64": "0.27.4", - "@esbuild/android-x64": "0.27.4", - "@esbuild/darwin-arm64": "0.27.4", - "@esbuild/darwin-x64": "0.27.4", - "@esbuild/freebsd-arm64": "0.27.4", - "@esbuild/freebsd-x64": "0.27.4", - "@esbuild/linux-arm": "0.27.4", - "@esbuild/linux-arm64": "0.27.4", - "@esbuild/linux-ia32": "0.27.4", - "@esbuild/linux-loong64": "0.27.4", - "@esbuild/linux-mips64el": "0.27.4", - "@esbuild/linux-ppc64": "0.27.4", - "@esbuild/linux-riscv64": "0.27.4", - "@esbuild/linux-s390x": "0.27.4", - "@esbuild/linux-x64": "0.27.4", - "@esbuild/netbsd-arm64": "0.27.4", - "@esbuild/netbsd-x64": "0.27.4", - "@esbuild/openbsd-arm64": "0.27.4", - "@esbuild/openbsd-x64": "0.27.4", - "@esbuild/openharmony-arm64": "0.27.4", - "@esbuild/sunos-x64": "0.27.4", - "@esbuild/win32-arm64": "0.27.4", - "@esbuild/win32-ia32": "0.27.4", - "@esbuild/win32-x64": "0.27.4" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/qrcode-terminal": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", - "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" - } - }, - "node_modules/rollup": { - "version": "4.59.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.1.tgz", - "integrity": "sha512-iZKH8BeoCwTCBTZBZWQQMreekd4mdomwdjIQ40GC1oZm6o+8PnNMIxFOiCsGMWeS8iDJ7KZcl7KwmKk/0HOQpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.59.1", - "@rollup/rollup-android-arm64": "4.59.1", - "@rollup/rollup-darwin-arm64": "4.59.1", - "@rollup/rollup-darwin-x64": "4.59.1", - "@rollup/rollup-freebsd-arm64": "4.59.1", - "@rollup/rollup-freebsd-x64": "4.59.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.59.1", - "@rollup/rollup-linux-arm-musleabihf": "4.59.1", - "@rollup/rollup-linux-arm64-gnu": "4.59.1", - "@rollup/rollup-linux-arm64-musl": "4.59.1", - "@rollup/rollup-linux-loong64-gnu": "4.59.1", - "@rollup/rollup-linux-loong64-musl": "4.59.1", - "@rollup/rollup-linux-ppc64-gnu": "4.59.1", - "@rollup/rollup-linux-ppc64-musl": "4.59.1", - "@rollup/rollup-linux-riscv64-gnu": "4.59.1", - "@rollup/rollup-linux-riscv64-musl": "4.59.1", - "@rollup/rollup-linux-s390x-gnu": "4.59.1", - "@rollup/rollup-linux-x64-gnu": "4.59.1", - "@rollup/rollup-linux-x64-musl": "4.59.1", - "@rollup/rollup-openbsd-x64": "4.59.1", - "@rollup/rollup-openharmony-arm64": "4.59.1", - "@rollup/rollup-win32-arm64-msvc": "4.59.1", - "@rollup/rollup-win32-ia32-msvc": "4.59.1", - "@rollup/rollup-win32-x64-gnu": "4.59.1", - "@rollup/rollup-win32-x64-msvc": "4.59.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-literal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", - "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.2.tgz", - "integrity": "sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^10.4.1", - "minimatch": "^10.2.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", - "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/vite": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", - "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.27.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vitest": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", - "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.4", - "@vitest/mocker": "3.2.4", - "@vitest/pretty-format": "^3.2.4", - "@vitest/runner": "3.2.4", - "@vitest/snapshot": "3.2.4", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.4", - "@vitest/ui": "3.2.4", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/debug": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} diff --git a/apps/controller/tests/skillhub-custom-import.test.ts b/apps/controller/tests/skillhub-custom-import.test.ts index a6bad8572..5048f9f31 100644 --- a/apps/controller/tests/skillhub-custom-import.test.ts +++ b/apps/controller/tests/skillhub-custom-import.test.ts @@ -27,7 +27,7 @@ const { SkillDb } = await import("../src/services/skillhub/skill-db.js"); function stubExtractTo( slug: string, - skillsDir: string, + _skillsDir: string, opts: { withPackageJson?: boolean } = {}, ) { vi.mocked(extractZipMock).mockImplementationOnce( diff --git a/apps/controller/tests/slimclaw-runtime-plugin-writer.test.ts b/apps/controller/tests/slimclaw-runtime-plugin-writer.test.ts index 8a215ce86..1f55bf339 100644 --- a/apps/controller/tests/slimclaw-runtime-plugin-writer.test.ts +++ b/apps/controller/tests/slimclaw-runtime-plugin-writer.test.ts @@ -155,6 +155,40 @@ describe("OpenClawRuntimePluginWriter", () => { }); }); + it("treats builtin openclaw-weixin as the source of truth and removes stale copied state extension", async () => { + env = { + ...env, + openclawBuiltinExtensionsDir: path.join(rootDir, "builtin-extensions"), + } as ControllerEnv; + + const runtimePluginDir = path.join( + env.runtimePluginTemplatesDir, + "openclaw-weixin", + ); + const builtinPluginDir = path.join( + env.openclawBuiltinExtensionsDir, + "openclaw-weixin", + ); + const staleTargetDir = path.join( + env.openclawExtensionsDir, + "openclaw-weixin", + ); + + await mkdir(runtimePluginDir, { recursive: true }); + await mkdir(builtinPluginDir, { recursive: true }); + await mkdir(staleTargetDir, { recursive: true }); + await writeFile(path.join(runtimePluginDir, "index.ts"), "export {}\n"); + await writeFile(path.join(builtinPluginDir, "index.js"), "export {}\n"); + await writeFile(path.join(staleTargetDir, "stale.txt"), "stale\n"); + + const writer = new OpenClawRuntimePluginWriter(env); + await writer.ensurePlugins(); + + await expect(access(staleTargetDir)).rejects.toMatchObject({ + code: "ENOENT", + }); + }); + it("prefers bundled qqbot over the legacy runtime plugin source", async () => { const bundledPluginDir = path.join( env.bundledRuntimePluginsDir, diff --git a/biome.json b/biome.json index 4128b0e3e..b0f9b1bc7 100644 --- a/biome.json +++ b/biome.json @@ -52,6 +52,7 @@ "specs/**", "apps/web/lib/api", "apps/controller/static/runtime-plugins/**", + "packages/slimclaw/runtime-plugins/**", "apps/web/public/skill-translations-zh.json", "apps/api/migrations/meta/*.json", "packages/slimclaw/runtime-patches/**", diff --git a/docs/en/guide/contributing.md b/docs/en/guide/contributing.md index d888a8f18..b21b1d145 100644 --- a/docs/en/guide/contributing.md +++ b/docs/en/guide/contributing.md @@ -22,6 +22,8 @@ If you want to update the English contribution guide itself, edit that file firs - Keep pull requests small and focused. - Never commit secrets such as API keys or tokens. - If you update a guide that exists in multiple languages, keep the localized versions aligned when possible. +- Treat `AGENTS.md`, `ARCHITECTURE.md`, `CONTRIBUTING.md`, and `docs/**` as live docs; treat `specs/**`, `docs/plans/**`, and other dated plan/spec docs as historical snapshots that are frozen by default. +- `packages/slimclaw` is the single source of truth for Nexu's embedded OpenClaw runtime packaging, prepared artifacts, builtin runtime plugins, and staging contract. Runtime ownership changes should go through slimclaw by default. ## Local Docs Workflow diff --git a/docs/zh/guide/contributing.md b/docs/zh/guide/contributing.md index 76f8b0dde..9551a66c8 100644 --- a/docs/zh/guide/contributing.md +++ b/docs/zh/guide/contributing.md @@ -4,6 +4,8 @@ 感谢你为 nexu 花时间改进项目。本页说明如何参与 **代码**、**文档** 贡献,以及 **PR 与协作约定**。 +文档维护上,`AGENTS.md`、`ARCHITECTURE.md`、`CONTRIBUTING.md` 与 `docs/**` 属于持续演进的 live docs;`specs/**`、`docs/plans/**` 以及其他带日期的设计/计划文档原则上属于历史快照,提交后默认冻结。 + Nexu 开源共创招募中,欢迎一起来写代码、拿积分、上榜单。想低门槛开始,可以先看 [第一次提 PR 指南](/zh/guide/first-pr)。 我们长期维护 [Good First Issue 列表](https://github.com/nexu-io/nexu/labels/good-first-issue),题目边界清晰、方向聚焦,还配有 AI Prompt 模板,方便你更快上手。首次贡献者和 `good-first-issue` 认领者,我们也会尽量提供引导与反馈。更多说明见 [贡献奖励与支持](/zh/guide/contributor-rewards)。 @@ -52,14 +54,14 @@ pnpm install ```text nexu/ ├── apps/ -│ ├── api/ -│ ├── web/ -│ ├── desktop/ # Electron 桌面客户端 -│ └── controller/ -├── packages/shared/ -├── docs/ # VitePress 文档站 -├── tests/ -└── specs/ +│ ├── web/ # React + Ant Design dashboard +│ ├── desktop/ # Electron 桌面运行时外壳 +│ └── controller/ # Hono backend + OpenClaw 编排 +├── packages/shared/ # 共享 Zod schemas +├── packages/slimclaw/ # 嵌入式 OpenClaw runtime contract 与 prepared artifacts 的唯一真相源 +├── docs/ # VitePress 文档站 +├── tests/ # Vitest 测试 +└── specs/ # 历史设计文档、产品规格与快照 ``` ## 常用命令 @@ -68,13 +70,18 @@ nexu/ | 命令 | 作用 | | --- | --- | -| `pnpm dev` | 开发态(controller + web)热更新 | -| `pnpm dev:desktop` | 桌面客户端开发 | +| `pnpm dev start` | 本地轻量栈:openclaw -> controller -> web -> desktop | +| `pnpm dev start ` | 单独启动一个本地开发服务 | +| `pnpm dev stop` | 按逆序停止本地轻量栈 | +| `pnpm dev restart` | 重启本地轻量栈 | | `pnpm dev:controller` | 仅启动 controller | +| `pnpm start` | 完整桌面运行时(Electron + launchd,macOS) | +| `pnpm stop` | 停止桌面运行时 | +| `pnpm status` | 查看桌面运行时状态 | | `pnpm build` | 各包生产构建 | | `pnpm typecheck` | 全仓库 TypeScript 检查 | -| `pnpm lint` | Biome 检查 + `typecheck`(与 CI 主流程一致) | -| `pnpm lint:fix` | 在可行范围内自动修复并 typecheck | +| `pnpm lint` | Biome 检查 | +| `pnpm lint:fix` | 在可行范围内自动修复 | | `pnpm format` | 使用 Biome 格式化/写入 | | `pnpm test` | 根目录 Vitest(`vitest run`) | | `pnpm check:esm-imports` | ESM 路径检查(CI 中也会跑) | @@ -150,6 +157,12 @@ pnpm dev - 中文:`docs/zh/` - 新增侧边栏条目:修改 `docs/.vitepress/config.ts` - 新增或大幅修改指南时,若两种语言都有对应页面,请**尽量保持中英文同步**。 +- 不要把 `specs/**` 或带日期的计划文档当作日常文档清理目标;当前事实变化时,应更新 live docs,或新增快照文档,而不是回写旧快照。 + +### Runtime ownership 规则 + +- `packages/slimclaw` 是 Nexu 嵌入式 OpenClaw runtime packaging、prepared artifacts、builtin runtime plugins 与 staging contract 的唯一真相源。 +- 任何会改变嵌入式 runtime 所有权或打包行为的改动,原则上都应通过 slimclaw 落地;若要绕开 slimclaw,先与维护者对齐。 ### 在 Markdown 中贴图 diff --git a/packages/slimclaw/build.mjs b/packages/slimclaw/build.mjs index d89584394..e5001ae3d 100644 --- a/packages/slimclaw/build.mjs +++ b/packages/slimclaw/build.mjs @@ -7,6 +7,24 @@ const packageRoot = path.dirname(fileURLToPath(import.meta.url)); const repoRoot = path.resolve(packageRoot, "..", ".."); const require = createRequire(import.meta.url); +function formatDurationMs(durationMs) { + return `${(durationMs / 1000).toFixed(3)}s`; +} + +async function timedStep(stepName, fn, timings) { + const startedAt = performance.now(); + console.log(`[slimclaw:build][timing] start ${stepName}`); + try { + return await fn(); + } finally { + const durationMs = performance.now() - startedAt; + timings.push({ stepName, durationMs }); + console.log( + `[slimclaw:build][timing] done ${stepName} duration=${formatDurationMs(durationMs)}`, + ); + } +} + function createCommandSpec(command, args) { if ( process.platform === "win32" && @@ -63,5 +81,23 @@ async function buildSlimclaw() { ); } -await prepareOpenclawRuntime(); -await buildSlimclaw(); +const totalStartedAt = performance.now(); +const timings = []; + +await timedStep( + "prepare-runtime", + async () => prepareOpenclawRuntime(), + timings, +); +await timedStep("tsc", async () => buildSlimclaw(), timings); + +const totalDurationMs = performance.now() - totalStartedAt; +console.log("[slimclaw:build][timing] summary"); +for (const timing of timings) { + console.log( + `[slimclaw:build][timing] ${timing.stepName}=${formatDurationMs(timing.durationMs)}`, + ); +} +console.log( + `[slimclaw:build][timing] total=${formatDurationMs(totalDurationMs)}`, +); diff --git a/packages/slimclaw/install-runtime.mjs b/packages/slimclaw/install-runtime.mjs index 5ae6654ff..2275ef2ce 100644 --- a/packages/slimclaw/install-runtime.mjs +++ b/packages/slimclaw/install-runtime.mjs @@ -29,6 +29,24 @@ function getPrunedInstallArgs() { return ["--omit=peer", "--no-audit", "--no-fund"]; } +function formatDurationMs(durationMs) { + return `${(durationMs / 1000).toFixed(3)}s`; +} + +async function timedStep(stepName, fn, timings) { + const startedAt = performance.now(); + console.log(`[slimclaw:install][timing] start ${stepName}`); + try { + return await fn(); + } finally { + const durationMs = performance.now() - startedAt; + timings.push({ stepName, durationMs }); + console.log( + `[slimclaw:install][timing] done ${stepName} duration=${formatDurationMs(durationMs)}`, + ); + } +} + async function ensureRuntimeBinWrappers(runtimeDir) { const binDir = path.join(runtimeDir, "bin"); await mkdir(binDir, { recursive: true }); @@ -142,27 +160,77 @@ async function run(command, args, cwd) { export async function installRuntimeAt(runtimeDir, mode = "pruned") { const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm"; - await syncRuntimeSeedManifests(runtimeDir); + const timings = []; + const totalStartedAt = performance.now(); + let attemptedCi = false; + let ciSucceeded = false; + let fellBackToInstall = false; + await timedStep( + "sync-runtime-seed-manifests", + async () => syncRuntimeSeedManifests(runtimeDir), + timings, + ); const lockfilePath = path.join(runtimeDir, "package-lock.json"); + const usedLockfile = await exists(lockfilePath); if (mode === "full") { - await run( - npmCommand, - ["install", "--no-audit", "--no-fund", "--prefer-offline"], - runtimeDir, + await timedStep( + "npm-install", + async () => + run( + npmCommand, + ["install", "--no-audit", "--no-fund", "--prefer-offline"], + runtimeDir, + ), + timings, + ); + await timedStep( + "ensure-runtime-bin-wrappers", + async () => ensureRuntimeBinWrappers(runtimeDir), + timings, + ); + const totalDurationMs = performance.now() - totalStartedAt; + console.log("[slimclaw:install][timing] summary"); + for (const timing of timings) { + console.log( + `[slimclaw:install][timing] ${timing.stepName}=${formatDurationMs(timing.durationMs)}`, + ); + } + console.log( + `[slimclaw:install][timing] total=${formatDurationMs(totalDurationMs)} mode=${mode} usedLockfile=${usedLockfile} attemptedCi=${attemptedCi} ciSucceeded=${ciSucceeded} fellBackToInstall=${fellBackToInstall}`, ); - await ensureRuntimeBinWrappers(runtimeDir); return; } const installArgs = getPrunedInstallArgs(); - if (await exists(lockfilePath)) { + if (usedLockfile) { + attemptedCi = true; try { - await run(npmCommand, ["ci", ...installArgs], runtimeDir); - await ensureRuntimeBinWrappers(runtimeDir); + await timedStep( + "npm-ci", + async () => run(npmCommand, ["ci", ...installArgs], runtimeDir), + timings, + ); + ciSucceeded = true; + await timedStep( + "ensure-runtime-bin-wrappers", + async () => ensureRuntimeBinWrappers(runtimeDir), + timings, + ); + const totalDurationMs = performance.now() - totalStartedAt; + console.log("[slimclaw:install][timing] summary"); + for (const timing of timings) { + console.log( + `[slimclaw:install][timing] ${timing.stepName}=${formatDurationMs(timing.durationMs)}`, + ); + } + console.log( + `[slimclaw:install][timing] total=${formatDurationMs(totalDurationMs)} mode=${mode} usedLockfile=${usedLockfile} attemptedCi=${attemptedCi} ciSucceeded=${ciSucceeded} fellBackToInstall=${fellBackToInstall}`, + ); return; } catch (error) { + fellBackToInstall = true; console.warn( "slimclaw runtime npm ci failed, falling back to npm install --prefer-offline.", ); @@ -170,13 +238,33 @@ export async function installRuntimeAt(runtimeDir, mode = "pruned") { } } - await run( - npmCommand, - ["install", ...installArgs, "--prefer-offline"], - runtimeDir, + await timedStep( + "npm-install", + async () => + run( + npmCommand, + ["install", ...installArgs, "--prefer-offline"], + runtimeDir, + ), + timings, ); - await ensureRuntimeBinWrappers(runtimeDir); + await timedStep( + "ensure-runtime-bin-wrappers", + async () => ensureRuntimeBinWrappers(runtimeDir), + timings, + ); + + const totalDurationMs = performance.now() - totalStartedAt; + console.log("[slimclaw:install][timing] summary"); + for (const timing of timings) { + console.log( + `[slimclaw:install][timing] ${timing.stepName}=${formatDurationMs(timing.durationMs)}`, + ); + } + console.log( + `[slimclaw:install][timing] total=${formatDurationMs(totalDurationMs)} mode=${mode} usedLockfile=${usedLockfile} attemptedCi=${attemptedCi} ciSucceeded=${ciSucceeded} fellBackToInstall=${fellBackToInstall}`, + ); } if (process.argv[1] === fileURLToPath(import.meta.url)) { diff --git a/packages/slimclaw/postinstall-cache.mjs b/packages/slimclaw/postinstall-cache.mjs index 06b93001f..a0144aded 100644 --- a/packages/slimclaw/postinstall-cache.mjs +++ b/packages/slimclaw/postinstall-cache.mjs @@ -1,4 +1,5 @@ import { createHash } from "node:crypto"; +import { readdirSync } from "node:fs"; import { readFile } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; @@ -7,17 +8,57 @@ import { exists } from "./utils.mjs"; const packageRoot = path.dirname(fileURLToPath(import.meta.url)); const repoRoot = path.resolve(packageRoot, "..", ".."); const runtimeSeedRoot = path.resolve(packageRoot, "runtime-seed"); +const weixinPluginSourceRoot = path.resolve( + packageRoot, + "runtime-plugins", + "openclaw-weixin", +); + +function collectWeixinPluginSourceInputs(currentDir = weixinPluginSourceRoot) { + const inputs = []; + let entries = []; + try { + entries = readdirSync(currentDir, { withFileTypes: true }); + } catch { + return inputs; + } + + for (const entry of [...entries].sort((left, right) => + left.name.localeCompare(right.name), + )) { + if (entry.name === "dist" || entry.name === "node_modules") { + continue; + } + + const entryPath = path.join(currentDir, entry.name); + if (entry.isDirectory()) { + inputs.push(...collectWeixinPluginSourceInputs(entryPath)); + continue; + } + + if ( + entry.isFile() && + (entry.name.endsWith(".ts") || entry.name.endsWith(".json")) + ) { + inputs.push(entryPath); + } + } + + return inputs; +} export const cacheInputs = [ path.join(runtimeSeedRoot, "package.json"), path.join(runtimeSeedRoot, "package-lock.json"), path.join(runtimeSeedRoot, "clean-node-modules.mjs"), path.join(packageRoot, "prepare-runtime.mjs"), + path.join(packageRoot, "prepare-hot-plugins.mjs"), path.join(packageRoot, "install-runtime.mjs"), path.join(packageRoot, "postinstall-cache.mjs"), path.join(packageRoot, "prune-runtime.mjs"), path.join(packageRoot, "prune-runtime-paths.mjs"), path.join(packageRoot, "utils.mjs"), + ...collectWeixinPluginSourceInputs(), ]; export const cacheEnvInputs = ["NEXU_OPENCLAW_PRUNE_DAVEY"]; diff --git a/packages/slimclaw/prepare-hot-plugins.mjs b/packages/slimclaw/prepare-hot-plugins.mjs new file mode 100644 index 000000000..54607b950 --- /dev/null +++ b/packages/slimclaw/prepare-hot-plugins.mjs @@ -0,0 +1,666 @@ +import { cp, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import ts from "typescript"; + +const packageRoot = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.resolve(packageRoot, "..", ".."); +const FEISHU_STARTUP_EXPERIMENT_MODE = + process.env.NEXU_SLIMCLAW_FEISHU_STARTUP_EXPERIMENT; + +function buildFeishuMinimalStartupEntrySource() { + return `import { + buildProbeChannelStatusSummary, + buildRuntimeAccountStatusSnapshot, + createDefaultChannelRuntimeState, + DEFAULT_ACCOUNT_ID, + PAIRING_APPROVED_MESSAGE, + emptyPluginConfigSchema, +} from "openclaw/plugin-sdk/feishu"; +import { + collectAllowlistProviderRestrictSendersWarnings, + formatAllowFromLowercase, + mapAllowFromEntries, +} from "openclaw/plugin-sdk/compat"; +import { registerFeishuBitableTools } from "./src/bitable.js"; +import { registerFeishuChatTools } from "./src/chat.js"; +import { registerFeishuDocTools } from "./src/docx.js"; +import { registerFeishuDriveTools } from "./src/drive.js"; +import { registerFeishuPermTools } from "./src/perm.js"; +import { + listFeishuAccountIds, + resolveDefaultFeishuAccountId, + resolveFeishuAccount, +} from "./src/accounts.js"; +import { resolveFeishuGroupToolPolicy } from "./src/policy.js"; +import { setFeishuRuntime } from "./src/runtime.js"; +import { + looksLikeFeishuId, + normalizeFeishuTarget, +} from "./src/targets.js"; +import { registerFeishuWikiTools } from "./src/wiki.js"; + +const secretInputJsonSchema = { + oneOf: [ + { type: "string" }, + { + type: "object", + additionalProperties: false, + required: ["source", "provider", "id"], + properties: { + source: { type: "string", enum: ["env", "file", "exec"] }, + provider: { type: "string", minLength: 1 }, + id: { type: "string", minLength: 1 }, + }, + }, + ], +}; + +function setFeishuNamedAccountEnabled(cfg, accountId, enabled) { + const feishuCfg = cfg.channels?.feishu; + return { + ...cfg, + channels: { + ...cfg.channels, + feishu: { + ...feishuCfg, + accounts: { + ...feishuCfg?.accounts, + [accountId]: { + ...feishuCfg?.accounts?.[accountId], + enabled, + }, + }, + }, + }, + }; +} + +let feishuDirectoryModulePromise; +let feishuOutboundModulePromise; +let feishuProbeModulePromise; +let feishuSendModulePromise; +let feishuOnboardingModulePromise; +let feishuMonitorModulePromise; + +function loadFeishuDirectoryModule() { + feishuDirectoryModulePromise ??= import("./src/directory.js"); + return feishuDirectoryModulePromise; +} + +function loadFeishuOutboundModule() { + feishuOutboundModulePromise ??= import("./src/outbound.js"); + return feishuOutboundModulePromise; +} + +function loadFeishuProbeModule() { + feishuProbeModulePromise ??= import("./src/probe.js"); + return feishuProbeModulePromise; +} + +function loadFeishuSendModule() { + feishuSendModulePromise ??= import("./src/send.js"); + return feishuSendModulePromise; +} + +function loadFeishuOnboardingModule() { + feishuOnboardingModulePromise ??= import("./src/onboarding.js"); + return feishuOnboardingModulePromise; +} + +function loadFeishuMonitorModule() { + feishuMonitorModulePromise ??= import("./src/monitor.js"); + return feishuMonitorModulePromise; +} + +const feishuPlugin = { + id: "feishu", + meta: { + id: "feishu", + label: "Feishu", + selectionLabel: "Feishu/Lark (飞书)", + docsPath: "/channels/feishu", + docsLabel: "feishu", + blurb: "飞书/Lark enterprise messaging.", + aliases: ["lark"], + order: 70, + }, + capabilities: { + chatTypes: ["direct", "channel"], + polls: false, + threads: true, + media: true, + reactions: true, + edit: true, + reply: true, + }, + pairing: { + idLabel: "feishuUserId", + normalizeAllowEntry: (entry) => entry.replace(/^(feishu|user|open_id):/i, ""), + notifyApproval: async ({ cfg, id }) => { + const { sendMessageFeishu } = await loadFeishuSendModule(); + await sendMessageFeishu({ + cfg, + to: id, + text: PAIRING_APPROVED_MESSAGE, + }); + }, + }, + agentPrompt: { + messageToolHints: () => [ + "- Feishu targeting: omit \`target\` to reply to the current conversation (auto-inferred). Explicit targets: \`user:open_id\` or \`chat:chat_id\`.", + "- Feishu supports interactive cards for rich messages.", + ], + }, + groups: { + resolveToolPolicy: resolveFeishuGroupToolPolicy, + }, + mentions: { + stripPatterns: () => ['[^<]*'], + }, + reload: { configPrefixes: ["channels.feishu"] }, + configSchema: { + schema: { + type: "object", + additionalProperties: false, + properties: { + enabled: { type: "boolean" }, + defaultAccount: { type: "string" }, + appId: { type: "string" }, + appSecret: secretInputJsonSchema, + encryptKey: { type: "string" }, + verificationToken: secretInputJsonSchema, + domain: { + oneOf: [ + { type: "string", enum: ["feishu", "lark"] }, + { type: "string", format: "uri", pattern: "^https://" }, + ], + }, + connectionMode: { type: "string", enum: ["websocket", "webhook"] }, + webhookPath: { type: "string" }, + webhookHost: { type: "string" }, + webhookPort: { type: "integer", minimum: 1 }, + dmPolicy: { type: "string", enum: ["open", "pairing", "allowlist"] }, + allowFrom: { type: "array", items: { oneOf: [{ type: "string" }, { type: "number" }] } }, + groupPolicy: { type: "string", enum: ["open", "allowlist", "disabled"] }, + groupAllowFrom: { + type: "array", + items: { oneOf: [{ type: "string" }, { type: "number" }] }, + }, + requireMention: { type: "boolean" }, + groupSessionScope: { + type: "string", + enum: ["group", "group_sender", "group_topic", "group_topic_sender"], + }, + topicSessionMode: { type: "string", enum: ["disabled", "enabled"] }, + replyInThread: { type: "string", enum: ["disabled", "enabled"] }, + historyLimit: { type: "integer", minimum: 0 }, + dmHistoryLimit: { type: "integer", minimum: 0 }, + textChunkLimit: { type: "integer", minimum: 1 }, + chunkMode: { type: "string", enum: ["length", "newline"] }, + mediaMaxMb: { type: "number", minimum: 0 }, + renderMode: { type: "string", enum: ["auto", "raw", "card"] }, + accounts: { + type: "object", + additionalProperties: { + type: "object", + properties: { + enabled: { type: "boolean" }, + name: { type: "string" }, + appId: { type: "string" }, + appSecret: secretInputJsonSchema, + encryptKey: { type: "string" }, + verificationToken: secretInputJsonSchema, + domain: { type: "string", enum: ["feishu", "lark"] }, + connectionMode: { type: "string", enum: ["websocket", "webhook"] }, + webhookHost: { type: "string" }, + webhookPath: { type: "string" }, + webhookPort: { type: "integer", minimum: 1 }, + }, + }, + }, + }, + }, + }, + config: { + listAccountIds: (cfg) => listFeishuAccountIds(cfg), + resolveAccount: (cfg, accountId) => resolveFeishuAccount({ cfg, accountId }), + defaultAccountId: (cfg) => resolveDefaultFeishuAccountId(cfg), + setAccountEnabled: ({ cfg, accountId, enabled }) => { + const account = resolveFeishuAccount({ cfg, accountId }); + const isDefault = accountId === DEFAULT_ACCOUNT_ID; + if (isDefault) { + return { + ...cfg, + channels: { + ...cfg.channels, + feishu: { + ...cfg.channels?.feishu, + enabled, + }, + }, + }; + } + + return setFeishuNamedAccountEnabled(cfg, account.accountId, enabled); + }, + deleteAccount: ({ cfg, accountId }) => { + const isDefault = accountId === DEFAULT_ACCOUNT_ID; + if (isDefault) { + const next = { ...cfg }; + const nextChannels = { ...cfg.channels }; + delete nextChannels.feishu; + if (Object.keys(nextChannels).length > 0) { + next.channels = nextChannels; + } else { + delete next.channels; + } + return next; + } + + const feishuCfg = cfg.channels?.feishu; + const accounts = { ...feishuCfg?.accounts }; + delete accounts[accountId]; + return { + ...cfg, + channels: { + ...cfg.channels, + feishu: { + ...feishuCfg, + accounts: Object.keys(accounts).length > 0 ? accounts : undefined, + }, + }, + }; + }, + isConfigured: (account) => account.configured, + describeAccount: (account) => ({ + accountId: account.accountId, + enabled: account.enabled, + configured: account.configured, + name: account.name, + appId: account.appId, + domain: account.domain, + }), + resolveAllowFrom: ({ cfg, accountId }) => { + const account = resolveFeishuAccount({ cfg, accountId }); + return mapAllowFromEntries(account.config?.allowFrom); + }, + formatAllowFrom: ({ allowFrom }) => formatAllowFromLowercase({ allowFrom }), + }, + security: { + collectWarnings: ({ cfg, accountId }) => { + const account = resolveFeishuAccount({ cfg, accountId }); + const feishuCfg = account.config; + return collectAllowlistProviderRestrictSendersWarnings({ + cfg, + providerConfigPresent: cfg.channels?.feishu !== undefined, + configuredGroupPolicy: feishuCfg?.groupPolicy, + surface: "Feishu[" + account.accountId + "] groups", + openScope: "any member", + groupPolicyPath: "channels.feishu.groupPolicy", + groupAllowFromPath: "channels.feishu.groupAllowFrom", + }); + }, + }, + setup: { + resolveAccountId: () => DEFAULT_ACCOUNT_ID, + applyAccountConfig: ({ cfg, accountId }) => { + const isDefault = !accountId || accountId === DEFAULT_ACCOUNT_ID; + if (isDefault) { + return { + ...cfg, + channels: { + ...cfg.channels, + feishu: { + ...cfg.channels?.feishu, + enabled: true, + }, + }, + }; + } + + return setFeishuNamedAccountEnabled(cfg, accountId, true); + }, + }, + onboarding: { + channel: "feishu", + getStatus: async (params) => { + const { feishuOnboardingAdapter } = await loadFeishuOnboardingModule(); + return feishuOnboardingAdapter.getStatus(params); + }, + configure: async (params) => { + const { feishuOnboardingAdapter } = await loadFeishuOnboardingModule(); + return feishuOnboardingAdapter.configure(params); + }, + disable: (cfg) => ({ + ...cfg, + channels: { + ...cfg.channels, + feishu: { ...cfg.channels?.feishu, enabled: false }, + }, + }), + }, + messaging: { + normalizeTarget: (raw) => normalizeFeishuTarget(raw) ?? undefined, + targetResolver: { + looksLikeId: looksLikeFeishuId, + hint: "", + }, + }, + directory: { + self: async () => null, + listPeers: async ({ cfg, query, limit, accountId }) => { + const { listFeishuDirectoryPeers } = await loadFeishuDirectoryModule(); + return listFeishuDirectoryPeers({ + cfg, + query: query ?? undefined, + limit: limit ?? undefined, + accountId: accountId ?? undefined, + }); + }, + listGroups: async ({ cfg, query, limit, accountId }) => { + const { listFeishuDirectoryGroups } = await loadFeishuDirectoryModule(); + return listFeishuDirectoryGroups({ + cfg, + query: query ?? undefined, + limit: limit ?? undefined, + accountId: accountId ?? undefined, + }); + }, + listPeersLive: async ({ cfg, query, limit, accountId }) => { + const { listFeishuDirectoryPeersLive } = await loadFeishuDirectoryModule(); + return listFeishuDirectoryPeersLive({ + cfg, + query: query ?? undefined, + limit: limit ?? undefined, + accountId: accountId ?? undefined, + }); + }, + listGroupsLive: async ({ cfg, query, limit, accountId }) => { + const { listFeishuDirectoryGroupsLive } = await loadFeishuDirectoryModule(); + return listFeishuDirectoryGroupsLive({ + cfg, + query: query ?? undefined, + limit: limit ?? undefined, + accountId: accountId ?? undefined, + }); + }, + }, + outbound: { + deliveryMode: "direct", + chunker: (text, limit) => apiRuntime.channel.text.chunkMarkdownText(text, limit), + chunkerMode: "markdown", + textChunkLimit: 4000, + sendText: async (params) => { + const { feishuOutbound } = await loadFeishuOutboundModule(); + return feishuOutbound.sendText(params); + }, + sendMedia: async (params) => { + const { feishuOutbound } = await loadFeishuOutboundModule(); + return feishuOutbound.sendMedia(params); + }, + }, + status: { + defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID, { port: null }), + buildChannelSummary: ({ snapshot }) => + buildProbeChannelStatusSummary(snapshot, { + port: snapshot.port ?? null, + }), + probeAccount: async ({ account }) => { + const { probeFeishu } = await loadFeishuProbeModule(); + return probeFeishu(account); + }, + buildAccountSnapshot: ({ account, runtime, probe }) => ({ + accountId: account.accountId, + enabled: account.enabled, + configured: account.configured, + name: account.name, + appId: account.appId, + domain: account.domain, + ...buildRuntimeAccountStatusSnapshot({ runtime, probe }), + port: runtime?.port ?? null, + }), + }, + gateway: { + startAccount: async (ctx) => { + const account = resolveFeishuAccount({ cfg: ctx.cfg, accountId: ctx.accountId }); + const port = account.config?.webhookPort ?? null; + ctx.setStatus({ accountId: ctx.accountId, port }); + ctx.log?.info( + "starting feishu[" + + ctx.accountId + + "] (mode: " + + (account.config?.connectionMode ?? "websocket") + + ")", + ); + const { monitorFeishuProvider } = await loadFeishuMonitorModule(); + return monitorFeishuProvider({ + config: ctx.cfg, + runtime: ctx.runtime, + abortSignal: ctx.abortSignal, + accountId: ctx.accountId, + }); + }, + }, +}; + +let apiRuntime = null; + +const plugin = { + id: "feishu", + name: "Feishu", + description: "Feishu/Lark channel plugin", + configSchema: emptyPluginConfigSchema(), + register(api) { + apiRuntime = api.runtime; + setFeishuRuntime(api.runtime); + api.registerChannel({ plugin: feishuPlugin }); + registerFeishuDocTools(api); + registerFeishuChatTools(api); + registerFeishuWikiTools(api); + registerFeishuDriveTools(api); + registerFeishuPermTools(api); + registerFeishuBitableTools(api); + }, +}; + +export { feishuPlugin }; +export default plugin; +`; +} + +async function walkTypescriptFiles(rootDir, currentDir = rootDir, files = []) { + const entries = await readdir(currentDir, { withFileTypes: true }); + + for (const entry of entries) { + if (entry.name === "node_modules") { + continue; + } + + const entryPath = path.join(currentDir, entry.name); + if (entry.isDirectory()) { + await walkTypescriptFiles(rootDir, entryPath, files); + continue; + } + + if ( + !entry.isFile() || + !entry.name.endsWith(".ts") || + entry.name.endsWith(".d.ts") + ) { + continue; + } + + files.push(entryPath); + } + + return files; +} + +function transpileTsToJs(sourceText, sourcePath) { + return ts.transpileModule(sourceText, { + compilerOptions: { + module: ts.ModuleKind.ES2022, + target: ts.ScriptTarget.ES2022, + moduleResolution: ts.ModuleResolutionKind.Bundler, + verbatimModuleSyntax: true, + isolatedModules: true, + }, + fileName: sourcePath, + reportDiagnostics: false, + }).outputText; +} + +export async function precompileFeishuPlugin(runtimeDir) { + const feishuRoot = path.join( + runtimeDir, + "node_modules", + "openclaw", + "extensions", + "feishu", + ); + const packageJsonPath = path.join(feishuRoot, "package.json"); + const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8")); + + if (!Array.isArray(packageJson.openclaw?.extensions)) { + throw new Error("feishu package.json is missing openclaw.extensions"); + } + + const tsFiles = await walkTypescriptFiles(feishuRoot); + let transpiledCount = 0; + + for (const tsFilePath of tsFiles) { + const sourceText = await readFile(tsFilePath, "utf8"); + const jsOutputPath = tsFilePath.replace(/\.ts$/u, ".js"); + const jsOutput = transpileTsToJs(sourceText, tsFilePath); + await writeFile(jsOutputPath, jsOutput, "utf8"); + transpiledCount += 1; + } + + let entryPath = "./index.js"; + let startupExperimentMode = null; + + if (FEISHU_STARTUP_EXPERIMENT_MODE === "minimal-entry") { + const experimentalEntryPath = path.join( + feishuRoot, + "slimclaw-startup-entry.js", + ); + await writeFile( + experimentalEntryPath, + buildFeishuMinimalStartupEntrySource(), + "utf8", + ); + entryPath = "./slimclaw-startup-entry.js"; + startupExperimentMode = FEISHU_STARTUP_EXPERIMENT_MODE; + } + + packageJson.openclaw.extensions = packageJson.openclaw.extensions.map( + (entry) => (entry === "./index.ts" ? entryPath : entry), + ); + await writeFile( + packageJsonPath, + `${JSON.stringify(packageJson, null, 2)}\n`, + "utf8", + ); + + return { + pluginId: "feishu", + transpiledCount, + packageJsonPath, + startupExperimentMode, + }; +} + +async function transpilePluginTypescriptTree(sourceRoot, targetRoot) { + const tsFiles = await walkTypescriptFiles(sourceRoot); + let transpiledCount = 0; + + for (const tsFilePath of tsFiles) { + const sourceText = await readFile(tsFilePath, "utf8"); + const relativePath = path.relative(sourceRoot, tsFilePath); + const jsOutputPath = path + .join(targetRoot, relativePath) + .replace(/\.ts$/u, ".js"); + const jsOutput = transpileTsToJs(sourceText, tsFilePath); + await mkdir(path.dirname(jsOutputPath), { recursive: true }); + await writeFile(jsOutputPath, jsOutput, "utf8"); + transpiledCount += 1; + } + + return transpiledCount; +} + +function rewriteWeixinRuntimePackage(sourcePackageJson) { + const { devDependencies: _devDependencies, ...runtimePackageJson } = { + ...sourcePackageJson, + openclaw: { + ...sourcePackageJson.openclaw, + extensions: Array.isArray(sourcePackageJson.openclaw?.extensions) + ? sourcePackageJson.openclaw.extensions.map((entry) => + entry === "./index.ts" + ? "./index.js" + : entry.replace(/\.ts$/u, ".js"), + ) + : sourcePackageJson.openclaw?.extensions, + }, + }; + return runtimePackageJson; +} + +export async function prepareBuiltinWeixinPlugin(runtimeDir) { + const sourceRoot = path.join( + repoRoot, + "packages", + "slimclaw", + "runtime-plugins", + "openclaw-weixin", + ); + const targetRoot = path.join( + runtimeDir, + "node_modules", + "openclaw", + "extensions", + "openclaw-weixin", + ); + const sourcePackageJsonPath = path.join(sourceRoot, "package.json"); + const targetPackageJsonPath = path.join(targetRoot, "package.json"); + const runtimeDependencyPaths = [ + path.join(runtimeDir, "node_modules", "qrcode-terminal", "package.json"), + path.join(runtimeDir, "node_modules", "zod", "package.json"), + ]; + const sourcePackageJson = JSON.parse( + await readFile(sourcePackageJsonPath, "utf8"), + ); + + for (const runtimeDependencyPath of runtimeDependencyPaths) { + await readFile(runtimeDependencyPath, "utf8"); + } + + await rm(targetRoot, { recursive: true, force: true }); + await mkdir(targetRoot, { recursive: true }); + + const transpiledCount = await transpilePluginTypescriptTree( + sourceRoot, + targetRoot, + ); + + await writeFile( + targetPackageJsonPath, + `${JSON.stringify(rewriteWeixinRuntimePackage(sourcePackageJson), null, 2)}\n`, + "utf8", + ); + + await cp( + path.join(sourceRoot, "openclaw.plugin.json"), + path.join(targetRoot, "openclaw.plugin.json"), + { + force: true, + }, + ); + + return { + pluginId: "openclaw-weixin", + transpiledCount, + packageJsonPath: targetPackageJsonPath, + }; +} diff --git a/packages/slimclaw/prepare-runtime.mjs b/packages/slimclaw/prepare-runtime.mjs index 347f3adea..83d8babf0 100644 --- a/packages/slimclaw/prepare-runtime.mjs +++ b/packages/slimclaw/prepare-runtime.mjs @@ -3,6 +3,10 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import { installRuntimeAt } from "./install-runtime.mjs"; import { computeFingerprint } from "./postinstall-cache.mjs"; +import { + precompileFeishuPlugin, + prepareBuiltinWeixinPlugin, +} from "./prepare-hot-plugins.mjs"; import { pruneRuntimeAt } from "./prune-runtime.mjs"; import { exists } from "./utils.mjs"; @@ -23,6 +27,24 @@ const criticalRuntimeFiles = [ path.join("node_modules", "@whiskeysockets", "baileys", "package.json"), ]; +function formatDurationMs(durationMs) { + return `${(durationMs / 1000).toFixed(3)}s`; +} + +async function timedStep(stepName, fn, timings) { + const startedAt = performance.now(); + console.log(`[slimclaw:prepare][timing] start ${stepName}`); + try { + return await fn(); + } finally { + const durationMs = performance.now() - startedAt; + timings.push({ stepName, durationMs }); + console.log( + `[slimclaw:prepare][timing] done ${stepName} duration=${formatDurationMs(durationMs)}`, + ); + } +} + async function readCachedFingerprint() { if (!(await exists(cacheFilePath))) { return null; @@ -48,53 +70,117 @@ async function hasCompleteRuntimeInstall() { } export async function prepareSlimclawOwnedRuntimeInstall() { - const fingerprint = await computeFingerprint(runtimeDir); - const cachedFingerprint = await readCachedFingerprint(); - const hasNodeModules = await exists(nodeModulesDir); - const hasCompleteRuntime = hasNodeModules - ? await hasCompleteRuntimeInstall() - : false; + const timings = []; + const totalStartedAt = performance.now(); + const fingerprint = await timedStep( + "compute-fingerprint", + async () => computeFingerprint(runtimeDir), + timings, + ); + const cachedFingerprint = await timedStep( + "read-cached-fingerprint", + async () => readCachedFingerprint(), + timings, + ); + const verification = await timedStep( + "verify-runtime-install", + async () => { + const hasNodeModules = await exists(nodeModulesDir); + const hasCompleteRuntime = hasNodeModules + ? await hasCompleteRuntimeInstall() + : false; + return { hasNodeModules, hasCompleteRuntime }; + }, + timings, + ); + const { hasNodeModules, hasCompleteRuntime } = verification; + let cacheHit = false; + let missReason = "fingerprint_changed"; if ( hasNodeModules && hasCompleteRuntime && cachedFingerprint === fingerprint ) { + cacheHit = true; console.log("slimclaw runtime unchanged, skipping install:pruned."); + const totalDurationMs = performance.now() - totalStartedAt; + console.log( + `[slimclaw:prepare][timing] summary total=${formatDurationMs(totalDurationMs)} cacheHit=true`, + ); return; } if (!hasNodeModules) { + missReason = "node_modules_missing"; console.log( "slimclaw runtime node_modules missing, running install:pruned.", ); } else if (!hasCompleteRuntime) { + missReason = "critical_files_missing"; console.log( "slimclaw runtime critical files missing, running install:pruned.", ); } else if (cachedFingerprint === null) { + missReason = "cache_missing"; console.log("slimclaw runtime cache missing, running install:pruned."); } else { + missReason = "fingerprint_changed"; console.log("slimclaw runtime inputs changed, running install:pruned."); } - await installRuntimeAt(runtimeDir, "pruned"); - await pruneRuntimeAt(runtimeDir); + await timedStep( + "install", + async () => installRuntimeAt(runtimeDir, "pruned"), + timings, + ); + await timedStep("prune", async () => pruneRuntimeAt(runtimeDir), timings); + const weixinResult = await timedStep( + "prepare-builtin-weixin-plugin", + async () => prepareBuiltinWeixinPlugin(runtimeDir), + timings, + ); + console.log( + `[slimclaw:prepare][timing] prepared plugin=${weixinResult.pluginId} files=${weixinResult.transpiledCount}`, + ); + const hotPluginResult = await timedStep( + "precompile-hot-plugins", + async () => precompileFeishuPlugin(runtimeDir), + timings, + ); + console.log( + `[slimclaw:prepare][timing] precompiled plugin=${hotPluginResult.pluginId} files=${hotPluginResult.transpiledCount}${hotPluginResult.startupExperimentMode ? ` startupExperiment=${hotPluginResult.startupExperimentMode}` : ""}`, + ); - await writeFile( - cacheFilePath, - `${JSON.stringify( - { - fingerprint, - updatedAt: new Date().toISOString(), - }, - null, - 2, - )}\n`, - "utf8", + await timedStep( + "write-cache", + async () => + writeFile( + cacheFilePath, + `${JSON.stringify( + { + fingerprint, + updatedAt: new Date().toISOString(), + }, + null, + 2, + )}\n`, + "utf8", + ), + timings, ); console.log("slimclaw runtime cache updated."); + const totalDurationMs = performance.now() - totalStartedAt; + console.log("[slimclaw:prepare][timing] summary"); + for (const timing of timings) { + console.log( + `[slimclaw:prepare][timing] ${timing.stepName}=${formatDurationMs(timing.durationMs)}`, + ); + } + console.log( + `[slimclaw:prepare][timing] total=${formatDurationMs(totalDurationMs)} cacheHit=${cacheHit} missReason=${missReason}`, + ); } if (process.argv[1] === fileURLToPath(import.meta.url)) { diff --git a/packages/slimclaw/prune-runtime.mjs b/packages/slimclaw/prune-runtime.mjs index 4df3f1c77..31f39a18d 100644 --- a/packages/slimclaw/prune-runtime.mjs +++ b/packages/slimclaw/prune-runtime.mjs @@ -6,12 +6,17 @@ import { exists } from "./utils.mjs"; const packageRoot = path.dirname(fileURLToPath(import.meta.url)); +function formatDurationMs(durationMs) { + return `${(durationMs / 1000).toFixed(3)}s`; +} + function resolveDefaultRuntimeDir() { return path.resolve(packageRoot, ".dist-runtime", "openclaw"); } export async function pruneRuntimeAt(runtimeDir, options = {}) { const isDryRun = options.dryRun === true; + const startedAt = performance.now(); if (pruneTargets.length === 0) { console.log("No prune targets configured."); @@ -24,6 +29,7 @@ export async function pruneRuntimeAt(runtimeDir, options = {}) { // is safe for the current list because each target is independent. const pruneResults = await Promise.all( pruneTargets.map(async (relativePath) => { + const targetStartedAt = performance.now(); const absolutePath = path.resolve(runtimeDir, relativePath); const relativeDisplayPath = path.relative(runtimeDir, absolutePath) || "."; @@ -35,20 +41,36 @@ export async function pruneRuntimeAt(runtimeDir, options = {}) { } if (!(await exists(absolutePath))) { - return { action: "skip", relativeDisplayPath }; + return { + action: "skip", + relativeDisplayPath, + durationMs: performance.now() - targetStartedAt, + }; } if (isDryRun) { - return { action: "dry-run", relativeDisplayPath }; + return { + action: "dry-run", + relativeDisplayPath, + durationMs: performance.now() - targetStartedAt, + }; } await rm(absolutePath, { recursive: true, force: true }); - return { action: "removed", relativeDisplayPath }; + return { + action: "removed", + relativeDisplayPath, + durationMs: performance.now() - targetStartedAt, + }; }), ); + let skippedCount = 0; + const slowestPresentTargets = []; + for (const result of pruneResults) { if (result.action === "skip") { + skippedCount += 1; console.log(`Skip missing ${result.relativeDisplayPath}`); continue; } @@ -56,11 +78,13 @@ export async function pruneRuntimeAt(runtimeDir, options = {}) { if (result.action === "dry-run") { console.log(`Would remove ${result.relativeDisplayPath}`); removedCount += 1; + slowestPresentTargets.push(result); continue; } console.log(`Removed ${result.relativeDisplayPath}`); removedCount += 1; + slowestPresentTargets.push(result); } if (removedCount === 0) { @@ -71,6 +95,18 @@ export async function pruneRuntimeAt(runtimeDir, options = {}) { console.log( `${isDryRun ? "Would prune" : "Pruned"} ${removedCount} path${removedCount === 1 ? "" : "s"}.`, ); + const totalDurationMs = performance.now() - startedAt; + const slowestSummary = slowestPresentTargets + .sort((left, right) => right.durationMs - left.durationMs) + .slice(0, 5) + .map( + (entry) => + `${entry.relativeDisplayPath}:${formatDurationMs(entry.durationMs)}`, + ) + .join(","); + console.log( + `[slimclaw:prune][timing] summary total=${formatDurationMs(totalDurationMs)} removed=${removedCount} skipped=${skippedCount}${slowestSummary ? ` slowest=[${slowestSummary}]` : ""}`, + ); } if (process.argv[1] === fileURLToPath(import.meta.url)) { diff --git a/packages/slimclaw/runtime-patches/README.md b/packages/slimclaw/runtime-patches/README.md deleted file mode 100644 index 1df143367..000000000 --- a/packages/slimclaw/runtime-patches/README.md +++ /dev/null @@ -1,5 +0,0 @@ -This directory stores file-level overlays for the locked slimclaw-managed OpenClaw runtime artifact. - -- Paths are relative to `node_modules/` within the prepared runtime root -- `packages/slimclaw/src/runtime-stage.ts` applies these overlays during runtime staging -- Keep patches minimal and version-specific diff --git a/packages/slimclaw/runtime-patches/openclaw/extensions/feishu/src/reply-dispatcher.ts b/packages/slimclaw/runtime-patches/openclaw/extensions/feishu/src/reply-dispatcher.ts deleted file mode 100644 index 1953c081b..000000000 --- a/packages/slimclaw/runtime-patches/openclaw/extensions/feishu/src/reply-dispatcher.ts +++ /dev/null @@ -1,352 +0,0 @@ -import { - createReplyPrefixContext, - createTypingCallbacks, - logTypingFailure, - type ClawdbotConfig, - type ReplyPayload, - type RuntimeEnv, -} from "openclaw/plugin-sdk/feishu"; -import { resolveFeishuAccount } from "./accounts.js"; -import { createFeishuClient } from "./client.js"; -import { sendMediaFeishu } from "./media.js"; -import type { MentionTarget } from "./mention.js"; -import { buildMentionedCardContent } from "./mention.js"; -import { getFeishuRuntime } from "./runtime.js"; -import { sendMarkdownCardFeishu, sendMessageFeishu } from "./send.js"; -import { FeishuStreamingSession, mergeStreamingText } from "./streaming-card.js"; -import { resolveReceiveIdType } from "./targets.js"; -import { addTypingIndicator, removeTypingIndicator, type TypingIndicatorState } from "./typing.js"; - -/** Detect if text contains markdown elements that benefit from card rendering */ -function shouldUseCard(text: string): boolean { - return /```[\s\S]*?```/.test(text) || /\|.+\|[\r\n]+\|[-:| ]+\|/.test(text); -} - -/** Maximum age (ms) for a message to receive a typing indicator reaction. - * Messages older than this are likely replays after context compaction (#30418). */ -const TYPING_INDICATOR_MAX_AGE_MS = 2 * 60_000; -const MS_EPOCH_MIN = 1_000_000_000_000; - -function normalizeEpochMs(timestamp: number | undefined): number | undefined { - if (!Number.isFinite(timestamp) || timestamp === undefined || timestamp <= 0) { - return undefined; - } - // Defensive normalization: some payloads use seconds, others milliseconds. - // Values below 1e12 are treated as epoch-seconds. - return timestamp < MS_EPOCH_MIN ? timestamp * 1000 : timestamp; -} - -export type CreateFeishuReplyDispatcherParams = { - cfg: ClawdbotConfig; - agentId: string; - runtime: RuntimeEnv; - chatId: string; - replyToMessageId?: string; - /** When true, preserve typing indicator on reply target but send messages without reply metadata */ - skipReplyToInMessages?: boolean; - replyInThread?: boolean; - /** True when inbound message is already inside a thread/topic context */ - threadReply?: boolean; - rootId?: string; - mentionTargets?: MentionTarget[]; - accountId?: string; - /** Epoch ms when the inbound message was created. Used to suppress typing - * indicators on old/replayed messages after context compaction (#30418). */ - messageCreateTimeMs?: number; -}; - -export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherParams) { - const core = getFeishuRuntime(); - const { - cfg, - agentId, - chatId, - replyToMessageId, - skipReplyToInMessages, - replyInThread, - threadReply, - rootId, - mentionTargets, - accountId, - } = params; - const sendReplyToMessageId = skipReplyToInMessages ? undefined : replyToMessageId; - const threadReplyMode = threadReply === true; - const effectiveReplyInThread = threadReplyMode ? true : replyInThread; - const account = resolveFeishuAccount({ cfg, accountId }); - const prefixContext = createReplyPrefixContext({ cfg, agentId }); - - let typingState: TypingIndicatorState | null = null; - const typingCallbacks = createTypingCallbacks({ - start: async () => { - // Check if typing indicator is enabled (default: true) - if (!(account.config.typingIndicator ?? true)) { - return; - } - if (!replyToMessageId) { - return; - } - // Skip typing indicator for old messages — likely replays after context - // compaction that would flood users with stale notifications (#30418). - const messageCreateTimeMs = normalizeEpochMs(params.messageCreateTimeMs); - if ( - messageCreateTimeMs !== undefined && - Date.now() - messageCreateTimeMs > TYPING_INDICATOR_MAX_AGE_MS - ) { - return; - } - // Feishu reactions persist until explicitly removed, so skip keepalive - // re-adds when a reaction already exists. Re-adding the same emoji - // triggers a new push notification for every call (#28660). - if (typingState?.reactionId) { - return; - } - typingState = await addTypingIndicator({ - cfg, - messageId: replyToMessageId, - accountId, - runtime: params.runtime, - }); - }, - stop: async () => { - if (!typingState) { - return; - } - await removeTypingIndicator({ cfg, state: typingState, accountId, runtime: params.runtime }); - typingState = null; - }, - onStartError: (err) => - logTypingFailure({ - log: (message) => params.runtime.log?.(message), - channel: "feishu", - action: "start", - error: err, - }), - onStopError: (err) => - logTypingFailure({ - log: (message) => params.runtime.log?.(message), - channel: "feishu", - action: "stop", - error: err, - }), - }); - - const textChunkLimit = core.channel.text.resolveTextChunkLimit(cfg, "feishu", accountId, { - fallbackLimit: 4000, - }); - const chunkMode = core.channel.text.resolveChunkMode(cfg, "feishu"); - const tableMode = core.channel.text.resolveMarkdownTableMode({ cfg, channel: "feishu" }); - const renderMode = account.config?.renderMode ?? "auto"; - // Card streaming may miss thread affinity in topic contexts; use direct replies there. - const streamingEnabled = - !threadReplyMode && account.config?.streaming !== false && renderMode !== "raw"; - - const runtimeLog = (message: string) => { - params.runtime.log?.(`feishu[${account.accountId}]: nexu runtime patch ${message}`); - }; - - const emitReplyOutcome = (payload: { - status: "failed" | "silent" | "sent"; - reasonCode: string; - error?: string; - }) => { - params.runtime.log?.( - `NEXU_EVENT channel.reply_outcome ${JSON.stringify({ - channel: "feishu", - status: payload.status, - reasonCode: payload.reasonCode, - accountId: account.accountId, - chatId, - replyToMessageId, - threadId: rootId, - error: payload.error, - ts: new Date().toISOString(), - })}`, - ); - }; - - let streaming: FeishuStreamingSession | null = null; - let streamText = ""; - let lastPartial = ""; - let hasDeliveredTextFinal = false; - let partialUpdateQueue: Promise = Promise.resolve(); - let streamingStartPromise: Promise | null = null; - type StreamTextUpdateMode = "snapshot" | "delta"; - - const queueStreamingUpdate = ( - nextText: string, - options?: { - dedupeWithLastPartial?: boolean; - mode?: StreamTextUpdateMode; - }, - ) => { - if (!nextText) { - return; - } - if (options?.dedupeWithLastPartial && nextText === lastPartial) { - return; - } - if (options?.dedupeWithLastPartial) { - lastPartial = nextText; - } - const mode = options?.mode ?? "snapshot"; - streamText = - mode === "delta" ? `${streamText}${nextText}` : mergeStreamingText(streamText, nextText); - partialUpdateQueue = partialUpdateQueue.then(async () => { - if (streamingStartPromise) { - await streamingStartPromise; - } - if (streaming?.isActive()) { - await streaming.update(streamText); - } - }); - }; - - const startStreaming = () => { - if (!streamingEnabled || streamingStartPromise || streaming) { - return; - } - streamingStartPromise = (async () => { - const creds = - account.appId && account.appSecret - ? { appId: account.appId, appSecret: account.appSecret, domain: account.domain } - : null; - if (!creds) { - return; - } - - streaming = new FeishuStreamingSession(createFeishuClient(account), creds, (message) => - params.runtime.log?.(`feishu[${account.accountId}] ${message}`), - ); - try { - await streaming.start(chatId, resolveReceiveIdType(chatId), { - replyToMessageId, - replyInThread: effectiveReplyInThread, - rootId, - }); - } catch (error) { - params.runtime.error?.(`feishu: streaming start failed: ${String(error)}`); - streaming = null; - } - })(); - }; - - const closeStreaming = async () => { - if (streamingStartPromise) { - await streamingStartPromise; - } - await partialUpdateQueue; - if (streaming?.isActive()) { - let text = streamText; - if (mentionTargets?.length) { - text = buildMentionedCardContent(mentionTargets, text); - } - await streaming.close(text); - } - streaming = null; - streamingStartPromise = null; - streamText = ""; - lastPartial = ""; - }; - - const { dispatcher, replyOptions, markDispatchIdle } = - core.channel.reply.createReplyDispatcherWithTyping({ - responsePrefix: prefixContext.responsePrefix, - responsePrefixContextProvider: prefixContext.responsePrefixContextProvider, - humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, agentId), - onReplyStart: () => { - if (streamingEnabled && renderMode === "card") { - startStreaming(); - } - void typingCallbacks.onReplyStart?.(); - }, - onPartialText: (text) => { - if (!streamingEnabled) { - return; - } - queueStreamingUpdate(text, { dedupeWithLastPartial: true, mode: "snapshot" }); - }, - onPartialTextDelta: (delta) => { - if (!streamingEnabled) { - return; - } - queueStreamingUpdate(delta, { mode: "delta" }); - }, - onReplyCommitted: async (payloads) => { - hasDeliveredTextFinal ||= payloads.some((payload) => Boolean(payload.text?.trim())); - await closeStreaming(); - await typingCallbacks.onReplyCommitted?.(payloads); - }, - onReplyIdle: async () => { - await closeStreaming(); - await typingCallbacks.onReplyIdle?.(); - }, - onReplyError: async () => { - await closeStreaming(); - await typingCallbacks.onReplyError?.(); - }, - }); - - const sendReply = async (payload: ReplyPayload) => { - if (payload.text?.trim()) { - runtimeLog(`sending text reply (${payload.text.length} chars)`); - } - if (payload.file) { - runtimeLog(`sending file reply (${payload.file.path})`); - } - if (payload.image) { - runtimeLog(`sending image reply (${payload.image.path})`); - } - - const client = createFeishuClient(account); - - if (payload.file) { - await sendMediaFeishu(client, { - chatId, - filePath: payload.file.path, - fileName: payload.file.filename, - receiveIdType: resolveReceiveIdType(chatId), - replyToMessageId: sendReplyToMessageId, - replyInThread: effectiveReplyInThread, - rootId, - }); - return; - } - - const text = payload.text ?? ""; - const shouldUseMarkdownCard = renderMode !== "raw" && shouldUseCard(text); - if (shouldUseMarkdownCard) { - await sendMarkdownCardFeishu(client, { - chatId, - text, - mentionTargets, - receiveIdType: resolveReceiveIdType(chatId), - replyToMessageId: sendReplyToMessageId, - replyInThread: effectiveReplyInThread, - rootId, - }); - return; - } - - await sendMessageFeishu(client, { - chatId, - text, - mentionTargets, - receiveIdType: resolveReceiveIdType(chatId), - replyToMessageId: sendReplyToMessageId, - replyInThread: effectiveReplyInThread, - rootId, - textChunkLimit, - chunkMode, - tableMode, - }); - }; - - return { - sendReply, - emitReplyOutcome, - dispatcher, - replyOptions, - markDispatchIdle, - hasDeliveredTextFinal: () => hasDeliveredTextFinal, - }; -} diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/index.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/index.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/index.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/index.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/openclaw.plugin.json b/packages/slimclaw/runtime-plugins/openclaw-weixin/openclaw.plugin.json similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/openclaw.plugin.json rename to packages/slimclaw/runtime-plugins/openclaw-weixin/openclaw.plugin.json diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/package.json b/packages/slimclaw/runtime-plugins/openclaw-weixin/package.json similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/package.json rename to packages/slimclaw/runtime-plugins/openclaw-weixin/package.json diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/api/api.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/api/api.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/api/api.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/api/api.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/api/config-cache.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/api/config-cache.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/api/config-cache.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/api/config-cache.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/api/session-guard.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/api/session-guard.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/api/session-guard.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/api/session-guard.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/api/types.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/api/types.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/api/types.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/api/types.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/auth/accounts.test.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/auth/accounts.test.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/auth/accounts.test.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/auth/accounts.test.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/auth/accounts.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/auth/accounts.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/auth/accounts.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/auth/accounts.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/auth/login-qr.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/auth/login-qr.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/auth/login-qr.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/auth/login-qr.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/auth/pairing.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/auth/pairing.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/auth/pairing.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/auth/pairing.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/cdn/aes-ecb.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/cdn/aes-ecb.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/cdn/aes-ecb.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/cdn/aes-ecb.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/cdn/cdn-upload.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/cdn/cdn-upload.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/cdn/cdn-upload.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/cdn/cdn-upload.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/cdn/cdn-url.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/cdn/cdn-url.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/cdn/cdn-url.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/cdn/cdn-url.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/cdn/pic-decrypt.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/cdn/pic-decrypt.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/cdn/pic-decrypt.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/cdn/pic-decrypt.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/cdn/upload.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/cdn/upload.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/cdn/upload.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/cdn/upload.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/channel.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/channel.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/channel.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/channel.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/config/config-schema.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/config/config-schema.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/config/config-schema.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/config/config-schema.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/log-upload.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/log-upload.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/log-upload.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/log-upload.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/media/media-download.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/media/media-download.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/media/media-download.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/media/media-download.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/media/mime.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/media/mime.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/media/mime.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/media/mime.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/media/silk-transcode.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/media/silk-transcode.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/media/silk-transcode.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/media/silk-transcode.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/messaging/debug-mode.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/messaging/debug-mode.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/messaging/debug-mode.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/messaging/debug-mode.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/messaging/error-notice.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/messaging/error-notice.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/messaging/error-notice.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/messaging/error-notice.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/messaging/inbound.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/messaging/inbound.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/messaging/inbound.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/messaging/inbound.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/messaging/process-message.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/messaging/process-message.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/messaging/process-message.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/messaging/process-message.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/messaging/send-media.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/messaging/send-media.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/messaging/send-media.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/messaging/send-media.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/messaging/send.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/messaging/send.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/messaging/send.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/messaging/send.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/messaging/slash-commands.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/messaging/slash-commands.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/messaging/slash-commands.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/messaging/slash-commands.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/monitor/monitor.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/monitor/monitor.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/monitor/monitor.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/monitor/monitor.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/runtime.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/runtime.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/runtime.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/runtime.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/storage/state-dir.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/storage/state-dir.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/storage/state-dir.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/storage/state-dir.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/storage/sync-buf.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/storage/sync-buf.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/storage/sync-buf.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/storage/sync-buf.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/util/logger.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/util/logger.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/util/logger.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/util/logger.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/util/random.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/util/random.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/util/random.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/util/random.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/util/redact.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/util/redact.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/util/redact.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/util/redact.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/src/vendor.d.ts b/packages/slimclaw/runtime-plugins/openclaw-weixin/src/vendor.d.ts similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/src/vendor.d.ts rename to packages/slimclaw/runtime-plugins/openclaw-weixin/src/vendor.d.ts diff --git a/apps/controller/static/runtime-plugins/openclaw-weixin/tsconfig.json b/packages/slimclaw/runtime-plugins/openclaw-weixin/tsconfig.json similarity index 100% rename from apps/controller/static/runtime-plugins/openclaw-weixin/tsconfig.json rename to packages/slimclaw/runtime-plugins/openclaw-weixin/tsconfig.json diff --git a/packages/slimclaw/runtime-seed/package-lock.json b/packages/slimclaw/runtime-seed/package-lock.json index 26b517027..f3a104829 100644 --- a/packages/slimclaw/runtime-seed/package-lock.json +++ b/packages/slimclaw/runtime-seed/package-lock.json @@ -7,7 +7,9 @@ "name": "slimclaw-runtime-seed", "dependencies": { "@larksuiteoapi/node-sdk": "^1.59.0", - "openclaw": "2026.3.7" + "openclaw": "2026.3.7", + "qrcode-terminal": "^0.12.0", + "zod": "^4.3.6" }, "optionalDependencies": { "@snazzah/davey-darwin-arm64": "^0.1.10" diff --git a/packages/slimclaw/runtime-seed/package.json b/packages/slimclaw/runtime-seed/package.json index a3497f486..116d345ee 100644 --- a/packages/slimclaw/runtime-seed/package.json +++ b/packages/slimclaw/runtime-seed/package.json @@ -12,7 +12,9 @@ }, "dependencies": { "@larksuiteoapi/node-sdk": "^1.59.0", - "openclaw": "2026.3.7" + "openclaw": "2026.3.7", + "qrcode-terminal": "^0.12.0", + "zod": "^4.3.6" }, "optionalDependencies": { "@snazzah/davey-darwin-arm64": "^0.1.10" diff --git a/packages/slimclaw/src/index.ts b/packages/slimclaw/src/index.ts index 62346d816..b41fcb9d9 100644 --- a/packages/slimclaw/src/index.ts +++ b/packages/slimclaw/src/index.ts @@ -4,7 +4,6 @@ export { resolveSlimclawRuntimeArtifacts, getSlimclawDescriptorPath, getSlimclawRuntimeRoot, - getSlimclawRuntimePatchesRoot, resolveSlimclawRuntimePaths, type PrepareSlimclawRuntimeStageOptions, type PrepareSlimclawRuntimeStageResult, diff --git a/packages/slimclaw/src/runtime-paths.ts b/packages/slimclaw/src/runtime-paths.ts index e92bb0e00..8b170dbe0 100644 --- a/packages/slimclaw/src/runtime-paths.ts +++ b/packages/slimclaw/src/runtime-paths.ts @@ -91,12 +91,6 @@ export function getSlimclawDescriptorPath( ); } -export function getSlimclawRuntimePatchesRoot( - workspaceRoot = getDefaultWorkspaceRoot(), -): string { - return path.join(workspaceRoot, "packages", "slimclaw", "runtime-patches"); -} - function readJsonFile(filePath: string): T | null { try { return JSON.parse(readFileSync(filePath, "utf8")) as T; @@ -260,7 +254,6 @@ export async function prepareSlimclawRuntimeStage( return prepareSlimclawRuntimeStageInternal({ sourceOpenclawRoot: path.dirname(runtimePaths.entryPath), - patchRoot: getSlimclawRuntimePatchesRoot(workspaceRoot), targetStageRoot: options.targetStageRoot, log: options.log, }); @@ -275,6 +268,5 @@ export async function computeSlimclawRuntimeStageFingerprint(): Promise return computeSlimclawRuntimeStageFingerprintInternal({ sourceOpenclawRoot: path.dirname(runtimePaths.entryPath), - patchRoot: getSlimclawRuntimePatchesRoot(workspaceRoot), }); } diff --git a/packages/slimclaw/src/runtime-stage.ts b/packages/slimclaw/src/runtime-stage.ts index 24f27a95c..f08d903ef 100644 --- a/packages/slimclaw/src/runtime-stage.ts +++ b/packages/slimclaw/src/runtime-stage.ts @@ -11,9 +11,9 @@ import { } from "node:fs/promises"; import { basename, dirname, relative, resolve } from "node:path"; -const OPENCLAW_PACKAGE_PATCH_DIRNAME = "openclaw"; const STAGE_MANIFEST_FILENAME = "manifest.json"; -const STAGE_PATCH_VERSION = "2026-04-09-slimclaw-runtime-stage-v1"; +const STAGE_PATCH_VERSION = + "2026-04-15-slimclaw-runtime-stage-v7-remove-feishu-patches"; const REPLY_OUTCOME_HELPER_SEARCH = ` const sessionKey = ctx.SessionKey; const startTime = diagnosticsEnabled ? Date.now() : 0; @@ -60,16 +60,6 @@ emitReplyOutcome("failed", "dispatch_threw", err instanceof Error ? err.message recordProcessed("error", { error: String(err) }); markIdle("message_error"); `.trim(); -const FEISHU_ERROR_REPLY_SUPPRESS_GUARD_SEARCH = ` -const genericErrorText = "The AI service returned an error. Please try again."; - const suppressErrorTextReply = params.messageChannel === "feishu" && lastAssistantErrored; - if (errorText && !suppressErrorTextReply) replyItems.push({ -`.trim(); -const FEISHU_ERROR_REPLY_SUPPRESS_GUARD_REPLACEMENT = ` -const genericErrorText = "The AI service returned an error. Please try again."; - const suppressErrorTextReply = (params.messageChannel === "feishu" || params.messageProvider === "feishu") && lastAssistantErrored; - if (errorText && !suppressErrorTextReply) replyItems.push({ -`.trim(); const CORE_EMBEDDED_PAYLOAD_MESSAGE_CHANNEL_SEARCH = ` toolResultFormat: resolvedToolResultFormat, messageChannel: params.messageChannel, @@ -83,33 +73,6 @@ toolResultFormat: resolvedToolResultFormat, suppressToolErrorWarnings: params.suppressToolErrorWarnings, inlineToolResultsAllowed: false, `.trim(); -const FEISHU_PRE_REPLY_FINAL_SEARCH = [ - "defaultRuntime.error(`Embedded agent failed before reply: ${message}`);", - '\t\tconst trimmedMessage = (isTransientHttp ? sanitizeUserFacingText(message, { errorContext: true }) : message).replace(/\\.\\s*$/, "");', - "\t\treturn {", - '\t\t\tkind: "final",', - '\t\t\tpayload: { text: isContextOverflow ? "⚠️ Context overflow — prompt too large for this model. Try a shorter message or a larger-context model." : isRoleOrderingError ? "⚠️ Message ordering conflict - please try again. If this persists, use /new to start a fresh session." : `⚠️ Agent failed before reply: ${trimmedMessage}.\\nLogs: openclaw logs --follow` }', - "\t\t};", -].join("\n"); -const FEISHU_PRE_REPLY_FINAL_REPLACEMENT = [ - "defaultRuntime.error(`Embedded agent failed before reply: ${message}`);", - '\t\tconst trimmedMessage = (isTransientHttp ? sanitizeUserFacingText(message, { errorContext: true }) : message).replace(/\\.\\s*$/, "");', - '\t\tif (resolveMessageChannel(params.sessionCtx.Surface, params.sessionCtx.Provider) === "feishu") return {', - '\t\t\tkind: "success",', - "\t\t\trunId,", - "\t\t\trunResult: { payloads: [] },", - "\t\t\tfallbackProvider,", - "\t\t\tfallbackModel,", - "\t\t\tfallbackAttempts,", - "\t\t\tdidLogHeartbeatStrip,", - "\t\t\tautoCompactionCompleted,", - "\t\t\tdirectlySentBlockKeys: directlySentBlockKeys.size > 0 ? directlySentBlockKeys : void 0", - "\t\t};", - "\t\treturn {", - '\t\t\tkind: "final",', - '\t\t\tpayload: { text: isContextOverflow ? "⚠️ Context overflow — prompt too large for this model. Try a shorter message or a larger-context model." : isRoleOrderingError ? "⚠️ Message ordering conflict - please try again. If this persists, use /new to start a fresh session." : `⚠️ Agent failed before reply: ${trimmedMessage}.\\nLogs: openclaw logs --follow` }', - "\t\t};", -].join("\n"); const CONTEXT_OVERFLOW_PATCHES = [ { search: @@ -164,6 +127,32 @@ const STOP_FOLLOWUP_ON_EMPTY_SEARCH = "if (payloadArray.length === 0) return finalizeWithFollowup(void 0, queueKey, runFollowupTurn);"; const STOP_FOLLOWUP_ON_EMPTY_REPLACEMENT = "if (payloadArray.length === 0) return;"; +const PLUGIN_MODULE_LOAD_SEARCH = [ + "\t\tlet mod = null;", + "\t\ttry {", + "\t\t\tmod = getJiti()(safeSource);", + "\t\t} catch (err) {", +].join("\n"); +const PLUGIN_MODULE_LOAD_REPLACEMENT = [ + "\t\tconst nexuPluginLoadStartedAt = Date.now();", + "\t\tlet mod = null;", + "\t\ttry {", + "\t\t\tmod = getJiti()(safeSource);", + '\t\t\tlogger.info("[openclaw:plugin-timing] " + JSON.stringify({ phase: "load", pluginId: record.id, origin: candidate.origin, source: record.source, elapsedMs: Date.now() - nexuPluginLoadStartedAt }));', + "\t\t} catch (err) {", +].join("\n"); +const PLUGIN_REGISTER_SEARCH = [ + "\t\ttry {", + "\t\t\tconst result = register(api);", + '\t\t\tif (result && typeof result.then === "function") registry.diagnostics.push({', +].join("\n"); +const PLUGIN_REGISTER_REPLACEMENT = [ + "\t\ttry {", + "\t\t\tconst nexuPluginRegisterStartedAt = Date.now();", + "\t\t\tconst result = register(api);", + '\t\t\tlogger.info("[openclaw:plugin-timing] " + JSON.stringify({ phase: "register", pluginId: record.id, origin: candidate.origin, source: record.source, elapsedMs: Date.now() - nexuPluginRegisterStartedAt }));', + '\t\t\tif (result && typeof result.then === "function") registry.diagnostics.push({', +].join("\n"); const LOCALE_READER_LINES = [ 'const _nexuLocale = (() => { try { const _fs = require("node:fs"); const _path = require("node:path"); const _stateDir = process.env.OPENCLAW_STATE_DIR; if (!_stateDir) return "zh-CN"; const _fp = _path.join(_stateDir, "nexu-credit-guard-state.json"); const _mt = _fs.statSync(_fp).mtimeMs; if (globalThis.__nexuCgMt === _mt) return globalThis.__nexuCgLocale || "zh-CN"; const _d = JSON.parse(_fs.readFileSync(_fp, "utf8")); globalThis.__nexuCgMt = _mt; globalThis.__nexuCgLocale = _d.locale || "zh-CN"; return globalThis.__nexuCgLocale; } catch { return globalThis.__nexuCgLocale || "zh-CN"; } })();', ] as const; @@ -190,98 +179,6 @@ const PLUGIN_SDK_BUNDLE_PATTERNS = [ /^dispatch-.*\.js$/u, ] as const; const CORE_DIST_REPLY_BUNDLE_PATTERNS = [/^reply-.*\.js$/u] as const; -const FEISHU_PRE_LLM_SINGLE_AGENT_SEARCH = ` - // --- Single-agent dispatch (existing behavior) --- - const ctxPayload = buildCtxPayloadForAgent( - route.sessionKey, - route.accountId, - ctx.mentionedBot, - ); -`.trim(); -const FEISHU_SYNTHETIC_PRE_LLM_LINES = [ - " const syntheticFailureTriggerPrefix = process.env.NEXU_FEISHU_TEST_TRIGGER_PREFIX?.trim();", - " if (syntheticFailureTriggerPrefix && ctx.content.includes(syntheticFailureTriggerPrefix)) {", - " const syntheticInput = ctx.content.slice(ctx.content.indexOf(syntheticFailureTriggerPrefix) + syntheticFailureTriggerPrefix.length).trim();", - " runtime.log?.(`NEXU_EVENT channel.reply_outcome ${JSON.stringify({", - ' channel: "feishu",', - ' status: "failed",', - ' reasonCode: "synthetic_pre_llm_failure",', - " accountId: account.accountId,", - " chatId: ctx.chatId,", - " replyToMessageId: replyTargetMessageId,", - " threadId: ctx.rootId,", - " sessionKey: route.sessionKey,", - " syntheticInput,", - ' error: "synthetic pre-llm failure",', - " ts: new Date().toISOString(),", - " })}`);", - " log(", - " `feishu[${account.accountId}]: synthetic pre-llm failure triggered (session=${route.sessionKey})`,", - " );", - " return;", - " }", -] as const; -const FEISHU_SYNTHETIC_PRE_LLM_BLOCK = - FEISHU_SYNTHETIC_PRE_LLM_LINES.join("\n"); -const FEISHU_PRE_LLM_SINGLE_AGENT_REPLACEMENT = [ - " // --- Single-agent dispatch (existing behavior) ---", - " const ctxPayload = buildCtxPayloadForAgent(", - " route.sessionKey,", - " route.accountId,", - " ctx.mentionedBot,", - " );", - ...FEISHU_SYNTHETIC_PRE_LLM_LINES, -].join("\n"); -const LEGACY_FEISHU_TRIGGER_CALLSITE = ` - accountId: account.accountId, - syntheticFailureTriggerText: ctx.content, - messageCreateTimeMs, -`.trim(); -const LEGACY_FEISHU_TRIGGER_CALLSITE_REPLACEMENT = ` - accountId: account.accountId, - messageCreateTimeMs, -`.trim(); -const LEGACY_FEISHU_PRE_LLM_BLOCK = [ - ' if (ctx.content.includes("__fail_reply__")) {', - " runtime.log?.(`NEXU_EVENT channel.reply_outcome ${JSON.stringify({", - ' channel: "feishu",', - ' status: "failed",', - ' reasonCode: "synthetic_pre_llm_failure",', - " accountId: account.accountId,", - " chatId: ctx.chatId,", - " replyToMessageId: replyTargetMessageId,", - " threadId: ctx.rootId,", - " sessionKey: route.sessionKey,", - ' error: "synthetic pre-llm failure",', - " ts: new Date().toISOString(),", - " })}`);", - " log(", - " `feishu[${account.accountId}]: synthetic pre-llm failure triggered (session=${route.sessionKey})`,", - " );", - " return;", - " }", - "", -].join("\n"); -const LEGACY_FEISHU_SINGLE_AGENT_TRIGGER_BLOCK = [ - ' if (ctx.content.includes("__fail_reply__")) {', - " runtime.log?.(`NEXU_EVENT channel.reply_outcome ${JSON.stringify({", - ' channel: "feishu",', - ' status: "failed",', - ' reasonCode: "synthetic_pre_llm_failure",', - " accountId: account.accountId,", - " chatId: ctx.chatId,", - " replyToMessageId: replyTargetMessageId,", - " threadId: ctx.rootId,", - " sessionKey: route.sessionKey,", - ' error: "synthetic pre-llm failure",', - " ts: new Date().toISOString(),", - " })}`);", - " log(", - " `feishu[${account.accountId}]: synthetic pre-llm failure triggered (session=${route.sessionKey})`,", - " );", - " return;", - " }", -].join("\n"); type StageLog = (message: string) => void; @@ -293,12 +190,10 @@ type StageManifest = { export type ComputeSlimclawRuntimeStageFingerprintOptions = { sourceOpenclawRoot: string; - patchRoot: string; }; export type PrepareSlimclawRuntimeStageInternalOptions = { sourceOpenclawRoot: string; - patchRoot: string; targetStageRoot: string; log?: StageLog; }; @@ -355,6 +250,10 @@ async function collectFiles(rootPath: string): Promise { for (const entry of [...entries].sort((left, right) => left.name.localeCompare(right.name), )) { + if (entry.name === "node_modules") { + continue; + } + const entryPath = resolve(rootPath, entry.name); if (entry.isDirectory()) { files.push(...(await collectFiles(entryPath))); @@ -369,39 +268,6 @@ async function collectFiles(rootPath: string): Promise { return files; } -async function readOverlayFiles( - patchRoot: string, - log?: StageLog, -): Promise> { - const patchedFiles = new Map(); - const openclawPackagePatchRoot = resolve( - patchRoot, - OPENCLAW_PACKAGE_PATCH_DIRNAME, - ); - - if (!(await directoryExists(openclawPackagePatchRoot))) { - return patchedFiles; - } - - const patchFiles = await collectFiles(openclawPackagePatchRoot); - - for (const patchFilePath of patchFiles) { - patchedFiles.set( - relative(openclawPackagePatchRoot, patchFilePath), - await readFile(patchFilePath, "utf8"), - ); - } - - if (patchFiles.length > 0) { - emitLog( - log, - `[slimclaw-runtime-stage] prepared ${patchFiles.length} overlay patch file(s) from ${openclawPackagePatchRoot}`, - ); - } - - return patchedFiles; -} - function applyExactReplacement( source: string, search: string, @@ -415,24 +281,6 @@ function applyExactReplacement( return source.replace(search, replacement); } -function countOccurrences(source: string, search: string): number { - if (search.length === 0) { - return 0; - } - - let count = 0; - let index = 0; - - while (true) { - const nextIndex = source.indexOf(search, index); - if (nextIndex === -1) { - return count; - } - count += 1; - index = nextIndex + search.length; - } -} - function injectKnownLinkErrorMappings( source: string, bundleName: string, @@ -467,71 +315,6 @@ async function patchReplyOutcomeBridge( log?: StageLog, ): Promise> { const patchedFiles = new Map(); - const feishuBotPath = resolve( - openclawPackageRoot, - "extensions", - "feishu", - "src", - "bot.ts", - ); - let feishuBotSource = await readFile(feishuBotPath, "utf8"); - - if (feishuBotSource.includes(LEGACY_FEISHU_PRE_LLM_BLOCK)) { - feishuBotSource = feishuBotSource.replaceAll( - LEGACY_FEISHU_PRE_LLM_BLOCK, - "", - ); - } - - if (feishuBotSource.includes(LEGACY_FEISHU_SINGLE_AGENT_TRIGGER_BLOCK)) { - feishuBotSource = feishuBotSource.replaceAll( - LEGACY_FEISHU_SINGLE_AGENT_TRIGGER_BLOCK, - FEISHU_PRE_LLM_SINGLE_AGENT_REPLACEMENT, - ); - } - - if (feishuBotSource.includes(LEGACY_FEISHU_TRIGGER_CALLSITE)) { - feishuBotSource = feishuBotSource.replaceAll( - LEGACY_FEISHU_TRIGGER_CALLSITE, - LEGACY_FEISHU_TRIGGER_CALLSITE_REPLACEMENT, - ); - } - - if (feishuBotSource.includes(FEISHU_SYNTHETIC_PRE_LLM_BLOCK)) { - feishuBotSource = feishuBotSource.replaceAll( - FEISHU_SYNTHETIC_PRE_LLM_BLOCK, - "", - ); - } - - if (feishuBotSource.includes(FEISHU_PRE_LLM_SINGLE_AGENT_SEARCH)) { - feishuBotSource = feishuBotSource.replace( - FEISHU_PRE_LLM_SINGLE_AGENT_SEARCH, - FEISHU_PRE_LLM_SINGLE_AGENT_REPLACEMENT, - ); - emitLog( - log, - "[slimclaw-runtime-stage] patched feishu single-agent pre-llm trigger", - ); - } - - if (countOccurrences(feishuBotSource, FEISHU_SYNTHETIC_PRE_LLM_BLOCK) !== 1) { - throw new Error( - "Feishu bot patch did not converge to a single synthetic pre-llm block.", - ); - } - - if (feishuBotSource.includes("return;\n }\n route.sessionKey,")) { - throw new Error( - "Feishu bot patch left a dangling buildCtxPayloadForAgent argument tail.", - ); - } - - patchedFiles.set( - relative(openclawPackageRoot, feishuBotPath), - feishuBotSource, - ); - const patchBundleGroup = async ( bundleDir: string, patterns: readonly RegExp[], @@ -575,19 +358,6 @@ async function patchReplyOutcomeBridge( ); } - if (source.includes(FEISHU_ERROR_REPLY_SUPPRESS_GUARD_SEARCH)) { - source = applyExactReplacement( - source, - FEISHU_ERROR_REPLY_SUPPRESS_GUARD_SEARCH, - FEISHU_ERROR_REPLY_SUPPRESS_GUARD_REPLACEMENT, - `${bundleName}: feishu error reply suppress guard`, - ); - emitLog( - log, - `[slimclaw-runtime-stage] patched feishu error final suppression in ${bundleName}`, - ); - } - if (source.includes(CORE_EMBEDDED_PAYLOAD_MESSAGE_CHANNEL_SEARCH)) { source = applyExactReplacement( source, @@ -601,22 +371,6 @@ async function patchReplyOutcomeBridge( ); } - if ( - !source.includes("runResult: { payloads: [] }") && - source.includes(FEISHU_PRE_REPLY_FINAL_SEARCH) - ) { - source = applyExactReplacement( - source, - FEISHU_PRE_REPLY_FINAL_SEARCH, - FEISHU_PRE_REPLY_FINAL_REPLACEMENT, - `${bundleName}: feishu pre-reply final suppression`, - ); - emitLog( - log, - `[slimclaw-runtime-stage] patched feishu pre-reply final suppression in ${bundleName}`, - ); - } - if (source.includes(FORMATTED_ASSISTANT_ERROR_PRIORITY_SEARCH)) { source = applyExactReplacement( source, @@ -711,6 +465,28 @@ async function patchReplyOutcomeBridge( ); } + if ( + source.includes(PLUGIN_MODULE_LOAD_SEARCH) && + !source.includes("[openclaw:plugin-timing]") + ) { + source = applyExactReplacement( + source, + PLUGIN_MODULE_LOAD_SEARCH, + PLUGIN_MODULE_LOAD_REPLACEMENT, + `${bundleName}: plugin module load timing`, + ); + source = applyExactReplacement( + source, + PLUGIN_REGISTER_SEARCH, + PLUGIN_REGISTER_REPLACEMENT, + `${bundleName}: plugin register timing`, + ); + emitLog( + log, + `[slimclaw-runtime-stage] patched plugin load/register timing in ${bundleName}`, + ); + } + if (source.includes(EMPTY_PAYLOADS_FALLBACK_SEARCH)) { source = applyExactReplacement( source, @@ -775,6 +551,44 @@ async function patchReplyOutcomeBridge( "core dist reply", ); + const allDistEntries = await readdir(resolve(openclawPackageRoot, "dist")); + for (const fileName of allDistEntries.sort((left, right) => + left.localeCompare(right), + )) { + if (!fileName.endsWith(".js")) { + continue; + } + + const filePath = resolve(openclawPackageRoot, "dist", fileName); + let source = patchedFiles.get(relative(openclawPackageRoot, filePath)); + if (!source) { + source = await readFile(filePath, "utf8"); + } + + if ( + source.includes(PLUGIN_MODULE_LOAD_SEARCH) && + !source.includes("[openclaw:plugin-timing]") + ) { + source = applyExactReplacement( + source, + PLUGIN_MODULE_LOAD_SEARCH, + PLUGIN_MODULE_LOAD_REPLACEMENT, + `${fileName}: plugin module load timing`, + ); + source = applyExactReplacement( + source, + PLUGIN_REGISTER_SEARCH, + PLUGIN_REGISTER_REPLACEMENT, + `${fileName}: plugin register timing`, + ); + patchedFiles.set(relative(openclawPackageRoot, filePath), source); + emitLog( + log, + `[slimclaw-runtime-stage] patched plugin load/register timing in ${fileName}`, + ); + } + } + const patchHelperBundleGroup = async (bundleDir: string, label: string) => { const entries = await readdir(bundleDir); const bundleNames = entries @@ -851,13 +665,9 @@ async function patchReplyOutcomeBridge( async function collectFingerprintFiles( sourceOpenclawRoot: string, - patchRoot: string, ): Promise> { const files: Array<{ label: string; path: string }> = []; - const sourceCandidates = [ - resolve(sourceOpenclawRoot, "package.json"), - resolve(sourceOpenclawRoot, "extensions", "feishu", "src", "bot.ts"), - ]; + const sourceCandidates = [resolve(sourceOpenclawRoot, "package.json")]; for (const sourceFilePath of sourceCandidates) { if (await pathExists(sourceFilePath)) { @@ -868,6 +678,30 @@ async function collectFingerprintFiles( } } + const extensionRoots = [ + resolve(sourceOpenclawRoot, "extensions", "feishu"), + resolve(sourceOpenclawRoot, "extensions", "openclaw-weixin"), + ]; + for (const extensionRoot of extensionRoots) { + if (!(await directoryExists(extensionRoot))) { + continue; + } + + const extensionFiles = (await collectFiles(extensionRoot)).filter( + (filePath) => + filePath.endsWith(".ts") || + filePath.endsWith(".js") || + filePath.endsWith(".json"), + ); + + for (const filePath of extensionFiles) { + files.push({ + label: `source:${relative(sourceOpenclawRoot, filePath)}`, + path: filePath, + }); + } + } + const bundleTargets = [ { dir: resolve(sourceOpenclawRoot, "dist"), patterns: [/\.js$/u] }, { @@ -893,19 +727,6 @@ async function collectFingerprintFiles( } } - const openclawPackagePatchRoot = resolve( - patchRoot, - OPENCLAW_PACKAGE_PATCH_DIRNAME, - ); - if (await directoryExists(openclawPackagePatchRoot)) { - for (const patchFilePath of await collectFiles(openclawPackagePatchRoot)) { - files.push({ - label: `patch:${relative(openclawPackagePatchRoot, patchFilePath)}`, - path: patchFilePath, - }); - } - } - return files; } @@ -917,7 +738,6 @@ export async function computeSlimclawRuntimeStageFingerprint( for (const file of await collectFingerprintFiles( options.sourceOpenclawRoot, - options.patchRoot, )) { hash.update(`${file.label}\n`); hash.update(await readFile(file.path)); @@ -949,7 +769,6 @@ export async function prepareSlimclawRuntimeStageInternal( ); const fingerprint = await computeSlimclawRuntimeStageFingerprint({ sourceOpenclawRoot: options.sourceOpenclawRoot, - patchRoot: options.patchRoot, }); stageTimer.mark( `[slimclaw-runtime-stage] computed runtime stage fingerprint for ${options.targetStageRoot}`, @@ -994,14 +813,13 @@ export async function prepareSlimclawRuntimeStageInternal( ); stageTimer.mark( - `[slimclaw-runtime-stage] applying overlay and compatibility patches inside ${stageRoot}`, + `[slimclaw-runtime-stage] applying compatibility patches inside ${stageRoot}`, ); - const overlayFiles = await readOverlayFiles(options.patchRoot, options.log); const bridgePatchedFiles = await patchReplyOutcomeBridge( stagedOpenclawRoot, options.log, ); - const patchedFiles = new Map([...overlayFiles, ...bridgePatchedFiles]); + const patchedFiles = bridgePatchedFiles; for (const [patchRelativePath, patchedSource] of patchedFiles) { await writeFile( diff --git a/scripts/postinstall.mjs b/scripts/postinstall.mjs index 21e3e0489..91d059adf 100644 --- a/scripts/postinstall.mjs +++ b/scripts/postinstall.mjs @@ -1,9 +1,7 @@ import { spawn } from "node:child_process"; -import { access } from "node:fs/promises"; import { resolve } from "node:path"; const repoRoot = process.cwd(); -const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm"; function createCommandSpec(command, args) { if ( @@ -28,15 +26,6 @@ function isTruthy(value) { return normalizedValue === "1" || normalizedValue === "true"; } -async function pathExists(targetPath) { - try { - await access(targetPath); - return true; - } catch { - return false; - } -} - async function run(command, args) { await new Promise((resolvePromise, rejectPromise) => { const commandSpec = createCommandSpec(command, args); @@ -62,38 +51,6 @@ async function run(command, args) { }); } -async function installWeixinRuntimePlugin() { - const pluginRoot = resolve( - repoRoot, - "apps/controller/static/runtime-plugins/openclaw-weixin", - ); - const pluginLockfilePath = resolve(pluginRoot, "package-lock.json"); - - if (await pathExists(pluginLockfilePath)) { - await run(npmCommand, [ - "--prefix", - "./apps/controller/static/runtime-plugins/openclaw-weixin", - "ci", - "--omit=dev", - "--ignore-scripts", - "--no-audit", - "--no-fund", - ]); - return; - } - - await run(npmCommand, [ - "--prefix", - "./apps/controller/static/runtime-plugins/openclaw-weixin", - "install", - "--production", - "--ignore-scripts", - "--prefer-offline", - "--no-audit", - "--no-fund", - ]); -} - async function buildDevUtils() { await run(process.execPath, [ resolve(repoRoot, "node_modules", "typescript", "bin", "tsc"), @@ -115,6 +72,5 @@ if (isTruthy(process.env.NEXU_SKIP_RUNTIME_POSTINSTALL)) { process.exit(0); } -await installWeixinRuntimePlugin(); await buildDevUtils(); await buildSlimclaw(); diff --git a/tests/notify/developer-notify.test.ts b/tests/notify/developer-notify.test.ts index cb3b630c7..f74028d1e 100644 --- a/tests/notify/developer-notify.test.ts +++ b/tests/notify/developer-notify.test.ts @@ -36,7 +36,9 @@ describe("developer-notify", () => { prUrl: "https://github.com/nexu-io/nexu/pull/10", }); - expect(payload.card.header.title.content).toContain("又有新贡献者给 Nexu 提 PR"); + expect(payload.card.header.title.content).toContain( + "又有新贡献者给 Nexu 提 PR", + ); expect(payload.card.body.elements[0]).toMatchObject({ tag: "markdown", content: expect.stringContaining("**Title:** fix: resolve login crash"), @@ -72,7 +74,9 @@ describe("developer-notify", () => { ).toEqual(["Good First Issue", "贡献者指南", "查看全部 Issue"]); expect(payload.card.body.elements[2]).toMatchObject({ tag: "markdown", - content: expect.stringContaining("只需 3 步💥:❶ 选任务 ❷ 认领 ❸ 提交 PR"), + content: expect.stringContaining( + "只需 3 步💥:❶ 选任务 ❷ 认领 ❸ 提交 PR", + ), }); }); @@ -81,7 +85,9 @@ describe("developer-notify", () => { issueUrl: "https://github.com/nexu-io/nexu/issues/99", }); - expect(payload.card.header.title.content).toContain("刚新增 1 条 issue 等你来领取"); + expect(payload.card.header.title.content).toContain( + "刚新增 1 条 issue 等你来领取", + ); expect(payload.card.body.elements[1]).toMatchObject({ tag: "column_set" }); expect( payload.card.body.elements[1].columns.map( diff --git a/tests/slimclaw-runtime/postinstall-cache.test.ts b/tests/slimclaw-runtime/postinstall-cache.test.ts index f9114680d..8008a70fc 100644 --- a/tests/slimclaw-runtime/postinstall-cache.test.ts +++ b/tests/slimclaw-runtime/postinstall-cache.test.ts @@ -16,8 +16,11 @@ async function createRuntimeFixture() { ); tempDirs.push(tempRoot); - const rewrittenCacheInputs = originalCacheInputs.map((inputPath) => - path.join(tempRoot, path.basename(inputPath)), + const rewrittenCacheInputs = originalCacheInputs.map((inputPath, index) => + path.join( + tempRoot, + `${String(index).padStart(3, "0")}-${path.basename(inputPath)}`, + ), ); cacheInputs.splice(0, cacheInputs.length, ...rewrittenCacheInputs); diff --git a/tools/dev/src/services/openclaw.ts b/tools/dev/src/services/openclaw.ts index f9dd3db20..087284234 100644 --- a/tools/dev/src/services/openclaw.ts +++ b/tools/dev/src/services/openclaw.ts @@ -299,7 +299,14 @@ async function waitForOpenclawCurrentLock(options: { return null; } -async function prepareOpenclawEntryPath(): Promise { +async function prepareOpenclawEntryPath(): Promise<{ + entryPath: string; + durationMs: number; + reused: boolean; + patchedFileCount: number; + fingerprint: string; +}> { + const startedAt = Date.now(); logger.info("preparing staged openclaw runtime", { targetStageRoot: getOpenclawRuntimeStageRootPath(), }); @@ -315,8 +322,21 @@ async function prepareOpenclawEntryPath(): Promise { reused: stage.reused, patchedFileCount: stage.patchedFileCount, }); + logger.info("openclaw startup timing", { + phase: "slimclaw-stage", + elapsedMs: Date.now() - startedAt, + reused: stage.reused, + patchedFileCount: stage.patchedFileCount, + fingerprint: stage.fingerprint, + }); - return join(stage.stagedOpenclawRoot, "openclaw.mjs"); + return { + entryPath: join(stage.stagedOpenclawRoot, "openclaw.mjs"), + durationMs: Date.now() - startedAt, + reused: stage.reused, + patchedFileCount: stage.patchedFileCount, + fingerprint: stage.fingerprint, + }; } export async function startOpenclawDevProcess(options: { @@ -345,6 +365,14 @@ export async function startOpenclawDevProcess(options: { runId, sessionId, }); + const timingStartedAt = Date.now(); + let stageDurationMs = 0; + let spawnDurationMs = 0; + let portReadyDurationMs = 0; + let healthReadyDurationMs = 0; + let stageReused = false; + let stagePatchedFileCount = 0; + let stageFingerprint = "unknown"; logOpenclawTiming("start:entered", startedAt); @@ -352,7 +380,12 @@ export async function startOpenclawDevProcess(options: { await ensureDirectory(runtimeConfig.openclawStateDir); await ensureParentDirectory(runtimeConfig.openclawConfigPath); await ensureDirectory(runtimeConfig.openclawLogDir); - const openclawEntryPath = await prepareOpenclawEntryPath(); + const stageResult = await prepareOpenclawEntryPath(); + const openclawEntryPath = stageResult.entryPath; + stageDurationMs = stageResult.durationMs; + stageReused = stageResult.reused; + stagePatchedFileCount = stageResult.patchedFileCount; + stageFingerprint = stageResult.fingerprint; logOpenclawTiming("filesystem-ready", startedAt); logger.info("starting openclaw supervisor", { @@ -360,6 +393,7 @@ export async function startOpenclawDevProcess(options: { logFilePath, }); + const spawnStartedAt = Date.now(); const processHandle = await spawnHiddenProcess({ command: commandSpec.command, args: commandSpec.args, @@ -384,6 +418,7 @@ export async function startOpenclawDevProcess(options: { try { if (processHandle.child) { await waitForProcessStart(processHandle.child, "openclaw dev process"); + spawnDurationMs = Date.now() - spawnStartedAt; logOpenclawTiming("supervisor-process-start-confirmed", startedAt); } } finally { @@ -402,8 +437,10 @@ export async function startOpenclawDevProcess(options: { port: runtimeConfig.openclawPort, }); + const portWaitStartedAt = Date.now(); try { listenerPid = await waitForOpenclawPortPid(supervisorPid, logFilePath); + portReadyDurationMs = Date.now() - portWaitStartedAt; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error( @@ -417,8 +454,10 @@ export async function startOpenclawDevProcess(options: { readyUrl: `${runtimeConfig.openclawBaseUrl}/health`, }); + const healthWaitStartedAt = Date.now(); try { await waitForOpenclawReady(supervisorPid, logFilePath); + healthReadyDurationMs = Date.now() - healthWaitStartedAt; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error( @@ -434,6 +473,16 @@ export async function startOpenclawDevProcess(options: { }); logOpenclawTiming("lock-written", startedAt); + logger.info("[dev:openclaw][timing] summary", { + totalMs: Date.now() - timingStartedAt, + stageMs: stageDurationMs, + spawnMs: spawnDurationMs, + portReadyMs: portReadyDurationMs, + healthReadyMs: healthReadyDurationMs, + reused: stageReused, + patchedFileCount: stagePatchedFileCount, + fingerprint: stageFingerprint, + }); return { service: "openclaw",