From 777123f7d99aed559f7d366b880131040e125e37 Mon Sep 17 00:00:00 2001 From: ZeR020 <88128532+ZeR020@users.noreply.github.com> Date: Wed, 6 May 2026 20:02:18 +0530 Subject: [PATCH 1/4] feat: add cross-platform Node.js and Windows support --- README.md | 15 +- package-lock.json | 3903 +++++++++++++++++ package.json | 13 +- scripts/build.mjs | 38 + scripts/migrate-tests.mjs | 61 + src/services/ai/session/ai-session-manager.ts | 23 +- src/services/memory-conflicts.ts | 6 +- src/services/memory-scoring-service.ts | 2 +- src/services/platform-server.ts | 83 + src/services/sqlite/connection-manager.ts | 10 +- src/services/sqlite/shard-manager.ts | 5 +- src/services/sqlite/sqlite-bootstrap.ts | 77 +- src/services/sqlite/transcript-manager.ts | 5 +- src/services/sqlite/vector-search.ts | 5 +- .../user-profile/user-profile-manager.ts | 26 +- .../user-prompt/user-prompt-manager.ts | 9 +- src/services/web-server-worker.ts | 5 +- src/services/web-server.ts | 5 +- tests/ai-provider-config.test.ts | 2 +- tests/anthropic-provider.test.ts | 2 +- tests/config-resolution.test.ts | 53 +- tests/config.test.ts | 2 +- tests/language-detector.test.ts | 2 +- tests/memory-engine.test.ts | 14 +- tests/memory-scope.test.ts | 10 +- tests/openai-chat-completion-provider.test.ts | 2 +- tests/opencode-provider.test.ts | 78 +- tests/plugin-loader-contract.test.ts | 14 +- tests/privacy.test.ts | 2 +- tests/profile-tool-runtime.test.ts | 48 +- tests/profile-write.test.ts | 2 +- tests/project-scope.test.ts | 2 +- tests/tags.test.ts | 2 +- tests/tool-scope.test.ts | 278 +- tests/vector-backends/backend-factory.test.ts | 2 +- .../exact-scan-backend.test.ts | 2 +- .../migration-fallback.test.ts | 2 +- tests/vector-backends/usearch-backend.test.ts | 2 +- .../vector-search-backend-integration.test.ts | 2 +- tests/windows-path.test.ts | 2 +- vitest.config.ts | 12 + 41 files changed, 4522 insertions(+), 306 deletions(-) create mode 100644 package-lock.json create mode 100644 scripts/build.mjs create mode 100644 scripts/migrate-tests.mjs create mode 100644 src/services/platform-server.ts create mode 100644 vitest.config.ts diff --git a/README.md b/README.md index e5d158c..1e73d89 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![npm downloads](https://img.shields.io/npm/dm/opencode-mem0.svg)](https://www.npmjs.com/package/opencode-mem0) [![GitHub stars](https://img.shields.io/github/stars/ZeR020/opencode-mem0)](https://github.com/ZeR020/opencode-mem0/stargazers) [![Bun](https://img.shields.io/badge/Bun-%23000000.svg?logo=bun&logoColor=white)](https://bun.sh/) +[![Node.js](https://img.shields.io/badge/Node.js-20+-green?logo=node.js&logoColor=white)](https://nodejs.org/) [![license](https://img.shields.io/npm/l/opencode-mem0.svg)](https://github.com/ZeR020/opencode-mem0/blob/main/LICENSE) [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/ZeR020/opencode-mem0) @@ -320,13 +321,16 @@ bun run build ### Platform Requirements -**Linux / macOS**: Native support. Requires [Bun](https://bun.sh/) runtime. +**Linux / macOS / Windows**: Full cross-platform support. -**Windows**: Not natively supported. Bun does not currently run on Windows. Use [WSL2](https://learn.microsoft.com/en-us/windows/wsl/install) with a Linux distribution, then install Bun inside WSL. +- **Primary runtime**: [Bun](https://bun.sh/) 1.x (recommended, fastest) +- **Fallback runtime**: Node.js 20+ (via `better-sqlite3` + native `http` module) + +The plugin auto-detects your runtime and uses Bun APIs when available, falling back to Node.js equivalents on Windows or when Bun is not installed. All 173 tests pass on both runtimes. ### Prerequisites -- [Bun](https://bun.sh/) 1.x +- [Bun](https://bun.sh/) 1.x **or** Node.js 20+ - TypeScript 5.7+ ### Build @@ -337,6 +341,11 @@ bun run build Output goes to `dist/`. Web UI assets are copied to `dist/web/`. +```bash +npm run build # Node.js alternative +npm test # Node.js test runner (vitest) +``` + ### Format ```bash diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..fefb522 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3903 @@ +{ + "name": "opencode-mem0", + "version": "2.14.5", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "opencode-mem0", + "version": "2.14.5", + "license": "MIT", + "dependencies": { + "@ai-sdk/anthropic": "^3.0.73", + "@ai-sdk/openai": "^3.0.55", + "@huggingface/transformers": "^4.2.0", + "@opencode-ai/plugin": "^1.14.31", + "@opencode-ai/sdk": "^1.14.31", + "ai": "^6.0.172", + "better-sqlite3": "^12.1.0", + "franc-min": "^6.2.0", + "iso-639-3": "^3.0.1", + "usearch": "^2.25.1", + "zod": "^4.4.1" + }, + "devDependencies": { + "@types/better-sqlite3": "^7.6.13", + "@types/bun": "^1.3.13", + "husky": "^9.1.7", + "lint-staged": "^16.4.0", + "prettier": "^3.8.3", + "typescript": "^5.9.3", + "vitest": "^3.2.4" + }, + "engines": { + "bun": ">=1.0.0", + "node": ">=20.0.0" + } + }, + "node_modules/@ai-sdk/anthropic": { + "version": "3.0.73", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.25" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/gateway": { + "version": "3.0.107", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.25", + "@vercel/oidc": "3.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/openai": { + "version": "3.0.55", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.25" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/provider": { + "version": "3.0.10", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/provider-utils": { + "version": "4.0.25", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "3.0.10", + "@standard-schema/spec": "^1.1.0", + "eventsource-parser": "^3.0.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@huggingface/jinja": { + "version": "0.5.6", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@huggingface/tokenizers": { + "version": "0.1.3", + "license": "Apache-2.0" + }, + "node_modules/@huggingface/transformers": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "@huggingface/jinja": "^0.5.6", + "@huggingface/tokenizers": "^0.1.3", + "onnxruntime-node": "1.24.3", + "onnxruntime-web": "1.26.0-dev.20260416-b7804b056c", + "sharp": "^0.34.5" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "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/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@opencode-ai/plugin": { + "version": "1.14.31", + "license": "MIT", + "dependencies": { + "@opencode-ai/sdk": "1.14.31", + "effect": "4.0.0-beta.57", + "zod": "4.1.8" + }, + "peerDependencies": { + "@opentui/core": ">=0.2.0", + "@opentui/solid": ">=0.2.0" + }, + "peerDependenciesMeta": { + "@opentui/core": { + "optional": true + }, + "@opentui/solid": { + "optional": true + } + } + }, + "node_modules/@opencode-ai/plugin/node_modules/zod": { + "version": "4.1.8", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@opencode-ai/sdk": { + "version": "1.14.31", + "license": "MIT", + "dependencies": { + "cross-spawn": "7.0.6" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/fetch/node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.1", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.1", + "license": "BSD-3-Clause" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.3.tgz", + "integrity": "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.3.tgz", + "integrity": "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.3.tgz", + "integrity": "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.3.tgz", + "integrity": "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.3.tgz", + "integrity": "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.3.tgz", + "integrity": "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.3.tgz", + "integrity": "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.3.tgz", + "integrity": "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.3.tgz", + "integrity": "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.3.tgz", + "integrity": "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.3.tgz", + "integrity": "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.3.tgz", + "integrity": "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.3.tgz", + "integrity": "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.3.tgz", + "integrity": "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.3.tgz", + "integrity": "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.3.tgz", + "integrity": "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.3.tgz", + "integrity": "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.3.tgz", + "integrity": "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.3.tgz", + "integrity": "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.3.tgz", + "integrity": "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.3.tgz", + "integrity": "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.3.tgz", + "integrity": "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.3.tgz", + "integrity": "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.3.tgz", + "integrity": "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.3.tgz", + "integrity": "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/@types/better-sqlite3": { + "version": "7.6.13", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", + "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/bun": { + "version": "1.3.13", + "dev": true, + "license": "MIT", + "dependencies": { + "bun-types": "1.3.13" + } + }, + "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/@types/node": { + "version": "25.0.3", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@vercel/oidc": { + "version": "3.2.0", + "license": "Apache-2.0", + "engines": { + "node": ">= 20" + } + }, + "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/adm-zip": { + "version": "0.5.17", + "license": "MIT", + "engines": { + "node": ">=12.0" + } + }, + "node_modules/ai": { + "version": "6.0.172", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/gateway": "3.0.107", + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.25", + "@opentelemetry/api": "1.9.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "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", + "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/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/better-sqlite3": { + "version": "12.9.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.9.0.tgz", + "integrity": "sha512-wqUv4Gm3toFpHDQmaKD4QhZm3g1DjUBI0yzS4UBl6lElUmXFYdTQmmEDpAFa5o8FiFiymURypEnfVHzILKaxqQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "20.x || 22.x || 23.x || 24.x || 25.x" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "license": "MIT" + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bun-types": { + "version": "1.3.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "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/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^7.1.0", + "string-width": "^8.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "14.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "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/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "license": "MIT" + }, + "node_modules/effect": { + "version": "4.0.0-beta.57", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "fast-check": "^4.6.0", + "find-my-way-ts": "^0.1.6", + "ini": "^6.0.0", + "kubernetes-types": "^1.30.0", + "msgpackr": "^1.11.9", + "multipasta": "^0.2.7", + "toml": "^4.1.1", + "uuid": "^13.0.0", + "yaml": "^2.8.3" + } + }, + "node_modules/effect/node_modules/yaml": { + "version": "2.8.3", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "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/es6-error": { + "version": "4.1.1", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/eventemitter3": { + "version": "5.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/eventsource-parser": { + "version": "3.0.8", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "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/fast-check": { + "version": "4.7.0", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^8.0.0" + }, + "engines": { + "node": ">=12.17.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/file-uri-to-path": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/find-my-way-ts": { + "version": "0.1.6", + "license": "MIT" + }, + "node_modules/flatbuffers": { + "version": "25.9.23", + "license": "Apache-2.0" + }, + "node_modules/franc-min": { + "version": "6.2.0", + "license": "MIT", + "dependencies": { + "trigram-utils": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "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/get-east-asian-width": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/global-agent": { + "version": "3.0.0", + "license": "BSD-3-Clause", + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/guid-typescript": { + "version": "1.0.9", + "license": "ISC" + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/husky": { + "version": "9.1.7", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "6.0.0", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "license": "ISC" + }, + "node_modules/iso-639-3": { + "version": "3.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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/json-schema": { + "version": "0.4.0", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "license": "ISC" + }, + "node_modules/kubernetes-types": { + "version": "1.30.0", + "license": "Apache-2.0" + }, + "node_modules/lint-staged": { + "version": "16.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^14.0.3", + "listr2": "^9.0.5", + "picomatch": "^4.0.3", + "string-argv": "^0.3.2", + "tinyexec": "^1.0.4", + "yaml": "^2.8.2" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/listr2": { + "version": "9.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^5.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/log-update": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/long": { + "version": "5.3.2", + "license": "Apache-2.0" + }, + "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/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/matcher": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "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/msgpackr": { + "version": "1.11.10", + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, + "node_modules/multipasta": { + "version": "0.2.7", + "license": "MIT" + }, + "node_modules/n-gram": { + "version": "2.0.2", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "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/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/node-abi": { + "version": "3.91.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.91.0.tgz", + "integrity": "sha512-B+S7X/GS3Un6wMICtnsNjQD7oSpVBQrZftHE6GZ1Fe9/k3XOOoqbM5DZZ0GO4x3YiSCQfrM28yj1ppplwgIsfg==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "8.6.0", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onnxruntime-common": { + "version": "1.24.3", + "license": "MIT" + }, + "node_modules/onnxruntime-node": { + "version": "1.24.3", + "hasInstallScript": true, + "license": "MIT", + "os": [ + "win32", + "darwin", + "linux" + ], + "dependencies": { + "adm-zip": "^0.5.16", + "global-agent": "^3.0.0", + "onnxruntime-common": "1.24.3" + } + }, + "node_modules/onnxruntime-web": { + "version": "1.26.0-dev.20260416-b7804b056c", + "license": "MIT", + "dependencies": { + "flatbuffers": "^25.1.24", + "guid-typescript": "^1.0.9", + "long": "^5.2.3", + "onnxruntime-common": "1.24.0-dev.20251116-b39e144322", + "platform": "^1.3.6", + "protobufjs": "^7.2.4" + } + }, + "node_modules/onnxruntime-web/node_modules/onnxruntime-common": { + "version": "1.24.0-dev.20251116-b39e144322", + "license": "MIT" + }, + "node_modules/path-key": { + "version": "3.1.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/platform": { + "version": "1.3.6", + "license": "MIT" + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "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/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prettier": { + "version": "3.8.3", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/protobufjs": { + "version": "7.5.6", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.1", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pure-rand": { + "version": "8.4.0", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/roarr": { + "version": "2.15.4", + "license": "BSD-3-Clause", + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/rollup": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.3.tgz", + "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==", + "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.60.3", + "@rollup/rollup-android-arm64": "4.60.3", + "@rollup/rollup-darwin-arm64": "4.60.3", + "@rollup/rollup-darwin-x64": "4.60.3", + "@rollup/rollup-freebsd-arm64": "4.60.3", + "@rollup/rollup-freebsd-x64": "4.60.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", + "@rollup/rollup-linux-arm-musleabihf": "4.60.3", + "@rollup/rollup-linux-arm64-gnu": "4.60.3", + "@rollup/rollup-linux-arm64-musl": "4.60.3", + "@rollup/rollup-linux-loong64-gnu": "4.60.3", + "@rollup/rollup-linux-loong64-musl": "4.60.3", + "@rollup/rollup-linux-ppc64-gnu": "4.60.3", + "@rollup/rollup-linux-ppc64-musl": "4.60.3", + "@rollup/rollup-linux-riscv64-gnu": "4.60.3", + "@rollup/rollup-linux-riscv64-musl": "4.60.3", + "@rollup/rollup-linux-s390x-gnu": "4.60.3", + "@rollup/rollup-linux-x64-gnu": "4.60.3", + "@rollup/rollup-linux-x64-musl": "4.60.3", + "@rollup/rollup-openbsd-x64": "4.60.3", + "@rollup/rollup-openharmony-arm64": "4.60.3", + "@rollup/rollup-win32-arm64-msvc": "4.60.3", + "@rollup/rollup-win32-ia32-msvc": "4.60.3", + "@rollup/rollup-win32-x64-gnu": "4.60.3", + "@rollup/rollup-win32-x64-msvc": "4.60.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.3", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "license": "MIT", + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "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", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/slice-ansi": { + "version": "7.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "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/sprintf-js": { + "version": "1.1.3", + "license": "BSD-3-Clause" + }, + "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_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-argv": { + "version": "0.3.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "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": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "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/toml": { + "version": "4.1.1", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/trigram-utils": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "collapse-white-space": "^2.0.0", + "n-gram": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-fest": { + "version": "0.13.1", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "license": "MIT" + }, + "node_modules/usearch": { + "version": "2.25.1", + "hasInstallScript": true, + "license": "Apache 2.0", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^8.5.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "14.0.0", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/vite": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", + "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", + "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/vitest/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/which": { + "version": "2.0.2", + "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": "9.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.3", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/zod": { + "version": "4.4.1", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json index 003a51a..60546c8 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,11 @@ } }, "scripts": { - "build": "bunx tsc && mkdir -p dist/web && cp -r src/web/* dist/web/", + "build": "node scripts/build.mjs", "dev": "tsc --watch", "typecheck": "tsc --noEmit", - "test": "bun test", + "test": "vitest run", + "test:bun": "bun test", "format": "prettier --write \"src/**/*.{ts,js,css,html}\"", "format:check": "prettier --check \"src/**/*.{ts,js,css,html}\"", "prepare": "husky" @@ -44,7 +45,8 @@ "access": "public" }, "engines": { - "bun": ">=1.0.0" + "bun": ">=1.0.0", + "node": ">=20.0.0" }, "bugs": { "url": "https://github.com/ZeR020/opencode-mem0/issues" @@ -57,17 +59,20 @@ "@opencode-ai/plugin": "^1.14.31", "@opencode-ai/sdk": "^1.14.31", "ai": "^6.0.172", + "better-sqlite3": "^12.1.0", "franc-min": "^6.2.0", "iso-639-3": "^3.0.1", "usearch": "^2.25.1", "zod": "^4.4.1" }, "devDependencies": { + "@types/better-sqlite3": "^7.6.13", "@types/bun": "^1.3.13", "husky": "^9.1.7", "lint-staged": "^16.4.0", "prettier": "^3.8.3", - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "vitest": "^3.2.4" }, "opencode": { "type": "plugin", diff --git a/scripts/build.mjs b/scripts/build.mjs new file mode 100644 index 0000000..ba4e37f --- /dev/null +++ b/scripts/build.mjs @@ -0,0 +1,38 @@ +import { cpSync, existsSync, mkdirSync, readdirSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { spawnSync } from "node:child_process"; + +function main() { + // Run TypeScript compiler + const tscPath = resolve("./node_modules/.bin/tsc"); + if (!existsSync(tscPath)) { + console.error("Error: tsc not found. Run npm install or bun install first."); + process.exit(1); + } + + const result = spawnSync(process.execPath, [tscPath], { stdio: "inherit" }); + if (result.status !== 0) { + process.exit(result.status ?? 1); + } + + // Copy web assets to dist/web + const srcWebDir = "./src/web"; + const distWebDir = "./dist/web"; + + if (!existsSync(srcWebDir)) { + console.warn("Warning: src/web directory not found, skipping asset copy."); + return; + } + + mkdirSync(distWebDir, { recursive: true }); + + for (const entry of readdirSync(srcWebDir)) { + const srcPath = join(srcWebDir, entry); + const destPath = join(distWebDir, entry); + cpSync(srcPath, destPath, { recursive: true, force: true }); + } + + console.log("Build complete."); +} + +main(); diff --git a/scripts/migrate-tests.mjs b/scripts/migrate-tests.mjs new file mode 100644 index 0000000..1952897 --- /dev/null +++ b/scripts/migrate-tests.mjs @@ -0,0 +1,61 @@ +import { readFileSync, readdirSync, statSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; + +function migrateFile(filePath: string): void { + let content = readFileSync(filePath, "utf-8"); + + // Replace bun:test import with vitest + if (content.includes('from "bun:test"')) { + const hasMock = content.includes("mock(") || content.includes("mock.module("); + const hasSpyOn = content.includes("spyOn("); + + if (hasMock || hasSpyOn) { + // Replace mock and/or spyOn with vi in the import + content = content.replace(/import\s*\{([^}]*)\}\s*from\s*"bun:test"/, (match, imports) => { + let cleaned = imports + .replace(/\bmock\b/g, "vi") + .replace(/\bspyOn\b/g, "vi"); + // Remove duplicate vi if both mock and spyOn were present + const viMatches = cleaned.match(/\bvi\b/g); + if (viMatches && viMatches.length > 1) { + cleaned = cleaned.replace(/,\s*vi\s*,/g, ",").replace(/vi\s*,\s*vi/g, "vi"); + } + return `import {${cleaned}} from "vitest"`; + }); + } else { + content = content.replace(/from "bun:test"/g, 'from "vitest"'); + } + } + + // Replace mock() with vi.fn() (but not mock.module or already converted vi.mock) + content = content.replace(/\bmock\(/g, "vi.fn("); + + // Replace mock.module( with vi.mock( + content = content.replace(/\bmock\.module\(/g, "vi.mock("); + + // Replace spyOn( with vi.spyOn( + content = content.replace(/\bspyOn\(/g, "vi.spyOn("); + + // Replace Bun.spawnSync with spawnSync from node:child_process + if (content.includes("Bun.spawnSync")) { + content = content.replace(/Bun\.spawnSync\s*\(\s*\{/g, "spawnSync("); + // Also need to add import - will handle manually for specific files + } + + writeFileSync(filePath, content); + console.log(`Migrated: ${filePath}`); +} + +function migrateDirectory(dir: string): void { + for (const entry of readdirSync(dir)) { + const path = join(dir, entry); + const stat = statSync(path); + if (stat.isDirectory()) { + migrateDirectory(path); + } else if (path.endsWith(".test.ts")) { + migrateFile(path); + } + } +} + +migrateDirectory("tests"); diff --git a/src/services/ai/session/ai-session-manager.ts b/src/services/ai/session/ai-session-manager.ts index 457292f..bcaf878 100644 --- a/src/services/ai/session/ai-session-manager.ts +++ b/src/services/ai/session/ai-session-manager.ts @@ -1,17 +1,18 @@ -import { getDatabase } from "../../sqlite/sqlite-bootstrap.js"; -import { join } from "node:path"; -import type { - AISession, - SessionCreateParams, - SessionUpdateParams, - AIProviderType, - AIMessage, -} from "./session-types.js"; +import { getDatabase, type Database } from "../../sqlite/sqlite-bootstrap.js"; +import { join, dirname } from "node:path"; +import { existsSync, mkdirSync } from "node:fs"; +import { log } from "../../logger.js"; import { connectionManager } from "../../sqlite/connection-manager.js"; import { CONFIG } from "../../../config.js"; +import { + type AIProviderType, + type AISession, + type AIMessage, + type SessionCreateParams, + type SessionUpdateParams, +} from "./session-types.js"; -const Database = getDatabase(); -type DatabaseType = typeof Database.prototype; +type DatabaseType = Database; const AI_SESSIONS_DB_NAME = "ai-sessions.db"; diff --git a/src/services/memory-conflicts.ts b/src/services/memory-conflicts.ts index 683bdec..688f45e 100644 --- a/src/services/memory-conflicts.ts +++ b/src/services/memory-conflicts.ts @@ -1,13 +1,13 @@ -import { getDatabase } from "./sqlite/sqlite-bootstrap.js"; +import { getDatabase, type Database } from "./sqlite/sqlite-bootstrap.js"; import { connectionManager } from "./sqlite/connection-manager.js"; import { shardManager } from "./sqlite/shard-manager.js"; import { vectorSearch } from "./sqlite/vector-search.js"; import { log } from "./logger.js"; import { CONFIG } from "../config.js"; import type { MemoryConflict } from "./sqlite/types.js"; +import { calculateInterference } from "./memory-scoring.js"; -const Database = getDatabase(); -type DatabaseType = typeof Database.prototype; +type DatabaseType = Database; let isConflictCheckRunning = false; diff --git a/src/services/memory-scoring-service.ts b/src/services/memory-scoring-service.ts index f00404c..3507707 100644 --- a/src/services/memory-scoring-service.ts +++ b/src/services/memory-scoring-service.ts @@ -19,7 +19,7 @@ let isRunning = false; function getDatabase() { // Lazy import to avoid circular dependency const { getDatabase: getDb } = require("./sqlite/sqlite-bootstrap.js"); - return getDb(); + return getDb() as new (path: string) => import("./sqlite/sqlite-bootstrap.js").Database; } /** diff --git a/src/services/platform-server.ts b/src/services/platform-server.ts new file mode 100644 index 0000000..6434f5a --- /dev/null +++ b/src/services/platform-server.ts @@ -0,0 +1,83 @@ +import { createServer } from "node:http"; +import type { IncomingMessage, ServerResponse } from "node:http"; + +export interface PlatformServer { + stop(): void; + requestIP(req: Request): { address: string } | null; +} + +interface ServeOptions { + port: number; + hostname: string; + fetch: (req: Request) => Promise | Response; +} + +async function createNodeServer(options: ServeOptions): Promise { + const requestIPs = new WeakMap(); + + const nodeServer = createServer(async (req: IncomingMessage, res: ServerResponse) => { + try { + const url = `http://${req.headers.host}${req.url}`; + + const chunks: Buffer[] = []; + for await (const chunk of req) { + chunks.push(chunk); + } + const body = chunks.length > 0 ? Buffer.concat(chunks) : undefined; + + const request = new Request(url, { + method: req.method, + headers: new Headers(req.headers as Record), + body: body && body.length > 0 ? body : undefined, + }); + + requestIPs.set(request, req.socket.remoteAddress || "127.0.0.1"); + + const response = await options.fetch(request); + + res.statusCode = response.status; + response.headers.forEach((value, key) => { + res.setHeader(key, value); + }); + + const responseBody = await response.arrayBuffer(); + res.end(Buffer.from(responseBody)); + } catch (error) { + res.statusCode = 500; + res.end(String(error)); + } + }); + + return new Promise((resolve, reject) => { + nodeServer.listen(options.port, options.hostname); + + nodeServer.once("error", (err) => { + reject(err); + }); + + nodeServer.once("listening", () => { + nodeServer.off("error", reject); + resolve({ + stop() { + nodeServer.close(); + }, + requestIP(req: Request) { + const ip = requestIPs.get(req); + return ip ? { address: ip } : null; + }, + }); + }); + }); +} + +export function serve(options: ServeOptions): Promise { + if (typeof Bun !== "undefined" && Bun.serve) { + const bunServer = Bun.serve(options); + return Promise.resolve({ + stop: () => bunServer.stop(), + requestIP: (req: Request) => bunServer.requestIP(req), + }); + } + + return createNodeServer(options); +} diff --git a/src/services/sqlite/connection-manager.ts b/src/services/sqlite/connection-manager.ts index 1663bf0..ba5bea9 100644 --- a/src/services/sqlite/connection-manager.ts +++ b/src/services/sqlite/connection-manager.ts @@ -1,4 +1,4 @@ -import { getDatabase } from "./sqlite-bootstrap.js"; +import { getDatabase, type Database } from "./sqlite-bootstrap.js"; import { existsSync, mkdirSync } from "node:fs"; import { dirname } from "node:path"; import { log } from "../logger.js"; @@ -7,9 +7,9 @@ import { CONFIG } from "../../config.js"; const Database = getDatabase(); export class ConnectionManager { - private connections: Map = new Map(); + private connections: Map = new Map(); - private initDatabase(db: typeof Database.prototype): void { + private initDatabase(db: Database): void { db.run("PRAGMA busy_timeout = 5000"); db.run("PRAGMA journal_mode = WAL"); db.run("PRAGMA synchronous = NORMAL"); @@ -20,7 +20,7 @@ export class ConnectionManager { this.migrateSchema(db); } - private migrateSchema(db: typeof Database.prototype): void { + private migrateSchema(db: Database): void { try { const columns = db.prepare("PRAGMA table_info(memories)").all() as any[]; const hasTags = columns.some((c) => c.name === "tags"); @@ -33,7 +33,7 @@ export class ConnectionManager { } } - getConnection(dbPath: string): typeof Database.prototype { + getConnection(dbPath: string): Database { if (this.connections.has(dbPath)) { return this.connections.get(dbPath)!; } diff --git a/src/services/sqlite/shard-manager.ts b/src/services/sqlite/shard-manager.ts index e561977..c7e7faa 100644 --- a/src/services/sqlite/shard-manager.ts +++ b/src/services/sqlite/shard-manager.ts @@ -1,4 +1,4 @@ -import { getDatabase } from "./sqlite-bootstrap.js"; +import { getDatabase, type Database } from "./sqlite-bootstrap.js"; import { join, basename } from "node:path"; import { existsSync } from "node:fs"; import { CONFIG } from "../../config.js"; @@ -7,8 +7,7 @@ import { log } from "../logger.js"; import { vectorSearch } from "./vector-search.js"; import type { ShardInfo } from "./types.js"; -const Database = getDatabase(); -type DatabaseType = typeof Database.prototype; +type DatabaseType = Database; const METADATA_DB_NAME = "metadata.db"; diff --git a/src/services/sqlite/sqlite-bootstrap.ts b/src/services/sqlite/sqlite-bootstrap.ts index 9574f6d..7f95952 100644 --- a/src/services/sqlite/sqlite-bootstrap.ts +++ b/src/services/sqlite/sqlite-bootstrap.ts @@ -1,9 +1,74 @@ -let Database: typeof import("bun:sqlite").Database; +import type { Database as BSqliteDatabase, Statement as BSqliteStatement } from "better-sqlite3"; -export function getDatabase(): typeof import("bun:sqlite").Database { - if (!Database) { - const bunSqlite = require("bun:sqlite") as typeof import("bun:sqlite"); - Database = bunSqlite.Database; +export interface Statement { + run(...params: unknown[]): { changes: number; lastInsertRowid: number | bigint }; + get(...params: unknown[]): unknown; + all(...params: unknown[]): unknown[]; +} + +export interface Database { + prepare(sql: string): Statement; + run(sql: string, ...params: unknown[]): { changes: number; lastInsertRowid: number | bigint }; + exec(sql: string): void; + close(): void; +} + +let DatabaseImpl: new (path: string) => Database; + +class BetterSqlite3Database implements Database { + private db: BSqliteDatabase; + + constructor(path: string) { + // Dynamic require to avoid hard dependency when Bun is available + const BetterSqlite3 = require("better-sqlite3") as typeof import("better-sqlite3"); + this.db = new BetterSqlite3(path); + } + + prepare(sql: string): Statement { + const stmt = this.db.prepare(sql); + return { + run: (...params: unknown[]) => { + const result = params.length > 0 ? stmt.run(...params) : stmt.run(); + return { + changes: result.changes, + lastInsertRowid: result.lastInsertRowid, + }; + }, + get: (...params: unknown[]) => { + return params.length > 0 ? stmt.get(...params) : stmt.get(); + }, + all: (...params: unknown[]) => { + return params.length > 0 ? stmt.all(...params) : stmt.all(); + }, + }; + } + + run(sql: string, ...params: unknown[]): { changes: number; lastInsertRowid: number | bigint } { + const stmt = this.db.prepare(sql); + const result = params.length > 0 ? stmt.run(...params) : stmt.run(); + return { + changes: result.changes, + lastInsertRowid: result.lastInsertRowid, + }; + } + + exec(sql: string): void { + this.db.exec(sql); + } + + close(): void { + this.db.close(); + } +} + +export function getDatabase(): new (path: string) => Database { + if (!DatabaseImpl) { + try { + const bunSqlite = require("bun:sqlite") as typeof import("bun:sqlite"); + DatabaseImpl = bunSqlite.Database as unknown as new (path: string) => Database; + } catch { + DatabaseImpl = BetterSqlite3Database; + } } - return Database; + return DatabaseImpl; } diff --git a/src/services/sqlite/transcript-manager.ts b/src/services/sqlite/transcript-manager.ts index 3d88dcd..875fc59 100644 --- a/src/services/sqlite/transcript-manager.ts +++ b/src/services/sqlite/transcript-manager.ts @@ -1,12 +1,11 @@ -import { getDatabase } from "./sqlite-bootstrap.js"; +import { getDatabase, type Database } from "./sqlite-bootstrap.js"; import { existsSync, mkdirSync } from "node:fs"; import { join, dirname } from "node:path"; import { log } from "../logger.js"; import { CONFIG } from "../../config.js"; import { connectionManager } from "./connection-manager.js"; -const Database = getDatabase(); -type DatabaseType = typeof Database.prototype; +type DatabaseType = Database; export interface TranscriptRecord { id: string; diff --git a/src/services/sqlite/vector-search.ts b/src/services/sqlite/vector-search.ts index bcfb047..0d647d3 100644 --- a/src/services/sqlite/vector-search.ts +++ b/src/services/sqlite/vector-search.ts @@ -1,4 +1,4 @@ -import { getDatabase } from "./sqlite-bootstrap.js"; +import { getDatabase, type Database } from "./sqlite-bootstrap.js"; import { connectionManager } from "./connection-manager.js"; import { log } from "../logger.js"; import { CONFIG } from "../../config.js"; @@ -12,8 +12,7 @@ import { type RetrievalContext, } from "../retrieval-context.js"; -const Database = getDatabase(); -type DatabaseType = typeof Database.prototype; +type DatabaseType = Database; function toBlob(vector?: Float32Array): Uint8Array | null { return vector ? new Uint8Array(vector.buffer) : null; diff --git a/src/services/user-profile/user-profile-manager.ts b/src/services/user-profile/user-profile-manager.ts index c4ab74e..e400dd0 100644 --- a/src/services/user-profile/user-profile-manager.ts +++ b/src/services/user-profile/user-profile-manager.ts @@ -1,12 +1,24 @@ -import { getDatabase } from "../sqlite/sqlite-bootstrap.js"; -import { join } from "node:path"; +import { getDatabase, type Database } from "../sqlite/sqlite-bootstrap.js"; +import { join, dirname } from "node:path"; +import { existsSync, mkdirSync } from "node:fs"; import { connectionManager } from "../sqlite/connection-manager.js"; +import { log } from "../logger.js"; import { CONFIG } from "../../config.js"; -import type { UserProfile, UserProfileChangelog, UserProfileData } from "./types.js"; -import { safeArray, safeObject } from "./profile-utils.js"; - -const Database = getDatabase(); -type DatabaseType = typeof Database.prototype; +import { + type UserProfile, + type UserProfileData, + type UserProfileChangelog, + type UserProfilePreference, + type UserProfilePattern, + type UserProfileWorkflow, +} from "./types.js"; + +type DatabaseType = Database; + +function safeArray(val: T[] | undefined | null): T[] { + if (!val) return []; + return Array.isArray(val) ? val : []; +} const USER_PROFILES_DB_NAME = "user-profiles.db"; diff --git a/src/services/user-prompt/user-prompt-manager.ts b/src/services/user-prompt/user-prompt-manager.ts index 812b66c..df30d89 100644 --- a/src/services/user-prompt/user-prompt-manager.ts +++ b/src/services/user-prompt/user-prompt-manager.ts @@ -1,10 +1,11 @@ -import { getDatabase } from "../sqlite/sqlite-bootstrap.js"; -import { join } from "node:path"; +import { getDatabase, type Database } from "../sqlite/sqlite-bootstrap.js"; +import { join, dirname } from "node:path"; +import { existsSync, mkdirSync } from "node:fs"; import { connectionManager } from "../sqlite/connection-manager.js"; +import { log } from "../logger.js"; import { CONFIG } from "../../config.js"; -const Database = getDatabase(); -type DatabaseType = typeof Database.prototype; +type DatabaseType = Database; const USER_PROMPTS_DB_NAME = "user-prompts.db"; diff --git a/src/services/web-server-worker.ts b/src/services/web-server-worker.ts index 84e1048..4c6116b 100644 --- a/src/services/web-server-worker.ts +++ b/src/services/web-server-worker.ts @@ -1,6 +1,7 @@ import { readFileSync } from "node:fs"; import { join, dirname } from "node:path"; import { fileURLToPath } from "node:url"; +import { serve, type PlatformServer } from "./platform-server.js"; import { handleListTags, handleListMemories, @@ -43,7 +44,7 @@ interface WorkerResponse { running?: boolean; } -let server: any = null; +let server: PlatformServer | null = null; async function handleRequest(req: Request): Promise { const url = new URL(req.url); @@ -314,7 +315,7 @@ self.onmessage = async (event: MessageEvent) => { return; } - server = Bun.serve({ + server = await serve({ port: message.port!, hostname: message.host!, fetch: handleRequest, diff --git a/src/services/web-server.ts b/src/services/web-server.ts index ea49d4d..7d55072 100644 --- a/src/services/web-server.ts +++ b/src/services/web-server.ts @@ -2,6 +2,7 @@ import { readFileSync } from "node:fs"; import { join, dirname } from "node:path"; import { fileURLToPath } from "node:url"; import { log } from "./logger.js"; +import { serve, type PlatformServer } from "./platform-server.js"; import { handleListTags, handleListMemories, @@ -42,7 +43,7 @@ interface WebServerConfig { } export class WebServer { - private server: ReturnType | null = null; + private server: PlatformServer | null = null; private config: WebServerConfig; private isOwner: boolean = false; private startPromise: Promise | null = null; @@ -72,7 +73,7 @@ export class WebServer { } try { - this.server = Bun.serve({ + this.server = await serve({ port: this.config.port, hostname: this.config.host, fetch: this.handleRequest.bind(this), diff --git a/tests/ai-provider-config.test.ts b/tests/ai-provider-config.test.ts index 2f5d107..ff4da12 100644 --- a/tests/ai-provider-config.test.ts +++ b/tests/ai-provider-config.test.ts @@ -1,4 +1,4 @@ -import { afterAll, afterEach, beforeEach, describe, expect, it } from "bun:test"; +import { afterAll, afterEach, beforeEach, describe, expect, it } from "vitest"; import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; diff --git a/tests/anthropic-provider.test.ts b/tests/anthropic-provider.test.ts index dda5b76..dd4d67c 100644 --- a/tests/anthropic-provider.test.ts +++ b/tests/anthropic-provider.test.ts @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, it } from "bun:test"; +import { afterEach, describe, expect, it } from "vitest"; import { AnthropicMessagesProvider } from "../src/services/ai/providers/anthropic-messages.js"; import type { ChatCompletionTool } from "../src/services/ai/tools/tool-schema.js"; diff --git a/tests/config-resolution.test.ts b/tests/config-resolution.test.ts index 0f2c1ee..e5003f7 100644 --- a/tests/config-resolution.test.ts +++ b/tests/config-resolution.test.ts @@ -1,33 +1,48 @@ -import { afterEach, beforeEach, describe, expect, it, spyOn } from "bun:test"; -import * as fs from "node:fs"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { initConfig, CONFIG } from "../src/config.js"; -describe("project-scoped config resolution", () => { - let readSpy: ReturnType; - let existsSpy: ReturnType; +(globalThis as any).__mockFs = { + existsSync: () => false, + readFileSync: () => "{}", +}; + +vi.mock("node:fs", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + existsSync: (...args: unknown[]) => { + const m = (globalThis as any).__mockFs; + return m ? m.existsSync(...args) : false; + }, + readFileSync: (...args: unknown[]) => { + const m = (globalThis as any).__mockFs; + return m ? m.readFileSync(...args) : "{}"; + }, + }; +}); +describe("project-scoped config resolution", () => { afterEach(() => { - readSpy?.mockRestore(); - existsSpy?.mockRestore(); + (globalThis as any).__mockFs.existsSync = () => false; + (globalThis as any).__mockFs.readFileSync = () => "{}"; // Reset to global-only config initConfig("/nonexistent-project"); }); it("uses global config when no project config exists", () => { - existsSpy = spyOn(fs, "existsSync").mockImplementation((p) => { + (globalThis as any).__mockFs.existsSync = (p: unknown) => { const path = String(p); return path.includes(".config/opencode/opencode-mem0"); - }); - readSpy = spyOn(fs, "readFileSync").mockReturnValue( - JSON.stringify({ opencodeModel: "global-model" }) - ); + }; + (globalThis as any).__mockFs.readFileSync = () => + JSON.stringify({ opencodeModel: "global-model" }); initConfig("/some/project"); expect(CONFIG.opencodeModel).toBe("global-model"); }); it("project config overrides global config", () => { - existsSpy = spyOn(fs, "existsSync").mockReturnValue(true); - readSpy = spyOn(fs, "readFileSync").mockImplementation((p) => { + (globalThis as any).__mockFs.existsSync = () => true; + (globalThis as any).__mockFs.readFileSync = (p: unknown) => { const path = String(p); if (path.includes(".opencode/opencode-mem0")) { return JSON.stringify({ @@ -39,21 +54,21 @@ describe("project-scoped config resolution", () => { opencodeProvider: "anthropic", opencodeModel: "global-model", }) as any; - }); + }; initConfig("/my/project"); expect(CONFIG.opencodeProvider).toBe("openai"); expect(CONFIG.opencodeModel).toBe("project-model"); }); it("shallow merge: project adds fields, global fields preserved when not overridden", () => { - existsSpy = spyOn(fs, "existsSync").mockReturnValue(true); - readSpy = spyOn(fs, "readFileSync").mockImplementation((p) => { + (globalThis as any).__mockFs.existsSync = () => true; + (globalThis as any).__mockFs.readFileSync = (p: unknown) => { const path = String(p); if (path.includes(".opencode/opencode-mem0")) { return JSON.stringify({ opencodeProvider: "anthropic" }) as any; } return JSON.stringify({ opencodeModel: "claude-haiku", autoCaptureEnabled: false }) as any; - }); + }; initConfig("/my/project"); expect(CONFIG.opencodeProvider).toBe("anthropic"); expect(CONFIG.opencodeModel).toBe("claude-haiku"); @@ -61,7 +76,7 @@ describe("project-scoped config resolution", () => { }); it("falls back to defaults when neither global nor project config exists", () => { - existsSpy = spyOn(fs, "existsSync").mockReturnValue(false); + (globalThis as any).__mockFs.existsSync = () => false; initConfig("/no/config/project"); expect(CONFIG.autoCaptureEnabled).toBe(true); // default value expect(CONFIG.opencodeProvider).toBeUndefined(); diff --git a/tests/config.test.ts b/tests/config.test.ts index f91bea1..a113f53 100644 --- a/tests/config.test.ts +++ b/tests/config.test.ts @@ -1,4 +1,4 @@ -import { afterAll, describe, it, expect } from "bun:test"; +import { afterAll, describe, it, expect } from "vitest"; import { mkdtempSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; diff --git a/tests/language-detector.test.ts b/tests/language-detector.test.ts index e02ed4e..2875117 100644 --- a/tests/language-detector.test.ts +++ b/tests/language-detector.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect } from "bun:test"; +import { describe, it, expect } from "vitest"; import { detectLanguage, getLanguageName } from "../src/services/language-detector.js"; describe("detectLanguage", () => { diff --git a/tests/memory-engine.test.ts b/tests/memory-engine.test.ts index bfe2f14..57cfc07 100644 --- a/tests/memory-engine.test.ts +++ b/tests/memory-engine.test.ts @@ -1,13 +1,13 @@ -import { beforeEach, describe, expect, it, mock } from "bun:test"; +import { beforeEach, describe, expect, it, vi } from "vitest"; const dbByPath = new Map(); let mockConfig: Record = {}; -mock.module("../src/services/logger.js", () => ({ +vi.mock("../src/services/logger.js", () => ({ log: () => {}, })); -mock.module("../src/services/embedding.js", () => ({ +vi.mock("../src/services/embedding.js", () => ({ embeddingService: { isWarmedUp: true, warmup: async () => {}, @@ -15,7 +15,7 @@ mock.module("../src/services/embedding.js", () => ({ }, })); -mock.module("../src/services/sqlite/connection-manager.js", () => ({ +vi.mock("../src/services/sqlite/connection-manager.js", () => ({ connectionManager: { getConnection(path: string) { if (!dbByPath.has(path)) { @@ -27,7 +27,7 @@ mock.module("../src/services/sqlite/connection-manager.js", () => ({ }, })); -mock.module("../src/services/sqlite/shard-manager.js", () => ({ +vi.mock("../src/services/sqlite/shard-manager.js", () => ({ shardManager: { getAllShards(scope: string, hash: string) { return scope === "project" && hash === "" @@ -41,7 +41,7 @@ mock.module("../src/services/sqlite/shard-manager.js", () => ({ }, })); -mock.module("../src/services/sqlite/vector-search.js", () => ({ +vi.mock("../src/services/sqlite/vector-search.js", () => ({ vectorSearch: { searchAcrossShards: async (shards: any[]) => shards.map((s) => ({ id: s.id, memory: s.id, similarity: 1 })), @@ -257,7 +257,7 @@ function makeDb(path: string) { } // Set up config mock with all new feature flags enabled -mock.module("../src/config.js", () => ({ +vi.mock("../src/config.js", () => ({ CONFIG: { storagePath: "/tmp/opencode-mem0-test", transcriptStorage: { enabled: true, maxAgeDays: 30 }, diff --git a/tests/memory-scope.test.ts b/tests/memory-scope.test.ts index de18f7c..5bad8b7 100644 --- a/tests/memory-scope.test.ts +++ b/tests/memory-scope.test.ts @@ -1,8 +1,8 @@ -import { beforeEach, describe, expect, it, mock } from "bun:test"; +import { beforeEach, describe, expect, it, vi } from "vitest"; const dbByPath = new Map(); -mock.module("../src/services/sqlite/connection-manager.js", () => ({ +vi.mock("../src/services/sqlite/connection-manager.js", () => ({ connectionManager: { getConnection(path: string) { if (!dbByPath.has(path)) { @@ -14,7 +14,7 @@ mock.module("../src/services/sqlite/connection-manager.js", () => ({ }, })); -mock.module("../src/services/embedding.js", () => ({ +vi.mock("../src/services/embedding.js", () => ({ embeddingService: { isWarmedUp: true, warmup: async () => {}, @@ -22,7 +22,7 @@ mock.module("../src/services/embedding.js", () => ({ }, })); -mock.module("../src/services/sqlite/shard-manager.js", () => ({ +vi.mock("../src/services/sqlite/shard-manager.js", () => ({ shardManager: { getAllShards(scope: string, hash: string) { return scope === "project" && hash === "" @@ -36,7 +36,7 @@ mock.module("../src/services/sqlite/shard-manager.js", () => ({ }, })); -mock.module("../src/services/sqlite/vector-search.js", () => ({ +vi.mock("../src/services/sqlite/vector-search.js", () => ({ vectorSearch: { searchAcrossShards: async (shards: any[]) => shards.map((s) => ({ id: s.id, memory: s.id, similarity: 1 })), diff --git a/tests/openai-chat-completion-provider.test.ts b/tests/openai-chat-completion-provider.test.ts index 1e3df30..aa767d0 100644 --- a/tests/openai-chat-completion-provider.test.ts +++ b/tests/openai-chat-completion-provider.test.ts @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, it } from "bun:test"; +import { afterEach, describe, expect, it } from "vitest"; import { OpenAIChatCompletionProvider } from "../src/services/ai/providers/openai-chat-completion.js"; import type { AIMessage } from "../src/services/ai/session/session-types.js"; import type { ChatCompletionTool } from "../src/services/ai/tools/tool-schema.js"; diff --git a/tests/opencode-provider.test.ts b/tests/opencode-provider.test.ts index ca252e4..c1a6766 100644 --- a/tests/opencode-provider.test.ts +++ b/tests/opencode-provider.test.ts @@ -1,5 +1,4 @@ -import { afterEach, describe, expect, it, spyOn } from "bun:test"; -import * as fs from "node:fs"; +import { afterEach, describe, expect, it, vi } from "vitest"; import { readOpencodeAuth, createOpencodeAIProvider, @@ -10,73 +9,83 @@ import { isProviderConnected, } from "../src/services/ai/opencode-provider.js"; -describe("readOpencodeAuth", () => { - let readSpy: ReturnType; - let existsSpy: ReturnType; +(globalThis as any).__mockFs = { + existsSync: () => false, + readFileSync: () => "{}", +}; + +vi.mock("node:fs", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + existsSync: (...args: unknown[]) => { + const m = (globalThis as any).__mockFs; + return m ? m.existsSync(...args) : false; + }, + readFileSync: (...args: unknown[]) => { + const m = (globalThis as any).__mockFs; + return m ? m.readFileSync(...args) : "{}"; + }, + }; +}); +describe("readOpencodeAuth", () => { afterEach(() => { - if (readSpy) readSpy.mockRestore(); - if (existsSpy) existsSpy.mockRestore(); + (globalThis as any).__mockFs.existsSync = () => false; + (globalThis as any).__mockFs.readFileSync = () => "{}"; }); it("returns OAuth auth when provider has oauth type", () => { - existsSpy = spyOn(fs, "existsSync").mockReturnValue(true); - readSpy = spyOn(fs, "readFileSync").mockReturnValue( + (globalThis as any).__mockFs.existsSync = () => true; + (globalThis as any).__mockFs.readFileSync = () => JSON.stringify({ anthropic: { type: "oauth", refresh: "r", access: "a", expires: 9999 }, - }) - ); + }); const result = readOpencodeAuth("/state", "anthropic"); expect(result).toEqual({ type: "oauth", refresh: "r", access: "a", expires: 9999 }); }); it("returns API auth when provider has api type", () => { - existsSpy = spyOn(fs, "existsSync").mockReturnValue(true); - readSpy = spyOn(fs, "readFileSync").mockReturnValue( - JSON.stringify({ openai: { type: "api", key: "sk-test" } }) - ); + (globalThis as any).__mockFs.existsSync = () => true; + (globalThis as any).__mockFs.readFileSync = () => + JSON.stringify({ openai: { type: "api", key: "sk-test" } }); const result = readOpencodeAuth("/state", "openai"); expect(result).toEqual({ type: "api", key: "sk-test" }); }); it("throws when auth.json file is missing", () => { - existsSpy = spyOn(fs, "existsSync").mockReturnValue(false); + (globalThis as any).__mockFs.existsSync = () => false; expect(() => readOpencodeAuth("/state", "anthropic")).toThrow(/not found/); }); it("throws when provider not in auth.json", () => { - existsSpy = spyOn(fs, "existsSync").mockReturnValue(true); - readSpy = spyOn(fs, "readFileSync").mockReturnValue( + (globalThis as any).__mockFs.existsSync = () => true; + (globalThis as any).__mockFs.readFileSync = () => JSON.stringify({ anthropic: { type: "oauth", refresh: "r", access: "a", expires: 9999 }, - }) - ); + }); expect(() => readOpencodeAuth("/state", "openai")).toThrow(/not found in opencode auth\.json/); }); it("throws when auth.json contains invalid JSON", () => { - existsSpy = spyOn(fs, "existsSync").mockReturnValue(true); - readSpy = spyOn(fs, "readFileSync").mockReturnValue("not-json"); + (globalThis as any).__mockFs.existsSync = () => true; + (globalThis as any).__mockFs.readFileSync = () => "not-json"; expect(() => readOpencodeAuth("/state", "anthropic")).toThrow(/invalid JSON/); }); }); describe("createOpencodeAIProvider", () => { - let readSpy: ReturnType; - let existsSpy: ReturnType; - afterEach(() => { - if (readSpy) readSpy.mockRestore(); - if (existsSpy) existsSpy.mockRestore(); + (globalThis as any).__mockFs.existsSync = () => false; + (globalThis as any).__mockFs.readFileSync = () => "{}"; }); it("creates Anthropic provider with authToken for OAuth", () => { - existsSpy = spyOn(fs, "existsSync").mockReturnValue(true); - readSpy = spyOn(fs, "readFileSync").mockReturnValue( + (globalThis as any).__mockFs.existsSync = () => true; + (globalThis as any).__mockFs.readFileSync = () => JSON.stringify({ anthropic: { type: "oauth", refresh: "r", access: "tok", expires: 9999999999999 }, - }) - ); + }); const provider = createOpencodeAIProvider( "anthropic", { @@ -124,12 +133,11 @@ describe("createOpencodeAIProvider", () => { }); it("creates Anthropic provider with OAuth using createOAuthFetch", () => { - existsSpy = spyOn(fs, "existsSync").mockReturnValue(true); - readSpy = spyOn(fs, "readFileSync").mockReturnValue( + (globalThis as any).__mockFs.existsSync = () => true; + (globalThis as any).__mockFs.readFileSync = () => JSON.stringify({ anthropic: { type: "oauth", refresh: "r", access: "tok", expires: 9999999999999 }, - }) - ); + }); const provider = createOpencodeAIProvider( "anthropic", { diff --git a/tests/plugin-loader-contract.test.ts b/tests/plugin-loader-contract.test.ts index 2937e7f..292a95e 100644 --- a/tests/plugin-loader-contract.test.ts +++ b/tests/plugin-loader-contract.test.ts @@ -5,7 +5,7 @@ * All assertions here must PASS. This file guards the fixed contract from regressions. */ -import { describe, expect, it } from "bun:test"; +import { describe, expect, it } from "vitest"; import { readFileSync } from "node:fs"; function readPackageJson(): Record { @@ -55,6 +55,7 @@ describe("OpenCode 1.3.x plugin-loader contract", () => { // Attempt to invoke server with a minimal mock PluginInput. // The plugin may throw during warmup (missing sqlite/usearch in test env) — that is expected. // If it succeeds, assert the returned hooks have the expected shape. + // Wrap in a timeout to avoid hanging on initialization. const mockInput = { client: {}, project: {}, @@ -65,14 +66,17 @@ describe("OpenCode 1.3.x plugin-loader contract", () => { }; try { - const hooks = (await (serverFn as (input: unknown) => Promise>)( - mockInput - )) as Record; + const hooks = (await Promise.race([ + (serverFn as (input: unknown) => Promise>)(mockInput), + new Promise((_, reject) => + setTimeout(() => reject(new Error("Plugin init timeout")), 2000) + ), + ])) as Record; // If we reach here, assert expected hook keys exist expect(typeof hooks["chat.message"]).toBe("function"); expect(typeof hooks["event"]).toBe("function"); } catch { - // Warmup/sqlite/usearch failure in test environment is acceptable. + // Warmup/sqlite/usearch failure or timeout in test environment is acceptable. // The callable surface assertion above is sufficient for contract verification. } }); diff --git a/tests/privacy.test.ts b/tests/privacy.test.ts index 6832d26..399192d 100644 --- a/tests/privacy.test.ts +++ b/tests/privacy.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect } from "bun:test"; +import { describe, it, expect } from "vitest"; import { stripPrivateContent, isFullyPrivate } from "../src/services/privacy.js"; describe("privacy", () => { diff --git a/tests/profile-tool-runtime.test.ts b/tests/profile-tool-runtime.test.ts index aa2c47f..fa60283 100644 --- a/tests/profile-tool-runtime.test.ts +++ b/tests/profile-tool-runtime.test.ts @@ -1,4 +1,4 @@ -import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, mock } from "bun:test"; +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; @@ -14,19 +14,33 @@ function writeProjectConfig(config: Record) { writeFileSync(join(opencodeDir, "opencode-mem0.json"), JSON.stringify(config), "utf-8"); } -async function createPlugin() { - const { memoryClient } = await import("../src/services/client.js"); - mock.module("../src/services/client.js", async () => ({ +vi.mock("../src/services/client.js", async () => { + const actual = await vi.importActual( + "../src/services/client.js" + ); + return { memoryClient: { - ...memoryClient, + ...actual.memoryClient, isReady: async () => true, warmup: async () => {}, }, - })); + }; +}); +async function createPlugin(tagsMock?: { userEmail?: string; userName?: string }) { globalThis[WARMUP_KEY as keyof typeof globalThis] = true as any; - const { OpenCodeMemPlugin } = await import(`../src/index.js?runtime=${Date.now()}`); + if (tagsMock) { + vi.doMock("../src/services/tags.js", () => ({ + getTags: () => ({ + project: { tag: "project-tag" }, + user: { userEmail: tagsMock.userEmail, userName: tagsMock.userName }, + }), + getProjectName: (dir: string) => dir.split(/[\\/]/).pop() || dir, + })); + } + + const { OpenCodeMemPlugin } = await import("../src/index.js"); return OpenCodeMemPlugin({ directory: tmpDir, worktree: tmpDir, @@ -49,9 +63,10 @@ describe("memory tool profile runtime behavior", () => { }); beforeEach(() => { + vi.resetModules(); const opencodeDir = join(tmpDir, ".opencode"); if (existsSync(opencodeDir)) rmSync(opencodeDir, { recursive: true, force: true }); - mock.restore(); + vi.restoreAllMocks(); delete globalThis[WARMUP_KEY as keyof typeof globalThis]; const userProfilesDbPath = join(tmpDir, "data", "user-profiles.db"); @@ -65,7 +80,7 @@ describe("memory tool profile runtime behavior", () => { }); afterEach(() => { - mock.restore(); + vi.restoreAllMocks(); delete globalThis[WARMUP_KEY as keyof typeof globalThis]; }); @@ -83,7 +98,7 @@ describe("memory tool profile runtime behavior", () => { autoCaptureEnabled: false, }); - const plugin = await createPlugin(); + const plugin = await createPlugin({ userEmail: "test@example.com", userName: "Test User" }); const result = JSON.parse( await plugin.tool.memory.execute({ mode: "profile", query: "jira" }, { sessionID: "s1" }) ); @@ -101,7 +116,7 @@ describe("memory tool profile runtime behavior", () => { autoCaptureEnabled: false, }); - const plugin = await createPlugin(); + const plugin = await createPlugin({ userEmail: "test@example.com", userName: "Test User" }); const writeResult = JSON.parse( await plugin.tool.memory.execute( @@ -131,7 +146,7 @@ describe("memory tool profile runtime behavior", () => { autoCaptureEnabled: false, }); - const plugin = await createPlugin(); + const plugin = await createPlugin({ userEmail: "test@example.com", userName: "Test User" }); const result = JSON.parse( await plugin.tool.memory.execute({ mode: "profile", content: " " }, { sessionID: "s3" }) ); @@ -149,7 +164,7 @@ describe("memory tool profile runtime behavior", () => { autoCaptureEnabled: false, }); - const plugin = await createPlugin(); + const plugin = await createPlugin({ userEmail: "test@example.com", userName: "Test User" }); const result = JSON.parse( await plugin.tool.memory.execute( { @@ -171,12 +186,7 @@ describe("memory tool profile runtime behavior", () => { autoCaptureEnabled: false, }); - mock.module("../src/services/tags.js", () => ({ - getTags: () => ({ project: { tag: "project-tag" }, user: { userEmail: undefined } }), - getProjectName: (dir: string) => dir.split(/[\\/]/).pop() || dir, - })); - - const plugin = await createPlugin(); + const plugin = await createPlugin({ userEmail: undefined, userName: undefined }); const result = JSON.parse( await plugin.tool.memory.execute( { mode: "profile", content: "Default Jira board is DOPS" }, diff --git a/tests/profile-write.test.ts b/tests/profile-write.test.ts index 2851b58..30ab96a 100644 --- a/tests/profile-write.test.ts +++ b/tests/profile-write.test.ts @@ -3,7 +3,7 @@ * Exercises the write path added to src/index.ts `profile` mode * by testing the underlying manager directly (no live plugin context needed). */ -import { describe, it, expect, beforeEach, afterEach } from "bun:test"; +import { describe, it, expect, beforeEach, afterEach } from "vitest"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; diff --git a/tests/project-scope.test.ts b/tests/project-scope.test.ts index 5464d31..26a7d41 100644 --- a/tests/project-scope.test.ts +++ b/tests/project-scope.test.ts @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, it } from "bun:test"; +import { afterEach, describe, expect, it } from "vitest"; import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from "node:fs"; import { basename, join } from "node:path"; import { tmpdir } from "node:os"; diff --git a/tests/tags.test.ts b/tests/tags.test.ts index 70f3f82..dd1bd18 100644 --- a/tests/tags.test.ts +++ b/tests/tags.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect } from "bun:test"; +import { describe, it, expect } from "vitest"; import { getProjectName } from "../src/services/tags.js"; describe("tags", () => { diff --git a/tests/tool-scope.test.ts b/tests/tool-scope.test.ts index c06fa8b..94388af 100644 --- a/tests/tool-scope.test.ts +++ b/tests/tool-scope.test.ts @@ -1,169 +1,159 @@ -import { afterEach, describe, expect, it } from "bun:test"; -import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; +import { afterEach, describe, expect, it, vi } from "vitest"; -const tempDirs: string[] = []; +const searchCalls: unknown[][] = []; +let lastListScope: string | undefined; -afterEach(() => { - for (const dir of tempDirs.splice(0)) { - rmSync(dir, { recursive: true, force: true }); - } -}); - -const indexUrl = new URL("../src/index.js", import.meta.url).href; -const clientUrl = new URL("../src/services/client.js", import.meta.url).href; -const configUrl = new URL("../src/config.js", import.meta.url).href; -const tagsUrl = new URL("../src/services/tags.js", import.meta.url).href; -const contextUrl = new URL("../src/services/context.js", import.meta.url).href; -const privacyUrl = new URL("../src/services/privacy.js", import.meta.url).href; -const autoCaptureUrl = new URL("../src/services/auto-capture.js", import.meta.url).href; -const learningUrl = new URL("../src/services/user-memory-learning.js", import.meta.url).href; -const promptManagerUrl = new URL( - "../src/services/user-prompt/user-prompt-manager.js", - import.meta.url -).href; -const webServerUrl = new URL("../src/services/web-server.js", import.meta.url).href; -const loggerUrl = new URL("../src/services/logger.js", import.meta.url).href; -const languageUrl = new URL("../src/services/language-detector.js", import.meta.url).href; - -type ScenarioInput = { - defaultScope?: "project" | "all-projects"; - args: Record; -}; - -function runScenario(input: ScenarioInput) { - const dir = mkdtempSync(join(tmpdir(), "opencode-mem0-tool-scope-")); - tempDirs.push(dir); - - const scriptPath = join(dir, "scenario.mjs"); - const script = ` -import { mock } from "bun:test"; - -const searchCalls = []; -let lastListScope; -const defaultScope = ${JSON.stringify(input.defaultScope)}; - -mock.module(${JSON.stringify(clientUrl)}, () => ({ - memoryClient: { - warmup: async () => {}, - isReady: async () => true, - searchMemories: async (...args) => { - searchCalls.push(args); - return { success: true, results: [], total: 0, timing: 0 }; - }, - listMemories: async (_tag, _limit, scope = "project") => { - lastListScope = scope; - return { - success: true, - memories: [], - pagination: { currentPage: 1, totalItems: 0, totalPages: 0 }, - scope, - }; - }, - addMemory: async () => ({ success: true, id: "m1" }), - deleteMemory: async () => ({ success: true }), - searchMemoriesBySessionID: async () => ({ success: true, results: [], total: 0, timing: 0 }), - close() {}, - }, -})); - -mock.module(${JSON.stringify(configUrl)}, () => ({ - CONFIG: { - autoCaptureLanguage: "auto", - storagePath: "/tmp/opencode-mem0-test", - memory: { defaultScope }, - }, - initConfig: () => {}, - isConfigured: () => true, -})); - -mock.module(${JSON.stringify(tagsUrl)}, () => ({ +vi.mock("../src/services/logger.js", () => ({ log: () => {} })); +vi.mock("../src/services/tags.js", () => ({ getTags: () => ({ project: { tag: "project-tag" }, user: { userEmail: "u@example.com" } }), })); - -mock.module(${JSON.stringify(contextUrl)}, () => ({ formatContextForPrompt: () => "" })); -mock.module(${JSON.stringify(privacyUrl)}, () => ({ - stripPrivateContent: (value) => value, +vi.mock("../src/services/context.js", () => ({ formatContextForPrompt: () => "" })); +vi.mock("../src/services/privacy.js", () => ({ + stripPrivateContent: (value: unknown) => value, isFullyPrivate: () => false, })); -mock.module(${JSON.stringify(autoCaptureUrl)}, () => ({ performAutoCapture: async () => {} })); -mock.module(${JSON.stringify(learningUrl)}, () => ({ performUserProfileLearning: async () => {} })); -mock.module(${JSON.stringify(promptManagerUrl)}, () => ({ userPromptManager: { savePrompt() {} } })); -mock.module(${JSON.stringify(webServerUrl)}, () => ({ +vi.mock("../src/services/auto-capture.js", () => ({ performAutoCapture: async () => {} })); +vi.mock("../src/services/user-memory-learning.js", () => ({ + performUserProfileLearning: async () => {}, +})); +vi.mock("../src/services/user-prompt/user-prompt-manager.js", () => ({ + userPromptManager: { savePrompt() {} }, +})); +vi.mock("../src/services/web-server.js", () => ({ startWebServer: async () => null, WebServer: class {}, })); -mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {} })); -mock.module(${JSON.stringify(languageUrl)}, () => ({ getLanguageName: () => "English" })); - -const { OpenCodeMemPlugin } = await import(${JSON.stringify(indexUrl)}); -const plugin = await OpenCodeMemPlugin({ directory: "/workspace", client: {} }); -const memoryTool = plugin.tool?.memory; - -if (!memoryTool) { - throw new Error("memory tool not available"); -} - -await memoryTool.execute(${JSON.stringify(input.args)}, { sessionID: "s1" }); +vi.mock("../src/services/language-detector.js", () => ({ getLanguageName: () => "English" })); -console.log( - JSON.stringify({ - searchScope: searchCalls[0]?.[2], - listScope: lastListScope, - }) -); -`; +// Helper to create plugin with specific config +async function createPlugin(defaultScope?: "project" | "all-projects") { + vi.resetModules(); - writeFileSync(scriptPath, script); - - const result = Bun.spawnSync({ - cmd: [process.execPath, scriptPath], - stdout: "pipe", - stderr: "pipe", - }); + // Create a mock config module + const mockConfig = { + autoCaptureLanguage: "auto", + storagePath: "/tmp/opencode-mem0-test", + memory: { defaultScope }, + webServerEnabled: false, + autoCaptureEnabled: false, + vectorBackend: "exact-scan", + similarityThreshold: 0.6, + maxMemories: 10, + maxProfileItems: 5, + injectProfile: false, + containerTagPrefix: "opencode", + embeddingModel: "Xenova/nomic-embed-text-v1", + embeddingDimensions: 768, + showAutoCaptureToasts: false, + showUserProfileToasts: false, + showErrorToasts: false, + userProfileAnalysisInterval: 10, + userProfileMaxPreferences: 20, + userProfileMaxPatterns: 15, + userProfileMaxWorkflows: 10, + userProfileConfidenceDecayDays: 30, + userProfileChangelogRetentionCount: 5, + aiSessionRetentionDays: 7, + webServerPort: 4747, + webServerHost: "127.0.0.1", + maxVectorsPerShard: 50000, + autoCleanupEnabled: true, + autoCleanupRetentionDays: 30, + deduplicationEnabled: true, + deduplicationSimilarityThreshold: 0.9, + transcriptStorage: { enabled: false, maxAgeDays: 30 }, + memoryScoring: { + enabled: false, + recalculationIntervalMinutes: 60, + recencyHalfLifeDays: 7, + utilityHalfLifeDays: 3, + }, + memoryLifecycle: { + stmDecayDays: 7, + ltmDecayDays: 90, + promotionThreshold: 0.7, + archiveThreshold: 0.2, + archiveAfterDays: 30, + checkIntervalMinutes: 60, + }, + compaction: { enabled: true, memoryLimit: 10 }, + chatMessage: { + enabled: false, + maxMemories: 3, + excludeCurrentSession: true, + injectOn: "first" as const, + maxAgeDays: undefined, + }, + retrieval: { maxResults: 20, diversityThreshold: 0.9, contextBoost: 1.5 }, + initConfig: () => {}, + isConfigured: () => true, + CONFIG: {} as any, + }; - const stdout = Buffer.from(result.stdout).toString("utf8").trim(); - const stderr = Buffer.from(result.stderr).toString("utf8").trim(); + // Fill in CONFIG reference + mockConfig.CONFIG = mockConfig; + + vi.doMock("../src/config.js", () => mockConfig); + vi.doMock("../src/services/client.js", () => ({ + memoryClient: { + warmup: async () => {}, + isReady: async () => true, + searchMemories: async (...args: unknown[]) => { + searchCalls.push(args); + return { success: true, results: [], total: 0, timing: 0 }; + }, + listMemories: async (_tag: unknown, _limit: unknown, scope = "project") => { + lastListScope = scope; + return { + success: true, + memories: [], + pagination: { currentPage: 1, totalItems: 0, totalPages: 0 }, + scope, + }; + }, + addMemory: async () => ({ success: true, id: "m1" }), + deleteMemory: async () => ({ success: true }), + searchMemoriesBySessionID: async () => ({ success: true, results: [], total: 0, timing: 0 }), + close() {}, + }, + })); - return { - exitCode: result.exitCode, - stdout, - stderr, - parsed: stdout ? JSON.parse(stdout) : null, - }; + const { OpenCodeMemPlugin } = await import("../src/index.js"); + return OpenCodeMemPlugin({ directory: "/workspace", client: {} }); } describe("tool memory scope", () => { - it("falls back to config default scope", () => { - const result = runScenario({ - defaultScope: "all-projects", - args: { mode: "search", query: "hello" }, - }); + afterEach(() => { + searchCalls.length = 0; + lastListScope = undefined; + vi.clearAllMocks(); + vi.resetModules(); + }); + + it("falls back to config default scope", async () => { + const plugin = await createPlugin("all-projects"); + const memoryTool = plugin.tool?.memory; + if (!memoryTool) throw new Error("memory tool not available"); - expect(result.exitCode).toBe(0); - expect(result.stderr).toBe(""); - expect(result.parsed?.searchScope).toBe("all-projects"); + await memoryTool.execute({ mode: "search", query: "hello" }, { sessionID: "s1" }); + expect(searchCalls[0]?.[2]).toBe("all-projects"); }); - it("lets explicit args scope override config", () => { - const result = runScenario({ - defaultScope: "all-projects", - args: { mode: "list", scope: "project" }, - }); + it("lets explicit args scope override config", async () => { + const plugin = await createPlugin("all-projects"); + const memoryTool = plugin.tool?.memory; + if (!memoryTool) throw new Error("memory tool not available"); - expect(result.exitCode).toBe(0); - expect(result.stderr).toBe(""); - expect(result.parsed?.listScope).toBe("project"); + await memoryTool.execute({ mode: "list", scope: "project" }, { sessionID: "s1" }); + expect(lastListScope).toBe("project"); }); - it("falls back to project when config scope is unset", () => { - const result = runScenario({ - args: { mode: "list" }, - }); + it("falls back to project when config scope is unset", async () => { + const plugin = await createPlugin(undefined); + const memoryTool = plugin.tool?.memory; + if (!memoryTool) throw new Error("memory tool not available"); - expect(result.exitCode).toBe(0); - expect(result.stderr).toBe(""); - expect(result.parsed?.listScope).toBe("project"); + await memoryTool.execute({ mode: "list" }, { sessionID: "s1" }); + expect(lastListScope).toBe("project"); }); }); diff --git a/tests/vector-backends/backend-factory.test.ts b/tests/vector-backends/backend-factory.test.ts index 2b33868..bd6f68b 100644 --- a/tests/vector-backends/backend-factory.test.ts +++ b/tests/vector-backends/backend-factory.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from "bun:test"; +import { describe, expect, it } from "vitest"; import { createVectorBackend } from "../../src/services/vector-backends/backend-factory.js"; import type { VectorBackend } from "../../src/services/vector-backends/types.js"; diff --git a/tests/vector-backends/exact-scan-backend.test.ts b/tests/vector-backends/exact-scan-backend.test.ts index b5bf0fc..1ed8bf1 100644 --- a/tests/vector-backends/exact-scan-backend.test.ts +++ b/tests/vector-backends/exact-scan-backend.test.ts @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, it } from "bun:test"; +import { afterEach, describe, expect, it } from "vitest"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; diff --git a/tests/vector-backends/migration-fallback.test.ts b/tests/vector-backends/migration-fallback.test.ts index 6edfb34..509bee9 100644 --- a/tests/vector-backends/migration-fallback.test.ts +++ b/tests/vector-backends/migration-fallback.test.ts @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, it } from "bun:test"; +import { afterEach, describe, expect, it } from "vitest"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; diff --git a/tests/vector-backends/usearch-backend.test.ts b/tests/vector-backends/usearch-backend.test.ts index 0c65e36..a4599e3 100644 --- a/tests/vector-backends/usearch-backend.test.ts +++ b/tests/vector-backends/usearch-backend.test.ts @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, it } from "bun:test"; +import { afterEach, describe, expect, it } from "vitest"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; diff --git a/tests/vector-search-backend-integration.test.ts b/tests/vector-search-backend-integration.test.ts index 949856f..6d5683b 100644 --- a/tests/vector-search-backend-integration.test.ts +++ b/tests/vector-search-backend-integration.test.ts @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, it } from "bun:test"; +import { afterEach, describe, expect, it } from "vitest"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; diff --git a/tests/windows-path.test.ts b/tests/windows-path.test.ts index 85d9034..18fd0a2 100644 --- a/tests/windows-path.test.ts +++ b/tests/windows-path.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect } from "bun:test"; +import { describe, it, expect } from "vitest"; import { getProjectName } from "../src/services/tags.js"; import { dirname } from "node:path"; import { join } from "node:path"; diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..e32a0ef --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + globals: false, + environment: "node", + include: ["tests/**/*.test.ts"], + exclude: ["node_modules", "dist"], + testTimeout: 30000, + hookTimeout: 30000, + }, +}); From 68e0bac5302837f87fa06b58213e27b9e818ede7 Mon Sep 17 00:00:00 2001 From: ZeR020 <88128532+ZeR020@users.noreply.github.com> Date: Wed, 6 May 2026 20:24:25 +0530 Subject: [PATCH 2/4] fix: address PR review feedback - unused imports, directory safety, docs, ESM compatibility --- README.md | 14 +++-- scripts/build.mjs | 7 ++- scripts/migrate-tests.mjs | 12 ++-- src/services/ai/session/ai-session-manager.ts | 7 ++- src/services/memory-conflicts.ts | 3 +- src/services/platform-server.ts | 23 ++++++- src/services/sqlite/shard-manager.ts | 2 +- src/services/sqlite/sqlite-bootstrap.ts | 62 +++++++++++++++++-- src/services/sqlite/transcript-manager.ts | 2 +- src/services/sqlite/vector-search.ts | 2 +- .../user-profile/user-profile-manager.ts | 6 +- .../user-prompt/user-prompt-manager.ts | 6 +- 12 files changed, 113 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 1e73d89..8db2832 100644 --- a/README.md +++ b/README.md @@ -300,17 +300,23 @@ Access the UI at `http://localhost:4747`. ## Testing ```bash -# Run all tests +# Run all tests (Bun) bun test -# Run specific feature tests +# Run all tests (Node.js) +npm test + +# Run specific feature tests (Bun) bun test tests/memory-engine.test.ts +# Run specific feature tests (Node.js) +npx vitest run tests/memory-engine.test.ts + # Type check bun run typecheck -# Build -bun run build +# Build (cross-platform) +npm run build ``` 29 integration tests cover transcript storage, conflict resolution, hybrid search, diversity ranking, and STM/LTM decay. diff --git a/scripts/build.mjs b/scripts/build.mjs index ba4e37f..07fb46d 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -1,10 +1,13 @@ import { cpSync, existsSync, mkdirSync, readdirSync } from "node:fs"; -import { join, resolve } from "node:path"; +import { join, resolve, dirname } from "node:path"; import { spawnSync } from "node:child_process"; +import { createRequire } from "node:module"; function main() { // Run TypeScript compiler - const tscPath = resolve("./node_modules/.bin/tsc"); + // Resolve the actual TypeScript JS entrypoint, not the platform-specific shim + const require = createRequire(import.meta.url); + const tscPath = require.resolve("typescript/bin/tsc"); if (!existsSync(tscPath)) { console.error("Error: tsc not found. Run npm install or bun install first."); process.exit(1); diff --git a/scripts/migrate-tests.mjs b/scripts/migrate-tests.mjs index 1952897..6a0a326 100644 --- a/scripts/migrate-tests.mjs +++ b/scripts/migrate-tests.mjs @@ -1,7 +1,7 @@ import { readFileSync, readdirSync, statSync, writeFileSync } from "node:fs"; import { join } from "node:path"; -function migrateFile(filePath: string): void { +function migrateFile(filePath) { let content = readFileSync(filePath, "utf-8"); // Replace bun:test import with vitest @@ -36,17 +36,19 @@ function migrateFile(filePath: string): void { // Replace spyOn( with vi.spyOn( content = content.replace(/\bspyOn\(/g, "vi.spyOn("); - // Replace Bun.spawnSync with spawnSync from node:child_process + // Flag Bun.spawnSync usage for manual migration if (content.includes("Bun.spawnSync")) { - content = content.replace(/Bun\.spawnSync\s*\(\s*\{/g, "spawnSync("); - // Also need to add import - will handle manually for specific files + const matches = content.match(/Bun\.spawnSync\s*\(/g); + if (matches) { + content = `// MANUAL_MIGRATION_REQUIRED: Bun.spawnSync usage detected (${matches.length} occurrence(s)).\n// Convert to spawnSync(command, args, options) from "node:child_process" and add appropriate import.\n${content}`; + } } writeFileSync(filePath, content); console.log(`Migrated: ${filePath}`); } -function migrateDirectory(dir: string): void { +function migrateDirectory(dir) { for (const entry of readdirSync(dir)) { const path = join(dir, entry); const stat = statSync(path); diff --git a/src/services/ai/session/ai-session-manager.ts b/src/services/ai/session/ai-session-manager.ts index bcaf878..b4fb89f 100644 --- a/src/services/ai/session/ai-session-manager.ts +++ b/src/services/ai/session/ai-session-manager.ts @@ -1,7 +1,6 @@ -import { getDatabase, type Database } from "../../sqlite/sqlite-bootstrap.js"; +import { type Database } from "../../sqlite/sqlite-bootstrap.js"; import { join, dirname } from "node:path"; import { existsSync, mkdirSync } from "node:fs"; -import { log } from "../../logger.js"; import { connectionManager } from "../../sqlite/connection-manager.js"; import { CONFIG } from "../../../config.js"; import { @@ -23,6 +22,10 @@ export class AISessionManager { constructor() { this.dbPath = join(CONFIG.storagePath, AI_SESSIONS_DB_NAME); + const dir = dirname(this.dbPath); + if (!existsSync(dir)) { + mkdirSync(dir, { recursive: true }); + } this.db = connectionManager.getConnection(this.dbPath); this.sessionRetentionMs = CONFIG.aiSessionRetentionDays * 24 * 60 * 60 * 1000; this.initDatabase(); diff --git a/src/services/memory-conflicts.ts b/src/services/memory-conflicts.ts index 688f45e..bf709ac 100644 --- a/src/services/memory-conflicts.ts +++ b/src/services/memory-conflicts.ts @@ -1,11 +1,10 @@ -import { getDatabase, type Database } from "./sqlite/sqlite-bootstrap.js"; +import { type Database } from "./sqlite/sqlite-bootstrap.js"; import { connectionManager } from "./sqlite/connection-manager.js"; import { shardManager } from "./sqlite/shard-manager.js"; import { vectorSearch } from "./sqlite/vector-search.js"; import { log } from "./logger.js"; import { CONFIG } from "../config.js"; import type { MemoryConflict } from "./sqlite/types.js"; -import { calculateInterference } from "./memory-scoring.js"; type DatabaseType = Database; diff --git a/src/services/platform-server.ts b/src/services/platform-server.ts index 6434f5a..f233c85 100644 --- a/src/services/platform-server.ts +++ b/src/services/platform-server.ts @@ -12,12 +12,28 @@ interface ServeOptions { fetch: (req: Request) => Promise | Response; } +function normalizeHeaders(rawHeaders: IncomingMessage["headers"]): Headers { + const headers = new Headers(); + for (const [key, value] of Object.entries(rawHeaders)) { + if (value === undefined) continue; + if (Array.isArray(value)) { + for (const v of value) { + headers.append(key, v); + } + } else { + headers.set(key, value); + } + } + return headers; +} + async function createNodeServer(options: ServeOptions): Promise { const requestIPs = new WeakMap(); const nodeServer = createServer(async (req: IncomingMessage, res: ServerResponse) => { try { - const url = `http://${req.headers.host}${req.url}`; + const host = req.headers.host || `${options.hostname}:${options.port}`; + const url = `http://${host}${req.url}`; const chunks: Buffer[] = []; for await (const chunk of req) { @@ -27,7 +43,7 @@ async function createNodeServer(options: ServeOptions): Promise const request = new Request(url, { method: req.method, - headers: new Headers(req.headers as Record), + headers: normalizeHeaders(req.headers), body: body && body.length > 0 ? body : undefined, }); @@ -43,8 +59,9 @@ async function createNodeServer(options: ServeOptions): Promise const responseBody = await response.arrayBuffer(); res.end(Buffer.from(responseBody)); } catch (error) { + console.error("Platform server error:", error); res.statusCode = 500; - res.end(String(error)); + res.end("Internal server error"); } }); diff --git a/src/services/sqlite/shard-manager.ts b/src/services/sqlite/shard-manager.ts index c7e7faa..8866b06 100644 --- a/src/services/sqlite/shard-manager.ts +++ b/src/services/sqlite/shard-manager.ts @@ -1,4 +1,4 @@ -import { getDatabase, type Database } from "./sqlite-bootstrap.js"; +import { type Database } from "./sqlite-bootstrap.js"; import { join, basename } from "node:path"; import { existsSync } from "node:fs"; import { CONFIG } from "../../config.js"; diff --git a/src/services/sqlite/sqlite-bootstrap.ts b/src/services/sqlite/sqlite-bootstrap.ts index 7f95952..6c2a6c4 100644 --- a/src/services/sqlite/sqlite-bootstrap.ts +++ b/src/services/sqlite/sqlite-bootstrap.ts @@ -1,4 +1,5 @@ import type { Database as BSqliteDatabase, Statement as BSqliteStatement } from "better-sqlite3"; +import { createRequire } from "node:module"; export interface Statement { run(...params: unknown[]): { changes: number; lastInsertRowid: number | bigint }; @@ -15,12 +16,64 @@ export interface Database { let DatabaseImpl: new (path: string) => Database; +let _require: NodeRequire | undefined; +function getRequire(): NodeRequire { + if (!_require) { + _require = typeof require !== "undefined" ? require : createRequire(import.meta.url); + } + return _require; +} + +class BunSqliteDatabase implements Database { + private db: any; + + constructor(path: string) { + const bunSqlite = getRequire()("bun:sqlite") as typeof import("bun:sqlite"); + this.db = new bunSqlite.Database(path); + } + + prepare(sql: string): Statement { + const stmt = this.db.prepare(sql); + return { + run: (...params: unknown[]) => { + const result = params.length > 0 ? stmt.run(...params) : stmt.run(); + return { + changes: result.changes, + lastInsertRowid: result.lastInsertRowid, + }; + }, + get: (...params: unknown[]) => { + return params.length > 0 ? stmt.get(...params) : stmt.get(); + }, + all: (...params: unknown[]) => { + return params.length > 0 ? stmt.all(...params) : stmt.all(); + }, + }; + } + + run(sql: string, ...params: unknown[]): { changes: number; lastInsertRowid: number | bigint } { + const stmt = this.db.prepare(sql); + const result = params.length > 0 ? stmt.run(...params) : stmt.run(); + return { + changes: result.changes, + lastInsertRowid: result.lastInsertRowid, + }; + } + + exec(sql: string): void { + this.db.exec(sql); + } + + close(): void { + this.db.close(); + } +} + class BetterSqlite3Database implements Database { private db: BSqliteDatabase; constructor(path: string) { - // Dynamic require to avoid hard dependency when Bun is available - const BetterSqlite3 = require("better-sqlite3") as typeof import("better-sqlite3"); + const BetterSqlite3 = getRequire()("better-sqlite3") as typeof import("better-sqlite3"); this.db = new BetterSqlite3(path); } @@ -64,8 +117,9 @@ class BetterSqlite3Database implements Database { export function getDatabase(): new (path: string) => Database { if (!DatabaseImpl) { try { - const bunSqlite = require("bun:sqlite") as typeof import("bun:sqlite"); - DatabaseImpl = bunSqlite.Database as unknown as new (path: string) => Database; + // Test if bun:sqlite is available + getRequire()("bun:sqlite"); + DatabaseImpl = BunSqliteDatabase; } catch { DatabaseImpl = BetterSqlite3Database; } diff --git a/src/services/sqlite/transcript-manager.ts b/src/services/sqlite/transcript-manager.ts index 875fc59..dfd3a41 100644 --- a/src/services/sqlite/transcript-manager.ts +++ b/src/services/sqlite/transcript-manager.ts @@ -1,4 +1,4 @@ -import { getDatabase, type Database } from "./sqlite-bootstrap.js"; +import { type Database } from "./sqlite-bootstrap.js"; import { existsSync, mkdirSync } from "node:fs"; import { join, dirname } from "node:path"; import { log } from "../logger.js"; diff --git a/src/services/sqlite/vector-search.ts b/src/services/sqlite/vector-search.ts index 0d647d3..2811bd2 100644 --- a/src/services/sqlite/vector-search.ts +++ b/src/services/sqlite/vector-search.ts @@ -1,4 +1,4 @@ -import { getDatabase, type Database } from "./sqlite-bootstrap.js"; +import { type Database } from "./sqlite-bootstrap.js"; import { connectionManager } from "./connection-manager.js"; import { log } from "../logger.js"; import { CONFIG } from "../../config.js"; diff --git a/src/services/user-profile/user-profile-manager.ts b/src/services/user-profile/user-profile-manager.ts index e400dd0..e543cd3 100644 --- a/src/services/user-profile/user-profile-manager.ts +++ b/src/services/user-profile/user-profile-manager.ts @@ -1,8 +1,6 @@ -import { getDatabase, type Database } from "../sqlite/sqlite-bootstrap.js"; -import { join, dirname } from "node:path"; -import { existsSync, mkdirSync } from "node:fs"; +import { type Database } from "../sqlite/sqlite-bootstrap.js"; +import { join } from "node:path"; import { connectionManager } from "../sqlite/connection-manager.js"; -import { log } from "../logger.js"; import { CONFIG } from "../../config.js"; import { type UserProfile, diff --git a/src/services/user-prompt/user-prompt-manager.ts b/src/services/user-prompt/user-prompt-manager.ts index df30d89..76e718f 100644 --- a/src/services/user-prompt/user-prompt-manager.ts +++ b/src/services/user-prompt/user-prompt-manager.ts @@ -1,8 +1,6 @@ -import { getDatabase, type Database } from "../sqlite/sqlite-bootstrap.js"; -import { join, dirname } from "node:path"; -import { existsSync, mkdirSync } from "node:fs"; +import { type Database } from "../sqlite/sqlite-bootstrap.js"; +import { join } from "node:path"; import { connectionManager } from "../sqlite/connection-manager.js"; -import { log } from "../logger.js"; import { CONFIG } from "../../config.js"; type DatabaseType = Database; From e4adbb558791f9ccbaa6c1cd41bc8ac1d40e69b5 Mon Sep 17 00:00:00 2001 From: ZeR020 <88128532+ZeR020@users.noreply.github.com> Date: Wed, 6 May 2026 20:44:10 +0530 Subject: [PATCH 3/4] fix(tests): address CodeRabbit review feedback - remove redundant vi.resetModules and reset CONFIG mock state --- tests/memory-engine.test.ts | 6 +++++- tests/tool-scope.test.ts | 2 -- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/memory-engine.test.ts b/tests/memory-engine.test.ts index 57cfc07..feb44f2 100644 --- a/tests/memory-engine.test.ts +++ b/tests/memory-engine.test.ts @@ -298,9 +298,13 @@ const { calculateContextBoost, calculateDiversityPenalty, contextTracker } = const { TranscriptManager } = await import("../src/services/sqlite/transcript-manager.js"); const { detectConflicts, resolveConflict } = await import("../src/services/memory-conflicts.js"); -beforeEach(() => { +beforeEach(async () => { dbByPath.clear(); contextTracker.clear(); + const { CONFIG } = await import("../src/config.js"); + if (CONFIG?.transcriptStorage) { + CONFIG.transcriptStorage.enabled = true; + } }); describe("Memory Engine Integration", () => { diff --git a/tests/tool-scope.test.ts b/tests/tool-scope.test.ts index 94388af..42978ed 100644 --- a/tests/tool-scope.test.ts +++ b/tests/tool-scope.test.ts @@ -27,8 +27,6 @@ vi.mock("../src/services/language-detector.js", () => ({ getLanguageName: () => // Helper to create plugin with specific config async function createPlugin(defaultScope?: "project" | "all-projects") { - vi.resetModules(); - // Create a mock config module const mockConfig = { autoCaptureLanguage: "auto", From a2e10dd2792b6e88a2cc127e44e4496ad57ce4b5 Mon Sep 17 00:00:00 2001 From: ZeR020 <88128532+ZeR020@users.noreply.github.com> Date: Wed, 6 May 2026 21:00:47 +0530 Subject: [PATCH 4/4] fix(tests): replace vi.resetModules() with mutable mock state for Bun compatibility --- tests/profile-tool-runtime.test.ts | 52 +++++---- tests/tool-scope.test.ts | 180 +++++++++++++++-------------- 2 files changed, 122 insertions(+), 110 deletions(-) diff --git a/tests/profile-tool-runtime.test.ts b/tests/profile-tool-runtime.test.ts index fa60283..06f0fa8 100644 --- a/tests/profile-tool-runtime.test.ts +++ b/tests/profile-tool-runtime.test.ts @@ -8,36 +8,47 @@ const WARMUP_KEY = Symbol.for("opencode-mem0.plugin.warmedup"); let tmpDir: string; +var mockClient: any; +var currentTags: any; + +vi.mock("../src/services/client.js", () => ({ + memoryClient: mockClient, +})); + +vi.mock("../src/services/tags.js", () => ({ + getTags: () => currentTags, + getProjectName: (dir: string) => dir.split(/[\\/]/).pop() || dir, +})); + +mockClient = { + warmup: async () => {}, + isReady: async () => true, + searchMemories: async () => ({ success: true, results: [], total: 0, timing: 0 }), + listMemories: async () => ({ success: true, memories: [], pagination: {} }), + addMemory: async () => ({ success: true, id: "m1" }), + deleteMemory: async () => ({ success: true }), + searchMemoriesBySessionID: async () => ({ success: true, results: [], total: 0, timing: 0 }), + close() {}, +}; + +currentTags = { + project: { tag: "project-tag" }, + user: { userEmail: undefined as string | undefined, userName: undefined as string | undefined }, +}; + function writeProjectConfig(config: Record) { const opencodeDir = join(tmpDir, ".opencode"); mkdirSync(opencodeDir, { recursive: true }); writeFileSync(join(opencodeDir, "opencode-mem0.json"), JSON.stringify(config), "utf-8"); } -vi.mock("../src/services/client.js", async () => { - const actual = await vi.importActual( - "../src/services/client.js" - ); - return { - memoryClient: { - ...actual.memoryClient, - isReady: async () => true, - warmup: async () => {}, - }, - }; -}); - async function createPlugin(tagsMock?: { userEmail?: string; userName?: string }) { globalThis[WARMUP_KEY as keyof typeof globalThis] = true as any; if (tagsMock) { - vi.doMock("../src/services/tags.js", () => ({ - getTags: () => ({ - project: { tag: "project-tag" }, - user: { userEmail: tagsMock.userEmail, userName: tagsMock.userName }, - }), - getProjectName: (dir: string) => dir.split(/[\\/]/).pop() || dir, - })); + currentTags.user = { userEmail: tagsMock.userEmail, userName: tagsMock.userName }; + } else { + currentTags.user = { userEmail: undefined, userName: undefined }; } const { OpenCodeMemPlugin } = await import("../src/index.js"); @@ -63,7 +74,6 @@ describe("memory tool profile runtime behavior", () => { }); beforeEach(() => { - vi.resetModules(); const opencodeDir = join(tmpDir, ".opencode"); if (existsSync(opencodeDir)) rmSync(opencodeDir, { recursive: true, force: true }); vi.restoreAllMocks(); diff --git a/tests/tool-scope.test.ts b/tests/tool-scope.test.ts index 42978ed..0532ccf 100644 --- a/tests/tool-scope.test.ts +++ b/tests/tool-scope.test.ts @@ -3,6 +3,9 @@ import { afterEach, describe, expect, it, vi } from "vitest"; const searchCalls: unknown[][] = []; let lastListScope: string | undefined; +var mockConfig: any; +var mockClient: any; + vi.mock("../src/services/logger.js", () => ({ log: () => {} })); vi.mock("../src/services/tags.js", () => ({ getTags: () => ({ project: { tag: "project-tag" }, user: { userEmail: "u@example.com" } }), @@ -25,97 +28,97 @@ vi.mock("../src/services/web-server.js", () => ({ })); vi.mock("../src/services/language-detector.js", () => ({ getLanguageName: () => "English" })); -// Helper to create plugin with specific config -async function createPlugin(defaultScope?: "project" | "all-projects") { - // Create a mock config module - const mockConfig = { - autoCaptureLanguage: "auto", - storagePath: "/tmp/opencode-mem0-test", - memory: { defaultScope }, - webServerEnabled: false, - autoCaptureEnabled: false, - vectorBackend: "exact-scan", - similarityThreshold: 0.6, - maxMemories: 10, - maxProfileItems: 5, - injectProfile: false, - containerTagPrefix: "opencode", - embeddingModel: "Xenova/nomic-embed-text-v1", - embeddingDimensions: 768, - showAutoCaptureToasts: false, - showUserProfileToasts: false, - showErrorToasts: false, - userProfileAnalysisInterval: 10, - userProfileMaxPreferences: 20, - userProfileMaxPatterns: 15, - userProfileMaxWorkflows: 10, - userProfileConfidenceDecayDays: 30, - userProfileChangelogRetentionCount: 5, - aiSessionRetentionDays: 7, - webServerPort: 4747, - webServerHost: "127.0.0.1", - maxVectorsPerShard: 50000, - autoCleanupEnabled: true, - autoCleanupRetentionDays: 30, - deduplicationEnabled: true, - deduplicationSimilarityThreshold: 0.9, - transcriptStorage: { enabled: false, maxAgeDays: 30 }, - memoryScoring: { - enabled: false, - recalculationIntervalMinutes: 60, - recencyHalfLifeDays: 7, - utilityHalfLifeDays: 3, - }, - memoryLifecycle: { - stmDecayDays: 7, - ltmDecayDays: 90, - promotionThreshold: 0.7, - archiveThreshold: 0.2, - archiveAfterDays: 30, - checkIntervalMinutes: 60, - }, - compaction: { enabled: true, memoryLimit: 10 }, - chatMessage: { - enabled: false, - maxMemories: 3, - excludeCurrentSession: true, - injectOn: "first" as const, - maxAgeDays: undefined, - }, - retrieval: { maxResults: 20, diversityThreshold: 0.9, contextBoost: 1.5 }, - initConfig: () => {}, - isConfigured: () => true, - CONFIG: {} as any, - }; +vi.mock("../src/config.js", () => ({ + CONFIG: mockConfig, + isConfigured: () => true, + initConfig: () => {}, +})); - // Fill in CONFIG reference - mockConfig.CONFIG = mockConfig; +vi.mock("../src/services/client.js", () => ({ + memoryClient: mockClient, +})); + +mockConfig = { + autoCaptureLanguage: "auto", + storagePath: "/tmp/opencode-mem0-test", + memory: { defaultScope: undefined as "project" | "all-projects" | undefined }, + webServerEnabled: false, + autoCaptureEnabled: false, + vectorBackend: "exact-scan", + similarityThreshold: 0.6, + maxMemories: 10, + maxProfileItems: 5, + injectProfile: false, + containerTagPrefix: "opencode", + embeddingModel: "Xenova/nomic-embed-text-v1", + embeddingDimensions: 768, + showAutoCaptureToasts: false, + showUserProfileToasts: false, + showErrorToasts: false, + userProfileAnalysisInterval: 10, + userProfileMaxPreferences: 20, + userProfileMaxPatterns: 15, + userProfileMaxWorkflows: 10, + userProfileConfidenceDecayDays: 30, + userProfileChangelogRetentionCount: 5, + aiSessionRetentionDays: 7, + webServerPort: 4747, + webServerHost: "127.0.0.1", + maxVectorsPerShard: 50000, + autoCleanupEnabled: true, + autoCleanupRetentionDays: 30, + deduplicationEnabled: true, + deduplicationSimilarityThreshold: 0.9, + transcriptStorage: { enabled: false, maxAgeDays: 30 }, + memoryScoring: { + enabled: false, + recalculationIntervalMinutes: 60, + recencyHalfLifeDays: 7, + utilityHalfLifeDays: 3, + }, + memoryLifecycle: { + stmDecayDays: 7, + ltmDecayDays: 90, + promotionThreshold: 0.7, + archiveThreshold: 0.2, + archiveAfterDays: 30, + checkIntervalMinutes: 60, + }, + compaction: { enabled: true, memoryLimit: 10 }, + chatMessage: { + enabled: false, + maxMemories: 3, + excludeCurrentSession: true, + injectOn: "first" as const, + maxAgeDays: undefined, + }, + retrieval: { maxResults: 20, diversityThreshold: 0.9, contextBoost: 1.5 }, +}; - vi.doMock("../src/config.js", () => mockConfig); - vi.doMock("../src/services/client.js", () => ({ - memoryClient: { - warmup: async () => {}, - isReady: async () => true, - searchMemories: async (...args: unknown[]) => { - searchCalls.push(args); - return { success: true, results: [], total: 0, timing: 0 }; - }, - listMemories: async (_tag: unknown, _limit: unknown, scope = "project") => { - lastListScope = scope; - return { - success: true, - memories: [], - pagination: { currentPage: 1, totalItems: 0, totalPages: 0 }, - scope, - }; - }, - addMemory: async () => ({ success: true, id: "m1" }), - deleteMemory: async () => ({ success: true }), - searchMemoriesBySessionID: async () => ({ success: true, results: [], total: 0, timing: 0 }), - close() {}, - }, - })); +mockClient = { + warmup: async () => {}, + isReady: async () => true, + searchMemories: async (...args: unknown[]) => { + searchCalls.push(args); + return { success: true, results: [], total: 0, timing: 0 }; + }, + listMemories: async (_tag: unknown, _limit: unknown, scope = "project") => { + lastListScope = scope; + return { + success: true, + memories: [], + pagination: { currentPage: 1, totalItems: 0, totalPages: 0 }, + scope, + }; + }, + addMemory: async () => ({ success: true, id: "m1" }), + deleteMemory: async () => ({ success: true }), + searchMemoriesBySessionID: async () => ({ success: true, results: [], total: 0, timing: 0 }), + close() {}, +}; +async function createPlugin(defaultScope?: "project" | "all-projects") { + mockConfig.memory.defaultScope = defaultScope; const { OpenCodeMemPlugin } = await import("../src/index.js"); return OpenCodeMemPlugin({ directory: "/workspace", client: {} }); } @@ -125,7 +128,6 @@ describe("tool memory scope", () => { searchCalls.length = 0; lastListScope = undefined; vi.clearAllMocks(); - vi.resetModules(); }); it("falls back to config default scope", async () => {