Jelajahi Sumber

Merge pull request #4 from claude-code-best/feature/prod

Feature/prod
claude-code-best 3 minggu lalu
induk
melakukan
c57f5a29e8

+ 16 - 0
.editorconfig

@@ -0,0 +1,16 @@
+root = true
+
+[*]
+indent_style = tab
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
+
+[*.{json,yml,yaml}]
+indent_style = space
+indent_size = 2

+ 22 - 0
.githooks/pre-commit

@@ -0,0 +1,22 @@
+#!/bin/sh
+# pre-commit hook: 对暂存的文件运行 Biome 检查
+# 仅检查 src/ 下的 .ts/.tsx/.js/.jsx 文件
+
+STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '^src/.*\.(ts|tsx|js|jsx)$')
+
+if [ -z "$STAGED_FILES" ]; then
+  exit 0
+fi
+
+echo "Running Biome lint on staged files..."
+
+# 使用 biome lint 对暂存文件进行检查(仅 lint,不格式化,不自动修复)
+echo "$STAGED_FILES" | xargs bunx biome lint --no-errors-on-unmatched
+
+if [ $? -ne 0 ]; then
+  echo ""
+  echo "Biome lint failed. Fix errors or use --no-verify to bypass."
+  exit 1
+fi
+
+exit 0

+ 30 - 0
.github/workflows/ci.yml

@@ -0,0 +1,30 @@
+name: CI
+
+on:
+  push:
+    branches: [main, feature/*]
+  pull_request:
+    branches: [main]
+
+jobs:
+  ci:
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v4
+
+      - uses: oven-sh/setup-bun@v2
+        with:
+          bun-version: latest
+
+      - name: Install dependencies
+        run: bun install --frozen-lockfile
+
+      - name: Lint
+        run: bun run lint
+
+      - name: Test
+        run: bun test
+
+      - name: Build
+        run: bun run build

+ 14 - 4
TODO.md

@@ -10,7 +10,17 @@
 - [x] `color-diff-napi` — 颜色差异计算 NAPI 模块 (纯 TS 实现)
 - [x] `image-processor-napi` — 图像处理 NAPI 模块 (sharp + osascript 剪贴板)
 
-<!-- - [ ] `@ant/computer-use-swift` — Computer Use Swift 原生模块
-- [ ] `@ant/computer-use-mcp` — Computer Use MCP 服务
-- [ ] `@ant/computer-use-input` — Computer Use 输入模块
-- [ ] `@ant/claude-for-chrome-mcp` — Chrome MCP 扩展 -->
+- [x] `@ant/computer-use-swift` — Computer Use Swift 原生模块 (macOS JXA/screencapture 实现)
+- [x] `@ant/computer-use-mcp` — Computer Use MCP 服务 (类型安全 stub + sentinel apps + targetImageSize)
+- [x] `@ant/computer-use-input` — Computer Use 输入模块 (macOS AppleScript/JXA 实现)
+<!-- - [ ] `@ant/claude-for-chrome-mcp` — Chrome MCP 扩展 -->
+
+## 工程化能力
+
+- [x] 代码格式化与校验
+- [x] 冗余代码检查
+- [x] git hook 的配置
+- [x] 代码健康度检查
+- [x] Biome lint 规则调优(适配反编译代码,关闭格式化避免大规模 diff)
+- [x] 单元测试基础设施搭建 (test runner 配置)
+- [x] CI/CD 流水线 (GitHub Actions)

+ 86 - 0
biome.json

@@ -0,0 +1,86 @@
+{
+	"$schema": "https://biomejs.dev/schemas/2.4.10/schema.json",
+	"vcs": {
+		"enabled": true,
+		"clientKind": "git",
+		"useIgnoreFile": true
+	},
+	"files": {
+		"includes": ["**", "!!**/dist", "!!**/packages/@ant"]
+	},
+	"formatter": {
+		"enabled": false,
+		"indentStyle": "tab",
+		"lineWidth": 120
+	},
+	"linter": {
+		"enabled": true,
+		"rules": {
+			"recommended": true,
+			"suspicious": {
+				"noExplicitAny": "off",
+				"noAssignInExpressions": "off",
+				"noDoubleEquals": "off",
+				"noRedeclare": "off",
+				"noImplicitAnyLet": "off",
+				"noGlobalIsNan": "off",
+				"noFallthroughSwitchClause": "off",
+				"noShadowRestrictedNames": "off",
+				"noArrayIndexKey": "off",
+				"noConsole": "off",
+				"noConfusingLabels": "off",
+				"useIterableCallbackReturn": "off"
+			},
+			"style": {
+				"useConst": "off",
+				"noNonNullAssertion": "off",
+				"noParameterAssign": "off",
+				"useDefaultParameterLast": "off",
+				"noUnusedTemplateLiteral": "off",
+				"useTemplate": "off",
+				"useNumberNamespace": "off",
+				"useNodejsImportProtocol": "off",
+				"useImportType": "off"
+			},
+			"complexity": {
+				"noForEach": "off",
+				"noBannedTypes": "off",
+				"noUselessConstructor": "off",
+				"noStaticOnlyClass": "off",
+				"useOptionalChain": "off",
+				"noUselessSwitchCase": "off",
+				"noUselessFragments": "off",
+				"noUselessTernary": "off",
+				"noUselessLoneBlockStatements": "off",
+				"noUselessEmptyExport": "off",
+				"useArrowFunction": "off",
+				"useLiteralKeys": "off"
+			},
+			"correctness": {
+				"noUnusedVariables": "off",
+				"noUnusedImports": "off",
+				"useExhaustiveDependencies": "off",
+				"noSwitchDeclarations": "off",
+				"noUnreachable": "off",
+				"useHookAtTopLevel": "off",
+				"noVoidTypeReturn": "off",
+				"noConstantCondition": "off",
+				"noUnusedFunctionParameters": "off"
+			},
+			"a11y": {
+				"recommended": false
+			},
+			"nursery": {
+				"recommended": false
+			}
+		}
+	},
+	"javascript": {
+		"formatter": {
+			"quoteStyle": "double"
+		}
+	},
+	"assist": {
+		"enabled": false
+	}
+}

+ 155 - 0
bun.lock

@@ -117,6 +117,7 @@
         "@types/react-reconciler": "^0.33.0",
         "@types/sharp": "^0.32.0",
         "@types/turndown": "^5.0.6",
+        "knip": "^6.1.1",
         "typescript": "^6.0.2",
       },
     },
@@ -310,8 +311,12 @@
 
     "@commander-js/extra-typings": ["@commander-js/extra-typings@14.0.0", "https://registry.npmmirror.com/@commander-js/extra-typings/-/extra-typings-14.0.0.tgz", { "peerDependencies": { "commander": "~14.0.0" } }, "sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg=="],
 
+    "@emnapi/core": ["@emnapi/core@1.9.1", "https://registry.npmmirror.com/@emnapi/core/-/core-1.9.1.tgz", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="],
+
     "@emnapi/runtime": ["@emnapi/runtime@1.9.1", "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.9.1.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="],
 
+    "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "https://registry.npmmirror.com/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="],
+
     "@growthbook/growthbook": ["@growthbook/growthbook@1.6.5", "https://registry.npmmirror.com/@growthbook/growthbook/-/growthbook-1.6.5.tgz", { "dependencies": { "dom-mutator": "^0.6.0" } }, "sha512-mUaMsgeUTpRIUOTn33EUXHRK6j7pxBjwqH4WpQyq+pukjd1AIzWlEa6w7i6bInJUcweGgP2beXZmaP6b6UPn7A=="],
 
     "@grpc/grpc-js": ["@grpc/grpc-js@1.14.3", "https://registry.npmmirror.com/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA=="],
@@ -404,6 +409,14 @@
 
     "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "https://registry.npmmirror.com/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="],
 
+    "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.2", "https://registry.npmmirror.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw=="],
+
+    "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
+
+    "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
+
+    "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
+
     "@npmcli/fs": ["@npmcli/fs@5.0.0", "https://registry.npmmirror.com/@npmcli/fs/-/fs-5.0.0.tgz", { "dependencies": { "semver": "^7.3.5" } }, "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og=="],
 
     "@opentelemetry/api": ["@opentelemetry/api@1.9.1", "https://registry.npmmirror.com/@opentelemetry/api/-/api-1.9.1.tgz", {}, "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q=="],
@@ -448,6 +461,88 @@
 
     "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "https://registry.npmmirror.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="],
 
+    "@oxc-parser/binding-android-arm-eabi": ["@oxc-parser/binding-android-arm-eabi@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.121.0.tgz", { "os": "android", "cpu": "arm" }, "sha512-n07FQcySwOlzap424/PLMtOkbS7xOu8nsJduKL8P3COGHKgKoDYXwoAHCbChfgFpHnviehrLWIPX0lKGtbEk/A=="],
+
+    "@oxc-parser/binding-android-arm64": ["@oxc-parser/binding-android-arm64@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.121.0.tgz", { "os": "android", "cpu": "arm64" }, "sha512-/Dd1xIXboYAicw+twT2utxPD7bL8qh7d3ej0qvaYIMj3/EgIrGR+tSnjCUkiCT6g6uTC0neSS4JY8LxhdSU/sA=="],
+
+    "@oxc-parser/binding-darwin-arm64": ["@oxc-parser/binding-darwin-arm64@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.121.0.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-A0jNEvv7QMtCO1yk205t3DWU9sWUjQ2KNF0hSVO5W9R9r/R1BIvzG01UQAfmtC0dQm7sCrs5puixurKSfr2bRQ=="],
+
+    "@oxc-parser/binding-darwin-x64": ["@oxc-parser/binding-darwin-x64@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.121.0.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-SsHzipdxTKUs3I9EOAPmnIimEeJOemqRlRDOp9LIj+96wtxZejF51gNibmoGq8KoqbT1ssAI5po/E3J+vEtXGA=="],
+
+    "@oxc-parser/binding-freebsd-x64": ["@oxc-parser/binding-freebsd-x64@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.121.0.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-v1APOTkCp+RWOIDAHRoaeW/UoaHF15a60E8eUL6kUQXh+i4K7PBwq2Wi7jm8p0ymID5/m/oC1w3W31Z/+r7HQw=="],
+
+    "@oxc-parser/binding-linux-arm-gnueabihf": ["@oxc-parser/binding-linux-arm-gnueabihf@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.121.0.tgz", { "os": "linux", "cpu": "arm" }, "sha512-PmqPQuqHZyFVWA4ycr0eu4VnTMmq9laOHZd+8R359w6kzuNZPvmmunmNJ8ybkm769A0nCoVp3TJ6dUz7B3FYIQ=="],
+
+    "@oxc-parser/binding-linux-arm-musleabihf": ["@oxc-parser/binding-linux-arm-musleabihf@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.121.0.tgz", { "os": "linux", "cpu": "arm" }, "sha512-vF24htj+MOH+Q7y9A8NuC6pUZu8t/C2Fr/kDOi2OcNf28oogr2xadBPXAbml802E8wRAVfbta6YLDQTearz+jw=="],
+
+    "@oxc-parser/binding-linux-arm64-gnu": ["@oxc-parser/binding-linux-arm64-gnu@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.121.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-wjH8cIG2Lu/3d64iZpbYr73hREMgKAfu7fqpXjgM2S16y2zhTfDIp8EQjxO8vlDtKP5Rc7waZW72lh8nZtWrpA=="],
+
+    "@oxc-parser/binding-linux-arm64-musl": ["@oxc-parser/binding-linux-arm64-musl@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.121.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-qT663J/W8yQFw3dtscbEi9LKJevr20V7uWs2MPGTnvNZ3rm8anhhE16gXGpxDOHeg9raySaSHKhd4IGa3YZvuw=="],
+
+    "@oxc-parser/binding-linux-ppc64-gnu": ["@oxc-parser/binding-linux-ppc64-gnu@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.121.0.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-mYNe4NhVvDBbPkAP8JaVS8lC1dsoJZWH5WCjpw5E+sjhk1R08wt3NnXYUzum7tIiWPfgQxbCMcoxgeemFASbRw=="],
+
+    "@oxc-parser/binding-linux-riscv64-gnu": ["@oxc-parser/binding-linux-riscv64-gnu@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.121.0.tgz", { "os": "linux", "cpu": "none" }, "sha512-+QiFoGxhAbaI/amqX567784cDyyuZIpinBrJNxUzb+/L2aBRX67mN6Jv40pqduHf15yYByI+K5gUEygCuv0z9w=="],
+
+    "@oxc-parser/binding-linux-riscv64-musl": ["@oxc-parser/binding-linux-riscv64-musl@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.121.0.tgz", { "os": "linux", "cpu": "none" }, "sha512-9ykEgyTa5JD/Uhv2sttbKnCfl2PieUfOjyxJC/oDL2UO0qtXOtjPLl7H8Kaj5G7p3hIvFgu3YWvAxvE0sqY+hQ=="],
+
+    "@oxc-parser/binding-linux-s390x-gnu": ["@oxc-parser/binding-linux-s390x-gnu@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.121.0.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-DB1EW5VHZdc1lIRjOI3bW/wV6R6y0xlfvdVrqj6kKi7Ayu2U3UqUBdq9KviVkcUGd5Oq+dROqvUEEFRXGAM7EQ=="],
+
+    "@oxc-parser/binding-linux-x64-gnu": ["@oxc-parser/binding-linux-x64-gnu@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.121.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-s4lfobX9p4kPTclvMiH3gcQUd88VlnkMTF6n2MTMDAyX5FPNRhhRSFZK05Ykhf8Zy5NibV4PbGR6DnK7FGNN6A=="],
+
+    "@oxc-parser/binding-linux-x64-musl": ["@oxc-parser/binding-linux-x64-musl@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.121.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-P9KlyTpuBuMi3NRGpJO8MicuGZfOoqZVRP1WjOecwx8yk4L/+mrCRNc5egSi0byhuReblBF2oVoDSMgV9Bj4Hw=="],
+
+    "@oxc-parser/binding-openharmony-arm64": ["@oxc-parser/binding-openharmony-arm64@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.121.0.tgz", { "os": "none", "cpu": "arm64" }, "sha512-R+4jrWOfF2OAPPhj3Eb3U5CaKNAH9/btMveMULIrcNW/hjfysFQlF8wE0GaVBr81dWz8JLgQlsxwctoL78JwXw=="],
+
+    "@oxc-parser/binding-wasm32-wasi": ["@oxc-parser/binding-wasm32-wasi@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.121.0.tgz", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "cpu": "none" }, "sha512-5TFISkPTymKvsmIlKasPVTPuWxzCcrT8pM+p77+mtQbIZDd1UC8zww4CJcRI46kolmgrEX6QpKO8AvWMVZ+ifw=="],
+
+    "@oxc-parser/binding-win32-arm64-msvc": ["@oxc-parser/binding-win32-arm64-msvc@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.121.0.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-V0pxh4mql4XTt3aiEtRNUeBAUFOw5jzZNxPABLaOKAWrVzSr9+XUaB095lY7jqMf5t8vkfh8NManGB28zanYKw=="],
+
+    "@oxc-parser/binding-win32-ia32-msvc": ["@oxc-parser/binding-win32-ia32-msvc@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.121.0.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-4Ob1qvYMPnlF2N9rdmKdkQFdrq16QVcQwBsO8yiPZXof0fHKFF+LmQV501XFbi7lHyrKm8rlJRfQ/M8bZZPVLw=="],
+
+    "@oxc-parser/binding-win32-x64-msvc": ["@oxc-parser/binding-win32-x64-msvc@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.121.0.tgz", { "os": "win32", "cpu": "x64" }, "sha512-BOp1KCzdboB1tPqoCPXgntgFs0jjeSyOXHzgxVFR7B/qfr3F8r4YDacHkTOUNXtDgM8YwKnkf3rE5gwALYX7NA=="],
+
+    "@oxc-project/types": ["@oxc-project/types@0.121.0", "https://registry.npmmirror.com/@oxc-project/types/-/types-0.121.0.tgz", {}, "sha512-CGtOARQb9tyv7ECgdAlFxi0Fv7lmzvmlm2rpD/RdijOO9rfk/JvB1CjT8EnoD+tjna/IYgKKw3IV7objRb+aYw=="],
+
+    "@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.19.1.tgz", { "os": "android", "cpu": "arm" }, "sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg=="],
+
+    "@oxc-resolver/binding-android-arm64": ["@oxc-resolver/binding-android-arm64@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.19.1.tgz", { "os": "android", "cpu": "arm64" }, "sha512-oolbkRX+m7Pq2LNjr/kKgYeC7bRDMVTWPgxBGMjSpZi/+UskVo4jsMU3MLheZV55jL6c3rNelPl4oD60ggYmqA=="],
+
+    "@oxc-resolver/binding-darwin-arm64": ["@oxc-resolver/binding-darwin-arm64@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.19.1.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-nUC6d2i3R5B12sUW4O646qD5cnMXf2oBGPLIIeaRfU9doJRORAbE2SGv4eW6rMqhD+G7nf2Y8TTJTLiiO3Q/dQ=="],
+
+    "@oxc-resolver/binding-darwin-x64": ["@oxc-resolver/binding-darwin-x64@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.19.1.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-cV50vE5+uAgNcFa3QY1JOeKDSkM/9ReIcc/9wn4TavhW/itkDGrXhw9jaKnkQnGbjJ198Yh5nbX/Gr2mr4Z5jQ=="],
+
+    "@oxc-resolver/binding-freebsd-x64": ["@oxc-resolver/binding-freebsd-x64@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.19.1.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-xZOQiYGFxtk48PBKff+Zwoym7ScPAIVp4c14lfLxizO2LTTTJe5sx9vQNGrBymrf/vatSPNMD4FgsaaRigPkqw=="],
+
+    "@oxc-resolver/binding-linux-arm-gnueabihf": ["@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.19.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-lXZYWAC6kaGe/ky2su94e9jN9t6M0/6c+GrSlCqL//XO1cxi5lpAhnJYdyrKfm0ZEr/c7RNyAx3P7FSBcBd5+A=="],
+
+    "@oxc-resolver/binding-linux-arm-musleabihf": ["@oxc-resolver/binding-linux-arm-musleabihf@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.19.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-veG1kKsuK5+t2IsO9q0DErYVSw2azvCVvWHnfTOS73WE0STdLLB7Q1bB9WR+yHPQM76ASkFyRbogWo1GR1+WbQ=="],
+
+    "@oxc-resolver/binding-linux-arm64-gnu": ["@oxc-resolver/binding-linux-arm64-gnu@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.19.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-heV2+jmXyYnUrpUXSPugqWDRpnsQcDm2AX4wzTuvgdlZfoNYO0O3W2AVpJYaDn9AG4JdM6Kxom8+foE7/BcSig=="],
+
+    "@oxc-resolver/binding-linux-arm64-musl": ["@oxc-resolver/binding-linux-arm64-musl@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.19.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-jvo2Pjs1c9KPxMuMPIeQsgu0mOJF9rEb3y3TdpsrqwxRM+AN6/nDDwv45n5ZrUnQMsdBy5gIabioMKnQfWo9ew=="],
+
+    "@oxc-resolver/binding-linux-ppc64-gnu": ["@oxc-resolver/binding-linux-ppc64-gnu@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.19.1.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-vLmdNxWCdN7Uo5suays6A/+ywBby2PWBBPXctWPg5V0+eVuzsJxgAn6MMB4mPlshskYbppjpN2Zg83ArHze9gQ=="],
+
+    "@oxc-resolver/binding-linux-riscv64-gnu": ["@oxc-resolver/binding-linux-riscv64-gnu@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.19.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-/b+WgR+VTSBxzgOhDO7TlMXC1ufPIMR6Vj1zN+/x+MnyXGW7prTLzU9eW85Aj7Th7CCEG9ArCbTeqxCzFWdg2w=="],
+
+    "@oxc-resolver/binding-linux-riscv64-musl": ["@oxc-resolver/binding-linux-riscv64-musl@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.19.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-YlRdeWb9j42p29ROh+h4eg/OQ3dTJlpHSa+84pUM9+p6i3djtPz1q55yLJhgW9XfDch7FN1pQ/Vd6YP+xfRIuw=="],
+
+    "@oxc-resolver/binding-linux-s390x-gnu": ["@oxc-resolver/binding-linux-s390x-gnu@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.19.1.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-EDpafVOQWF8/MJynsjOGFThcqhRHy417sRyLfQmeiamJ8qVhSKAn2Dn2VVKUGCjVB9C46VGjhNo7nOPUi1x6uA=="],
+
+    "@oxc-resolver/binding-linux-x64-gnu": ["@oxc-resolver/binding-linux-x64-gnu@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.19.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-NxjZe+rqWhr+RT8/Ik+5ptA3oz7tUw361Wa5RWQXKnfqwSSHdHyrw6IdcTfYuml9dM856AlKWZIUXDmA9kkiBQ=="],
+
+    "@oxc-resolver/binding-linux-x64-musl": ["@oxc-resolver/binding-linux-x64-musl@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.19.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-cM/hQwsO3ReJg5kR+SpI69DMfvNCp+A/eVR4b4YClE5bVZwz8rh2Nh05InhwI5HR/9cArbEkzMjcKgTHS6UaNw=="],
+
+    "@oxc-resolver/binding-openharmony-arm64": ["@oxc-resolver/binding-openharmony-arm64@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.19.1.tgz", { "os": "none", "cpu": "arm64" }, "sha512-QF080IowFB0+9Rh6RcD19bdgh49BpQHUW5TajG1qvWHvmrQznTZZjYlgE2ltLXyKY+qs4F/v5xuX1XS7Is+3qA=="],
+
+    "@oxc-resolver/binding-wasm32-wasi": ["@oxc-resolver/binding-wasm32-wasi@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.19.1.tgz", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "cpu": "none" }, "sha512-w8UCKhX826cP/ZLokXDS6+milN8y4X7zidsAttEdWlVoamTNf6lhBJldaWr3ukTDiye7s4HRcuPEPOXNC432Vg=="],
+
+    "@oxc-resolver/binding-win32-arm64-msvc": ["@oxc-resolver/binding-win32-arm64-msvc@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.19.1.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-nJ4AsUVZrVKwnU/QRdzPCCrO0TrabBqgJ8pJhXITdZGYOV28TIYystV1VFLbQ7DtAcaBHpocT5/ZJnF78YJPtQ=="],
+
+    "@oxc-resolver/binding-win32-ia32-msvc": ["@oxc-resolver/binding-win32-ia32-msvc@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.19.1.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-EW+ND5q2Tl+a3pH81l1QbfgbF3HmqgwLfDfVithRFheac8OTcnbXt/JxqD2GbDkb7xYEqy1zNaVFRr3oeG8npA=="],
+
+    "@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.19.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-6hIU3RQu45B+VNTY4Ru8ppFwjVS/S5qwYyGhBotmjxfEKk41I2DlGtRfGJndZ5+6lneE2pwloqunlOyZuX/XAw=="],
+
     "@pondwader/socks5-server": ["@pondwader/socks5-server@1.0.10", "https://registry.npmmirror.com/@pondwader/socks5-server/-/socks5-server-1.0.10.tgz", {}, "sha512-bQY06wzzR8D2+vVCUoBsr5QS2U6UgPUQRmErNwtsuI6vLcyRKkafjkr3KxbtGFf9aBBIV2mcvlsKD1UYaIV+sg=="],
 
     "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "https://registry.npmmirror.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
@@ -564,6 +659,8 @@
 
     "@smithy/uuid": ["@smithy/uuid@1.1.2", "https://registry.npmmirror.com/@smithy/uuid/-/uuid-1.1.2.tgz", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g=="],
 
+    "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "https://registry.npmmirror.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
+
     "@types/bun": ["@types/bun@1.3.11", "https://registry.npmmirror.com/@types/bun/-/bun-1.3.11.tgz", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
 
     "@types/cacache": ["@types/cacache@20.0.1", "https://registry.npmmirror.com/@types/cacache/-/cacache-20.0.1.tgz", { "dependencies": { "@types/node": "*", "minipass": "*" } }, "sha512-QlKW3AFoFr/hvPHwFHMIVUH/ZCYeetBNou3PCmxu5LaNDvrtBlPJtIA6uhmU9JRt9oxj7IYoqoLcpxtzpPiTcw=="],
@@ -632,6 +729,8 @@
 
     "brace-expansion": ["brace-expansion@5.0.5", "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.5.tgz", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="],
 
+    "braces": ["braces@3.0.3", "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
+
     "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
 
     "bun-types": ["bun-types@1.3.11", "https://registry.npmmirror.com/bun-types/-/bun-types-1.3.11.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
@@ -764,18 +863,26 @@
 
     "fast-deep-equal": ["fast-deep-equal@3.1.3", "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
 
+    "fast-glob": ["fast-glob@3.3.3", "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
+
     "fast-uri": ["fast-uri@3.1.0", "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.0.tgz", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
 
     "fast-xml-builder": ["fast-xml-builder@1.1.4", "https://registry.npmmirror.com/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", { "dependencies": { "path-expression-matcher": "^1.1.3" } }, "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg=="],
 
     "fast-xml-parser": ["fast-xml-parser@5.5.8", "https://registry.npmmirror.com/fast-xml-parser/-/fast-xml-parser-5.5.8.tgz", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="],
 
+    "fastq": ["fastq@1.20.1", "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
+
+    "fd-package-json": ["fd-package-json@2.0.0", "https://registry.npmmirror.com/fd-package-json/-/fd-package-json-2.0.0.tgz", { "dependencies": { "walk-up-path": "^4.0.0" } }, "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ=="],
+
     "fetch-blob": ["fetch-blob@3.2.0", "https://registry.npmmirror.com/fetch-blob/-/fetch-blob-3.2.0.tgz", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
 
     "fflate": ["fflate@0.8.2", "https://registry.npmmirror.com/fflate/-/fflate-0.8.2.tgz", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
 
     "figures": ["figures@6.1.0", "https://registry.npmmirror.com/figures/-/figures-6.1.0.tgz", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="],
 
+    "fill-range": ["fill-range@7.1.1", "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
+
     "finalhandler": ["finalhandler@2.1.1", "https://registry.npmmirror.com/finalhandler/-/finalhandler-2.1.1.tgz", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
 
     "find-up": ["find-up@4.1.0", "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
@@ -786,6 +893,8 @@
 
     "form-data": ["form-data@4.0.5", "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
 
+    "formatly": ["formatly@0.3.0", "https://registry.npmmirror.com/formatly/-/formatly-0.3.0.tgz", { "dependencies": { "fd-package-json": "^2.0.0" }, "bin": { "formatly": "bin/index.mjs" } }, "sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w=="],
+
     "formdata-polyfill": ["formdata-polyfill@4.0.10", "https://registry.npmmirror.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
 
     "forwarded": ["forwarded@0.2.0", "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
@@ -816,8 +925,12 @@
 
     "get-stream": ["get-stream@9.0.1", "https://registry.npmmirror.com/get-stream/-/get-stream-9.0.1.tgz", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
 
+    "get-tsconfig": ["get-tsconfig@4.13.7", "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.13.7.tgz", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q=="],
+
     "glob": ["glob@13.0.6", "https://registry.npmmirror.com/glob/-/glob-13.0.6.tgz", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
 
+    "glob-parent": ["glob-parent@5.1.2", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
+
     "google-auth-library": ["google-auth-library@10.6.2", "https://registry.npmmirror.com/google-auth-library/-/google-auth-library-10.6.2.tgz", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.1.4", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" } }, "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw=="],
 
     "google-logging-utils": ["google-logging-utils@1.1.3", "https://registry.npmmirror.com/google-logging-utils/-/google-logging-utils-1.1.3.tgz", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
@@ -866,10 +979,16 @@
 
     "is-docker": ["is-docker@3.0.0", "https://registry.npmmirror.com/is-docker/-/is-docker-3.0.0.tgz", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
 
+    "is-extglob": ["is-extglob@2.1.1", "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
+
     "is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
 
+    "is-glob": ["is-glob@4.0.3", "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
+
     "is-inside-container": ["is-inside-container@1.0.0", "https://registry.npmmirror.com/is-inside-container/-/is-inside-container-1.0.0.tgz", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
 
+    "is-number": ["is-number@7.0.0", "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
+
     "is-plain-obj": ["is-plain-obj@4.1.0", "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
 
     "is-promise": ["is-promise@4.0.0", "https://registry.npmmirror.com/is-promise/-/is-promise-4.0.0.tgz", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
@@ -884,6 +1003,8 @@
 
     "isexe": ["isexe@2.0.0", "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
 
+    "jiti": ["jiti@2.6.1", "https://registry.npmmirror.com/jiti/-/jiti-2.6.1.tgz", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
+
     "jose": ["jose@6.2.2", "https://registry.npmmirror.com/jose/-/jose-6.2.2.tgz", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="],
 
     "json-bigint": ["json-bigint@1.0.0", "https://registry.npmmirror.com/json-bigint/-/json-bigint-1.0.0.tgz", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
@@ -904,6 +1025,8 @@
 
     "jws": ["jws@4.0.1", "https://registry.npmmirror.com/jws/-/jws-4.0.1.tgz", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="],
 
+    "knip": ["knip@6.1.1", "https://registry.npmmirror.com/knip/-/knip-6.1.1.tgz", { "dependencies": { "@nodelib/fs.walk": "^1.2.3", "fast-glob": "^3.3.3", "formatly": "^0.3.0", "get-tsconfig": "4.13.7", "jiti": "^2.6.0", "minimist": "^1.2.8", "oxc-parser": "^0.121.0", "oxc-resolver": "^11.19.1", "picocolors": "^1.1.1", "picomatch": "^4.0.1", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", "unbash": "^2.2.0", "yaml": "^2.8.2", "zod": "^4.1.11" }, "bin": { "knip": "bin/knip.js", "knip-bun": "bin/knip-bun.js" } }, "sha512-BC/kbdxwCgv+p/3YkGbtlLxbOXhQDuR+CeKKFEpJyKb3BFwG1gZa+CMWSqAnPi+kUexz74m327d3zWxyn2fMew=="],
+
     "locate-path": ["locate-path@5.0.0", "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
 
     "lodash-es": ["lodash-es@4.17.23", "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.23.tgz", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="],
@@ -938,12 +1061,18 @@
 
     "merge-descriptors": ["merge-descriptors@2.0.0", "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
 
+    "merge2": ["merge2@1.4.1", "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
+
+    "micromatch": ["micromatch@4.0.8", "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
+
     "mime-db": ["mime-db@1.54.0", "https://registry.npmmirror.com/mime-db/-/mime-db-1.54.0.tgz", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
 
     "mime-types": ["mime-types@3.0.2", "https://registry.npmmirror.com/mime-types/-/mime-types-3.0.2.tgz", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
 
     "minimatch": ["minimatch@10.2.5", "https://registry.npmmirror.com/minimatch/-/minimatch-10.2.5.tgz", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
 
+    "minimist": ["minimist@1.2.8", "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
+
     "minipass": ["minipass@7.1.3", "https://registry.npmmirror.com/minipass/-/minipass-7.1.3.tgz", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
 
     "minipass-collect": ["minipass-collect@2.0.1", "https://registry.npmmirror.com/minipass-collect/-/minipass-collect-2.0.1.tgz", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw=="],
@@ -982,6 +1111,10 @@
 
     "os-tmpdir": ["os-tmpdir@1.0.2", "https://registry.npmmirror.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz", {}, "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g=="],
 
+    "oxc-parser": ["oxc-parser@0.121.0", "https://registry.npmmirror.com/oxc-parser/-/oxc-parser-0.121.0.tgz", { "dependencies": { "@oxc-project/types": "^0.121.0" }, "optionalDependencies": { "@oxc-parser/binding-android-arm-eabi": "0.121.0", "@oxc-parser/binding-android-arm64": "0.121.0", "@oxc-parser/binding-darwin-arm64": "0.121.0", "@oxc-parser/binding-darwin-x64": "0.121.0", "@oxc-parser/binding-freebsd-x64": "0.121.0", "@oxc-parser/binding-linux-arm-gnueabihf": "0.121.0", "@oxc-parser/binding-linux-arm-musleabihf": "0.121.0", "@oxc-parser/binding-linux-arm64-gnu": "0.121.0", "@oxc-parser/binding-linux-arm64-musl": "0.121.0", "@oxc-parser/binding-linux-ppc64-gnu": "0.121.0", "@oxc-parser/binding-linux-riscv64-gnu": "0.121.0", "@oxc-parser/binding-linux-riscv64-musl": "0.121.0", "@oxc-parser/binding-linux-s390x-gnu": "0.121.0", "@oxc-parser/binding-linux-x64-gnu": "0.121.0", "@oxc-parser/binding-linux-x64-musl": "0.121.0", "@oxc-parser/binding-openharmony-arm64": "0.121.0", "@oxc-parser/binding-wasm32-wasi": "0.121.0", "@oxc-parser/binding-win32-arm64-msvc": "0.121.0", "@oxc-parser/binding-win32-ia32-msvc": "0.121.0", "@oxc-parser/binding-win32-x64-msvc": "0.121.0" } }, "sha512-ek9o58+SCv6AV7nchiAcUJy1DNE2CC5WRdBcO0mF+W4oRjNQfPO7b3pLjTHSFECpHkKGOZSQxx3hk8viIL5YCg=="],
+
+    "oxc-resolver": ["oxc-resolver@11.19.1", "https://registry.npmmirror.com/oxc-resolver/-/oxc-resolver-11.19.1.tgz", { "optionalDependencies": { "@oxc-resolver/binding-android-arm-eabi": "11.19.1", "@oxc-resolver/binding-android-arm64": "11.19.1", "@oxc-resolver/binding-darwin-arm64": "11.19.1", "@oxc-resolver/binding-darwin-x64": "11.19.1", "@oxc-resolver/binding-freebsd-x64": "11.19.1", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.19.1", "@oxc-resolver/binding-linux-arm-musleabihf": "11.19.1", "@oxc-resolver/binding-linux-arm64-gnu": "11.19.1", "@oxc-resolver/binding-linux-arm64-musl": "11.19.1", "@oxc-resolver/binding-linux-ppc64-gnu": "11.19.1", "@oxc-resolver/binding-linux-riscv64-gnu": "11.19.1", "@oxc-resolver/binding-linux-riscv64-musl": "11.19.1", "@oxc-resolver/binding-linux-s390x-gnu": "11.19.1", "@oxc-resolver/binding-linux-x64-gnu": "11.19.1", "@oxc-resolver/binding-linux-x64-musl": "11.19.1", "@oxc-resolver/binding-openharmony-arm64": "11.19.1", "@oxc-resolver/binding-wasm32-wasi": "11.19.1", "@oxc-resolver/binding-win32-arm64-msvc": "11.19.1", "@oxc-resolver/binding-win32-ia32-msvc": "11.19.1", "@oxc-resolver/binding-win32-x64-msvc": "11.19.1" } }, "sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg=="],
+
     "p-limit": ["p-limit@2.3.0", "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
 
     "p-locate": ["p-locate@4.1.0", "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
@@ -1008,6 +1141,8 @@
 
     "path-to-regexp": ["path-to-regexp@8.4.1", "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-8.4.1.tgz", {}, "sha512-fvU78fIjZ+SBM9YwCknCvKOUKkLVqtWDVctl0s7xIqfmfb38t2TT4ZU2gHm+Z8xGwgW+QWEU3oQSAzIbo89Ggw=="],
 
+    "picocolors": ["picocolors@1.1.1", "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
+
     "picomatch": ["picomatch@4.0.4", "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.4.tgz", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
 
     "pkce-challenge": ["pkce-challenge@5.0.1", "https://registry.npmmirror.com/pkce-challenge/-/pkce-challenge-5.0.1.tgz", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="],
@@ -1032,6 +1167,8 @@
 
     "qs": ["qs@6.15.0", "https://registry.npmmirror.com/qs/-/qs-6.15.0.tgz", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="],
 
+    "queue-microtask": ["queue-microtask@1.2.3", "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
+
     "range-parser": ["range-parser@1.2.1", "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
 
     "raw-body": ["raw-body@3.0.2", "https://registry.npmmirror.com/raw-body/-/raw-body-3.0.2.tgz", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
@@ -1050,12 +1187,18 @@
 
     "require-main-filename": ["require-main-filename@2.0.0", "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz", {}, "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="],
 
+    "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "https://registry.npmmirror.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
+
     "retry": ["retry@0.12.0", "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
 
+    "reusify": ["reusify@1.1.0", "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
+
     "router": ["router@2.2.0", "https://registry.npmmirror.com/router/-/router-2.2.0.tgz", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
 
     "run-applescript": ["run-applescript@7.1.0", "https://registry.npmmirror.com/run-applescript/-/run-applescript-7.1.0.tgz", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="],
 
+    "run-parallel": ["run-parallel@1.2.0", "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
+
     "safe-buffer": ["safe-buffer@5.2.1", "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
 
     "safer-buffer": ["safer-buffer@2.1.2", "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
@@ -1092,6 +1235,8 @@
 
     "simple-swizzle": ["simple-swizzle@0.2.4", "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.4.tgz", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="],
 
+    "smol-toml": ["smol-toml@1.6.1", "https://registry.npmmirror.com/smol-toml/-/smol-toml-1.6.1.tgz", {}, "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg=="],
+
     "ssri": ["ssri@13.0.1", "https://registry.npmmirror.com/ssri/-/ssri-13.0.1.tgz", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="],
 
     "stack-utils": ["stack-utils@2.0.6", "https://registry.npmmirror.com/stack-utils/-/stack-utils-2.0.6.tgz", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="],
@@ -1104,6 +1249,8 @@
 
     "strip-final-newline": ["strip-final-newline@4.0.0", "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-4.0.0.tgz", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="],
 
+    "strip-json-comments": ["strip-json-comments@5.0.3", "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-5.0.3.tgz", {}, "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw=="],
+
     "strnum": ["strnum@2.2.2", "https://registry.npmmirror.com/strnum/-/strnum-2.2.2.tgz", {}, "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA=="],
 
     "supports-color": ["supports-color@10.2.2", "https://registry.npmmirror.com/supports-color/-/supports-color-10.2.2.tgz", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
@@ -1118,6 +1265,8 @@
 
     "tmp": ["tmp@0.0.33", "https://registry.npmmirror.com/tmp/-/tmp-0.0.33.tgz", { "dependencies": { "os-tmpdir": "~1.0.2" } }, "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw=="],
 
+    "to-regex-range": ["to-regex-range@5.0.1", "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
+
     "toidentifier": ["toidentifier@1.0.1", "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
 
     "tr46": ["tr46@0.0.3", "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
@@ -1136,6 +1285,8 @@
 
     "typescript": ["typescript@6.0.2", "https://registry.npmmirror.com/typescript/-/typescript-6.0.2.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="],
 
+    "unbash": ["unbash@2.2.0", "https://registry.npmmirror.com/unbash/-/unbash-2.2.0.tgz", {}, "sha512-X2wH19RAPZE3+ldGicOkoj/SIA83OIxcJ6Cuaw23hf8Xc6fQpvZXY0SftE2JgS0QhYLUG4uwodSI3R53keyh7w=="],
+
     "undici": ["undici@7.24.6", "https://registry.npmmirror.com/undici/-/undici-7.24.6.tgz", {}, "sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA=="],
 
     "undici-types": ["undici-types@7.18.2", "https://registry.npmmirror.com/undici-types/-/undici-types-7.18.2.tgz", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
@@ -1160,6 +1311,8 @@
 
     "vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "https://registry.npmmirror.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="],
 
+    "walk-up-path": ["walk-up-path@4.0.0", "https://registry.npmmirror.com/walk-up-path/-/walk-up-path-4.0.0.tgz", {}, "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A=="],
+
     "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "https://registry.npmmirror.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
 
     "webidl-conversions": ["webidl-conversions@3.0.1", "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
@@ -1502,6 +1655,8 @@
 
     "image-processor-napi/sharp": ["sharp@0.33.5", "https://registry.npmmirror.com/sharp/-/sharp-0.33.5.tgz", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="],
 
+    "micromatch/picomatch": ["picomatch@2.3.2", "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
+
     "minipass-flush/minipass": ["minipass@3.3.6", "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
 
     "minipass-pipeline/minipass": ["minipass@3.3.6", "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],

+ 3 - 0
bunfig.toml

@@ -0,0 +1,3 @@
+[test]
+root = "."
+timeout = 10000

+ 22 - 0
knip.json

@@ -0,0 +1,22 @@
+{
+	"$schema": "https://unpkg.com/knip@6/schema.json",
+	"entry": ["src/entrypoints/cli.tsx"],
+	"project": ["src/**/*.{ts,tsx}"],
+	"ignore": ["src/types/**", "src/**/*.d.ts"],
+	"ignoreDependencies": [
+		"@ant/*",
+		"react-compiler-runtime",
+		"@anthropic-ai/mcpb",
+		"@anthropic-ai/sandbox-runtime"
+	],
+	"ignoreBinaries": ["bun"],
+	"workspaces": {
+		"packages/*": {
+			"entry": ["src/index.ts"],
+			"project": ["src/**/*.ts"]
+		},
+		"packages/@ant/*": {
+			"ignore": ["**"]
+		}
+	}
+}

+ 9 - 1
package.json

@@ -12,7 +12,14 @@
     "scripts": {
         "build": "bun build src/entrypoints/cli.tsx --outdir dist --target bun",
         "dev": "bun run src/entrypoints/cli.tsx",
-        "prepublishOnly": "bun run build"
+        "prepublishOnly": "bun run build",
+        "lint": "biome lint src/",
+        "lint:fix": "biome lint --fix src/",
+        "format": "biome format --write src/",
+        "prepare": "git config core.hooksPath .githooks",
+        "test": "bun test",
+        "check:unused": "knip-bun",
+        "health": "bun run scripts/health-check.ts"
     },
     "dependencies": {
         "@alcalzone/ansi-tokenize": "^0.3.0",
@@ -127,6 +134,7 @@
         "@types/react-reconciler": "^0.33.0",
         "@types/sharp": "^0.32.0",
         "@types/turndown": "^5.0.6",
+        "knip": "^6.1.1",
         "typescript": "^6.0.2"
     }
 }

+ 0 - 1
packages/@ant/computer-use-input/package.json

@@ -2,7 +2,6 @@
     "name": "@ant/computer-use-input",
     "version": "1.0.0",
     "private": true,
-    "type": "module",
     "main": "./src/index.ts",
     "types": "./src/index.ts"
 }

+ 162 - 23
packages/@ant/computer-use-input/src/index.ts

@@ -1,39 +1,174 @@
+/**
+ * @ant/computer-use-input — macOS 键鼠模拟实现
+ *
+ * 使用 macOS 原生工具实现:
+ * - AppleScript (osascript) — 应用信息、键盘输入
+ * - CGEvent via AppleScript-ObjC bridge — 鼠标操作、位置查询
+ *
+ * 仅 macOS 支持。其他平台返回 { isSupported: false }
+ */
+
+import { $ } from 'bun'
+
 interface FrontmostAppInfo {
   bundleId: string
   appName: string
 }
 
-export class ComputerUseInputAPI {
-  declare moveMouse: (
-    x: number,
-    y: number,
-    animated: boolean,
-  ) => Promise<void>
+// AppleScript key code mapping
+const KEY_MAP: Record<string, number> = {
+  return: 36, enter: 36, tab: 48, space: 49, delete: 51, backspace: 51,
+  escape: 53, esc: 53,
+  left: 123, right: 124, down: 125, up: 126,
+  f1: 122, f2: 120, f3: 99, f4: 118, f5: 96, f6: 97,
+  f7: 98, f8: 100, f9: 101, f10: 109, f11: 103, f12: 111,
+  home: 115, end: 119, pageup: 116, pagedown: 121,
+}
 
-  declare key: (
-    key: string,
-    action: 'press' | 'release',
-  ) => Promise<void>
+const MODIFIER_MAP: Record<string, string> = {
+  command: 'command down', cmd: 'command down', meta: 'command down', super: 'command down',
+  shift: 'shift down',
+  option: 'option down', alt: 'option down',
+  control: 'control down', ctrl: 'control down',
+}
 
-  declare keys: (parts: string[]) => Promise<void>
+async function osascript(script: string): Promise<string> {
+  const result = await $`osascript -e ${script}`.quiet().nothrow().text()
+  return result.trim()
+}
 
-  declare mouseLocation: () => Promise<{ x: number; y: number }>
+async function jxa(script: string): Promise<string> {
+  const result = await $`osascript -l JavaScript -e ${script}`.quiet().nothrow().text()
+  return result.trim()
+}
 
-  declare mouseButton: (
-    button: 'left' | 'right' | 'middle',
-    action: 'click' | 'press' | 'release',
-    count?: number,
-  ) => Promise<void>
+function jxaSync(script: string): string {
+  const result = Bun.spawnSync({
+    cmd: ['osascript', '-l', 'JavaScript', '-e', script],
+    stdout: 'pipe', stderr: 'pipe',
+  })
+  return new TextDecoder().decode(result.stdout).trim()
+}
 
-  declare mouseScroll: (
-    amount: number,
-    direction: 'vertical' | 'horizontal',
-  ) => Promise<void>
+function buildMouseJxa(eventType: string, x: number, y: number, btn: number, clickState?: number): string {
+  let script = `ObjC.import("CoreGraphics"); var p = $.CGPointMake(${x},${y}); var e = $.CGEventCreateMouseEvent(null, $.${eventType}, p, ${btn});`
+  if (clickState !== undefined) {
+    script += ` $.CGEventSetIntegerValueField(e, $.kCGMouseEventClickState, ${clickState});`
+  }
+  script += ` $.CGEventPost($.kCGHIDEventTap, e);`
+  return script
+}
 
-  declare typeText: (text: string) => Promise<void>
+// ---- Implementation functions ----
 
-  declare getFrontmostAppInfo: () => FrontmostAppInfo | null
+async function moveMouse(x: number, y: number, _animated: boolean): Promise<void> {
+  await jxa(buildMouseJxa('kCGEventMouseMoved', x, y, 0))
+}
+
+async function key(keyName: string, action: 'press' | 'release'): Promise<void> {
+  if (action === 'release') return
+  const lower = keyName.toLowerCase()
+  const keyCode = KEY_MAP[lower]
+  if (keyCode !== undefined) {
+    await osascript(`tell application "System Events" to key code ${keyCode}`)
+  } else {
+    await osascript(`tell application "System Events" to keystroke "${keyName.length === 1 ? keyName : lower}"`)
+  }
+}
+
+async function keys(parts: string[]): Promise<void> {
+  const modifiers: string[] = []
+  let finalKey: string | null = null
+  for (const part of parts) {
+    const mod = MODIFIER_MAP[part.toLowerCase()]
+    if (mod) modifiers.push(mod)
+    else finalKey = part
+  }
+  if (!finalKey) return
+  const lower = finalKey.toLowerCase()
+  const keyCode = KEY_MAP[lower]
+  const modStr = modifiers.length > 0 ? ` using {${modifiers.join(', ')}}` : ''
+  if (keyCode !== undefined) {
+    await osascript(`tell application "System Events" to key code ${keyCode}${modStr}`)
+  } else {
+    await osascript(`tell application "System Events" to keystroke "${finalKey.length === 1 ? finalKey : lower}"${modStr}`)
+  }
+}
 
+async function mouseLocation(): Promise<{ x: number; y: number }> {
+  const result = await jxa('ObjC.import("CoreGraphics"); var e = $.CGEventCreate(null); var p = $.CGEventGetLocation(e); p.x + "," + p.y')
+  const [xStr, yStr] = result.split(',')
+  return { x: Math.round(Number(xStr)), y: Math.round(Number(yStr)) }
+}
+
+async function mouseButton(
+  button: 'left' | 'right' | 'middle',
+  action: 'click' | 'press' | 'release',
+  count?: number,
+): Promise<void> {
+  const pos = await mouseLocation()
+  const btn = button === 'left' ? 0 : button === 'right' ? 1 : 2
+  const downType = btn === 0 ? 'kCGEventLeftMouseDown' : btn === 1 ? 'kCGEventRightMouseDown' : 'kCGEventOtherMouseDown'
+  const upType = btn === 0 ? 'kCGEventLeftMouseUp' : btn === 1 ? 'kCGEventRightMouseUp' : 'kCGEventOtherMouseUp'
+
+  if (action === 'click') {
+    for (let i = 0; i < (count ?? 1); i++) {
+      await jxa(buildMouseJxa(downType, pos.x, pos.y, btn, i + 1))
+      await jxa(buildMouseJxa(upType, pos.x, pos.y, btn, i + 1))
+    }
+  } else if (action === 'press') {
+    await jxa(buildMouseJxa(downType, pos.x, pos.y, btn))
+  } else {
+    await jxa(buildMouseJxa(upType, pos.x, pos.y, btn))
+  }
+}
+
+async function mouseScroll(amount: number, direction: 'vertical' | 'horizontal'): Promise<void> {
+  const script = direction === 'vertical'
+    ? `ObjC.import("CoreGraphics"); var e = $.CGEventCreateScrollWheelEvent(null, 0, 1, ${amount}); $.CGEventPost($.kCGHIDEventTap, e);`
+    : `ObjC.import("CoreGraphics"); var e = $.CGEventCreateScrollWheelEvent(null, 0, 2, 0, ${amount}); $.CGEventPost($.kCGHIDEventTap, e);`
+  await jxa(script)
+}
+
+async function typeText(text: string): Promise<void> {
+  const escaped = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
+  await osascript(`tell application "System Events" to keystroke "${escaped}"`)
+}
+
+function getFrontmostAppInfo(): FrontmostAppInfo | null {
+  try {
+    const result = Bun.spawnSync({
+      cmd: ['osascript', '-e', `
+        tell application "System Events"
+          set frontApp to first application process whose frontmost is true
+          set appName to name of frontApp
+          set bundleId to bundle identifier of frontApp
+          return bundleId & "|" & appName
+        end tell
+      `],
+      stdout: 'pipe',
+      stderr: 'pipe',
+    })
+    const output = new TextDecoder().decode(result.stdout).trim()
+    if (!output || !output.includes('|')) return null
+    const [bundleId, appName] = output.split('|', 2)
+    return { bundleId: bundleId!, appName: appName! }
+  } catch {
+    return null
+  }
+}
+
+// ---- Exports ----
+
+export class ComputerUseInputAPI {
+  declare moveMouse: (x: number, y: number, animated: boolean) => Promise<void>
+  declare key: (key: string, action: 'press' | 'release') => Promise<void>
+  declare keys: (parts: string[]) => Promise<void>
+  declare mouseLocation: () => Promise<{ x: number; y: number }>
+  declare mouseButton: (button: 'left' | 'right' | 'middle', action: 'click' | 'press' | 'release', count?: number) => Promise<void>
+  declare mouseScroll: (amount: number, direction: 'vertical' | 'horizontal') => Promise<void>
+  declare typeText: (text: string) => Promise<void>
+  declare getFrontmostAppInfo: () => FrontmostAppInfo | null
   declare isSupported: true
 }
 
@@ -42,3 +177,7 @@ interface ComputerUseInputUnsupported {
 }
 
 export type ComputerUseInput = ComputerUseInputAPI | ComputerUseInputUnsupported
+
+// Plain object with all methods as own properties — compatible with require()
+export const isSupported = process.platform === 'darwin'
+export { moveMouse, key, keys, mouseLocation, mouseButton, mouseScroll, typeText, getFrontmostAppInfo }

+ 156 - 23
packages/@ant/computer-use-mcp/src/index.ts

@@ -1,30 +1,163 @@
-export const API_RESIZE_PARAMS: any = {}
-
-export class ComputerExecutor {}
-
-export type ComputerUseSessionContext = any
-export type CuCallToolResult = any
-export type CuPermissionRequest = any
-export type CuPermissionResponse = any
-export const DEFAULT_GRANT_FLAGS: any = {}
-export type DisplayGeometry = any
-export type FrontmostApp = any
-export type InstalledApp = any
-export type ResolvePrepareCaptureResult = any
-export type RunningApp = any
-export type ScreenshotDims = any
-export type ScreenshotResult = any
-
-export function bindSessionContext(..._args: any[]): any {
-  return null
+/**
+ * @ant/computer-use-mcp — Stub 实现
+ *
+ * 提供类型安全的 stub,所有函数返回合理的默认值。
+ * 在 feature('CHICAGO_MCP') = false 时不会被实际调用,
+ * 但确保 import 不报错且类型正确。
+ */
+
+import type {
+  ComputerUseHostAdapter,
+  CoordinateMode,
+  GrantFlags,
+  Logger,
+} from './types'
+
+// Re-export types from types.ts
+export type { CoordinateMode, Logger } from './types'
+export type {
+  ComputerUseConfig,
+  ComputerUseHostAdapter,
+  CuPermissionRequest,
+  CuPermissionResponse,
+  CuSubGates,
+} from './types'
+export { DEFAULT_GRANT_FLAGS } from './types'
+
+// ---------------------------------------------------------------------------
+// Types (defined here for callers that import from the main entry)
+// ---------------------------------------------------------------------------
+
+export interface DisplayGeometry {
+  width: number
+  height: number
+  displayId?: number
+  originX?: number
+  originY?: number
+}
+
+export interface FrontmostApp {
+  bundleId: string
+  displayName: string
+}
+
+export interface InstalledApp {
+  bundleId: string
+  displayName: string
+  path: string
 }
 
-export function buildComputerUseTools(..._args: any[]): any[] {
+export interface RunningApp {
+  bundleId: string
+  displayName: string
+}
+
+export interface ScreenshotResult {
+  base64: string
+  width: number
+  height: number
+}
+
+export type ResolvePrepareCaptureResult = ScreenshotResult
+
+export interface ScreenshotDims {
+  width: number
+  height: number
+  displayWidth: number
+  displayHeight: number
+  displayId: number
+  originX: number
+  originY: number
+}
+
+export interface CuCallToolResultContent {
+  type: 'image' | 'text'
+  data?: string
+  mimeType?: string
+  text?: string
+}
+
+export interface CuCallToolResult {
+  content: CuCallToolResultContent[]
+  telemetry: {
+    error_kind?: string
+    [key: string]: unknown
+  }
+}
+
+export type ComputerUseSessionContext = Record<string, unknown>
+
+// ---------------------------------------------------------------------------
+// API_RESIZE_PARAMS — 默认的截图缩放参数
+// ---------------------------------------------------------------------------
+
+export const API_RESIZE_PARAMS = {
+  maxWidth: 1280,
+  maxHeight: 800,
+  maxPixels: 1280 * 800,
+}
+
+// ---------------------------------------------------------------------------
+// ComputerExecutor — stub class
+// ---------------------------------------------------------------------------
+
+export class ComputerExecutor {
+  capabilities: Record<string, boolean> = {}
+}
+
+// ---------------------------------------------------------------------------
+// Functions — 返回合理默认值的 stub
+// ---------------------------------------------------------------------------
+
+/**
+ * 计算目标截图尺寸。
+ * 在物理宽高和 API 限制之间取最优尺寸。
+ */
+export function targetImageSize(
+  physW: number,
+  physH: number,
+  _params?: typeof API_RESIZE_PARAMS,
+): [number, number] {
+  const maxW = _params?.maxWidth ?? 1280
+  const maxH = _params?.maxHeight ?? 800
+  const scale = Math.min(1, maxW / physW, maxH / physH)
+  return [Math.round(physW * scale), Math.round(physH * scale)]
+}
+
+/**
+ * 绑定会话上下文,返回工具调度函数。
+ * Stub 返回一个始终返回空结果的调度器。
+ */
+export function bindSessionContext(
+  _adapter: ComputerUseHostAdapter,
+  _coordinateMode: CoordinateMode,
+  _ctx: ComputerUseSessionContext,
+): (name: string, args: unknown) => Promise<CuCallToolResult> {
+  return async (_name: string, _args: unknown) => ({
+    content: [],
+    telemetry: {},
+  })
+}
+
+/**
+ * 构建 Computer Use 工具定义列表。
+ * Stub 返回空数组(无工具)。
+ */
+export function buildComputerUseTools(
+  _capabilities?: Record<string, boolean>,
+  _coordinateMode?: CoordinateMode,
+  _installedAppNames?: string[],
+): Array<{ name: string; description: string; inputSchema: Record<string, unknown> }> {
   return []
 }
 
-export function createComputerUseMcpServer(..._args: any[]): any {
+/**
+ * 创建 Computer Use MCP server。
+ * Stub 返回 null(服务未启用)。
+ */
+export function createComputerUseMcpServer(
+  _adapter?: ComputerUseHostAdapter,
+  _coordinateMode?: CoordinateMode,
+): null {
   return null
 }
-
-export const targetImageSize: any = null

+ 30 - 3
packages/@ant/computer-use-mcp/src/sentinelApps.ts

@@ -1,5 +1,32 @@
-export const sentinelApps: string[] = []
+/**
+ * Sentinel apps — 需要特殊权限警告的应用列表
+ *
+ * 包含终端、文件管理器、系统设置等敏感应用。
+ * Computer Use 操作这些应用时会显示额外警告。
+ */
 
-export function getSentinelCategory(_appName: string): string | null {
-  return null
+type SentinelCategory = 'shell' | 'filesystem' | 'system_settings'
+
+const SENTINEL_MAP: Record<string, SentinelCategory> = {
+  // Shell / Terminal
+  'com.apple.Terminal': 'shell',
+  'com.googlecode.iterm2': 'shell',
+  'dev.warp.Warp-Stable': 'shell',
+  'io.alacritty': 'shell',
+  'com.github.wez.wezterm': 'shell',
+  'net.kovidgoyal.kitty': 'shell',
+  'co.zeit.hyper': 'shell',
+
+  // Filesystem
+  'com.apple.finder': 'filesystem',
+
+  // System Settings
+  'com.apple.systempreferences': 'system_settings',
+  'com.apple.SystemPreferences': 'system_settings',
+}
+
+export const sentinelApps: string[] = Object.keys(SENTINEL_MAP)
+
+export function getSentinelCategory(bundleId: string): SentinelCategory | null {
+  return SENTINEL_MAP[bundleId] ?? null
 }

+ 70 - 8
packages/@ant/computer-use-mcp/src/types.ts

@@ -1,8 +1,70 @@
-export type ComputerUseConfig = any
-export type ComputerUseHostAdapter = any
-export type CoordinateMode = any
-export type CuPermissionRequest = any
-export type CuPermissionResponse = any
-export type CuSubGates = any
-export const DEFAULT_GRANT_FLAGS: any = {}
-export type Logger = any
+/**
+ * @ant/computer-use-mcp — Types
+ *
+ * 从调用侧反推的真实类型定义,替代 any stub。
+ */
+
+export type CoordinateMode = 'pixels' | 'normalized'
+
+export interface CuSubGates {
+  pixelValidation: boolean
+  clipboardPasteMultiline: boolean
+  mouseAnimation: boolean
+  hideBeforeAction: boolean
+  autoTargetDisplay: boolean
+  clipboardGuard: boolean
+}
+
+export interface Logger {
+  silly(message: string, ...args: unknown[]): void
+  debug(message: string, ...args: unknown[]): void
+  info(message: string, ...args: unknown[]): void
+  warn(message: string, ...args: unknown[]): void
+  error(message: string, ...args: unknown[]): void
+}
+
+export interface CuPermissionRequest {
+  apps: Array<{ bundleId: string; displayName: string }>
+  requestedFlags: GrantFlags
+  reason: string
+  tccState: { accessibility: boolean; screenRecording: boolean }
+  willHide: string[]
+}
+
+export interface GrantFlags {
+  clipboardRead: boolean
+  clipboardWrite: boolean
+  systemKeyCombos: boolean
+}
+
+export interface CuPermissionResponse {
+  granted: string[]
+  denied: string[]
+  flags: GrantFlags
+}
+
+export const DEFAULT_GRANT_FLAGS: GrantFlags = {
+  clipboardRead: false,
+  clipboardWrite: false,
+  systemKeyCombos: false,
+}
+
+export interface ComputerUseConfig {
+  coordinateMode: CoordinateMode
+  enabledTools: string[]
+}
+
+export interface ComputerUseHostAdapter {
+  serverName: string
+  logger: Logger
+  executor: ComputerExecutor
+  ensureOsPermissions(): Promise<{ granted: true } | { granted: false; accessibility: boolean; screenRecording: boolean }>
+  isDisabled(): boolean
+  getSubGates(): CuSubGates
+  getAutoUnhideEnabled(): boolean
+  cropRawPatch?(base64: string, x: number, y: number, w: number, h: number): Promise<string>
+}
+
+export interface ComputerExecutor {
+  capabilities: Record<string, boolean>
+}

+ 324 - 48
packages/@ant/computer-use-swift/src/index.ts

@@ -1,66 +1,194 @@
-interface DisplayGeometry {
+/**
+ * @ant/computer-use-swift — macOS 实现
+ *
+ * 用 AppleScript/JXA/screencapture 替代原始 Swift 原生模块。
+ * 提供显示器信息、应用管理、截图等功能。
+ *
+ * 仅 macOS 支持。
+ */
+
+import { readFileSync, unlinkSync } from 'fs'
+import { tmpdir } from 'os'
+import { join } from 'path'
+
+// ---------------------------------------------------------------------------
+// Types (exported for callers)
+// ---------------------------------------------------------------------------
+
+export interface DisplayGeometry {
   width: number
   height: number
   scaleFactor: number
   displayId: number
 }
 
-interface PrepareDisplayResult {
+export interface PrepareDisplayResult {
   activated: string
   hidden: string[]
 }
 
-interface AppInfo {
+export interface AppInfo {
   bundleId: string
   displayName: string
 }
 
-interface InstalledApp {
+export interface InstalledApp {
   bundleId: string
   displayName: string
   path: string
   iconDataUrl?: string
 }
 
-interface RunningApp {
+export interface RunningApp {
   bundleId: string
   displayName: string
 }
 
-interface ScreenshotResult {
+export interface ScreenshotResult {
   base64: string
   width: number
   height: number
 }
 
-interface ResolvePrepareCaptureResult {
+export interface ResolvePrepareCaptureResult {
   base64: string
   width: number
   height: number
 }
 
-interface WindowDisplayInfo {
+export interface WindowDisplayInfo {
   bundleId: string
   displayIds: number[]
 }
 
+// ---------------------------------------------------------------------------
+// Helpers
+// ---------------------------------------------------------------------------
+
+function jxaSync(script: string): string {
+  const result = Bun.spawnSync({
+    cmd: ['osascript', '-l', 'JavaScript', '-e', script],
+    stdout: 'pipe', stderr: 'pipe',
+  })
+  return new TextDecoder().decode(result.stdout).trim()
+}
+
+function osascriptSync(script: string): string {
+  const result = Bun.spawnSync({
+    cmd: ['osascript', '-e', script],
+    stdout: 'pipe', stderr: 'pipe',
+  })
+  return new TextDecoder().decode(result.stdout).trim()
+}
+
+async function osascript(script: string): Promise<string> {
+  const proc = Bun.spawn(['osascript', '-e', script], {
+    stdout: 'pipe', stderr: 'pipe',
+  })
+  const text = await new Response(proc.stdout).text()
+  await proc.exited
+  return text.trim()
+}
+
+async function jxa(script: string): Promise<string> {
+  const proc = Bun.spawn(['osascript', '-l', 'JavaScript', '-e', script], {
+    stdout: 'pipe', stderr: 'pipe',
+  })
+  const text = await new Response(proc.stdout).text()
+  await proc.exited
+  return text.trim()
+}
+
+// ---------------------------------------------------------------------------
+// DisplayAPI
+// ---------------------------------------------------------------------------
+
+interface DisplayAPI {
+  getSize(displayId?: number): DisplayGeometry
+  listAll(): DisplayGeometry[]
+}
+
+const displayAPI: DisplayAPI = {
+  getSize(displayId?: number): DisplayGeometry {
+    const all = this.listAll()
+    if (displayId !== undefined) {
+      const found = all.find(d => d.displayId === displayId)
+      if (found) return found
+    }
+    return all[0] ?? { width: 1920, height: 1080, scaleFactor: 2, displayId: 1 }
+  },
+
+  listAll(): DisplayGeometry[] {
+    try {
+      const raw = jxaSync(`
+        ObjC.import("CoreGraphics");
+        var displays = $.CGDisplayCopyAllDisplayModes ? [] : [];
+        var active = $.CGGetActiveDisplayList(10, null, Ref());
+        var countRef = Ref();
+        $.CGGetActiveDisplayList(0, null, countRef);
+        var count = countRef[0];
+        var idBuf = Ref();
+        $.CGGetActiveDisplayList(count, idBuf, countRef);
+        var result = [];
+        for (var i = 0; i < count; i++) {
+          var did = idBuf[i];
+          var w = $.CGDisplayPixelsWide(did);
+          var h = $.CGDisplayPixelsHigh(did);
+          var mode = $.CGDisplayCopyDisplayMode(did);
+          var pw = $.CGDisplayModeGetPixelWidth(mode);
+          var sf = pw > 0 && w > 0 ? pw / w : 2;
+          result.push({width: w, height: h, scaleFactor: sf, displayId: did});
+        }
+        JSON.stringify(result);
+      `)
+      return (JSON.parse(raw) as DisplayGeometry[]).map(d => ({
+        width: Number(d.width), height: Number(d.height),
+        scaleFactor: Number(d.scaleFactor), displayId: Number(d.displayId),
+      }))
+    } catch {
+      // Fallback: use NSScreen via JXA
+      try {
+        const raw = jxaSync(`
+          ObjC.import("AppKit");
+          var screens = $.NSScreen.screens;
+          var result = [];
+          for (var i = 0; i < screens.count; i++) {
+            var s = screens.objectAtIndex(i);
+            var frame = s.frame;
+            var desc = s.deviceDescription;
+            var screenNumber = desc.objectForKey($("NSScreenNumber")).intValue;
+            var backingFactor = s.backingScaleFactor;
+            result.push({
+              width: Math.round(frame.size.width),
+              height: Math.round(frame.size.height),
+              scaleFactor: backingFactor,
+              displayId: screenNumber
+            });
+          }
+          JSON.stringify(result);
+        `)
+        return (JSON.parse(raw) as DisplayGeometry[]).map(d => ({
+          width: Number(d.width),
+          height: Number(d.height),
+          scaleFactor: Number(d.scaleFactor),
+          displayId: Number(d.displayId),
+        }))
+      } catch {
+        return [{ width: 1920, height: 1080, scaleFactor: 2, displayId: 1 }]
+      }
+    }
+  },
+}
+
+// ---------------------------------------------------------------------------
+// AppsAPI
+// ---------------------------------------------------------------------------
+
 interface AppsAPI {
-  prepareDisplay(
-    allowlistBundleIds: string[],
-    surrogateHost: string,
-    displayId?: number,
-  ): Promise<PrepareDisplayResult>
-  previewHideSet(
-    bundleIds: string[],
-    displayId?: number,
-  ): Promise<Array<AppInfo>>
-  findWindowDisplays(
-    bundleIds: string[],
-  ): Promise<Array<WindowDisplayInfo>>
-  appUnderPoint(
-    x: number,
-    y: number,
-  ): Promise<AppInfo | null>
+  prepareDisplay(allowlistBundleIds: string[], surrogateHost: string, displayId?: number): Promise<PrepareDisplayResult>
+  previewHideSet(bundleIds: string[], displayId?: number): Promise<AppInfo[]>
+  findWindowDisplays(bundleIds: string[]): Promise<WindowDisplayInfo[]>
+  appUnderPoint(x: number, y: number): Promise<AppInfo | null>
   listInstalled(): Promise<InstalledApp[]>
   iconDataUrl(path: string): string | null
   listRunning(): RunningApp[]
@@ -68,45 +196,193 @@ interface AppsAPI {
   unhide(bundleIds: string[]): Promise<void>
 }
 
-interface DisplayAPI {
-  getSize(displayId?: number): DisplayGeometry
-  listAll(): DisplayGeometry[]
+const appsAPI: AppsAPI = {
+  async prepareDisplay(
+    _allowlistBundleIds: string[],
+    _surrogateHost: string,
+    _displayId?: number,
+  ): Promise<PrepareDisplayResult> {
+    return { activated: '', hidden: [] }
+  },
+
+  async previewHideSet(
+    _bundleIds: string[],
+    _displayId?: number,
+  ): Promise<AppInfo[]> {
+    return []
+  },
+
+  async findWindowDisplays(bundleIds: string[]): Promise<WindowDisplayInfo[]> {
+    // Each running app is assumed to be on display 1
+    return bundleIds.map(bundleId => ({ bundleId, displayIds: [1] }))
+  },
+
+  async appUnderPoint(_x: number, _y: number): Promise<AppInfo | null> {
+    // Use JXA to find app at mouse position via accessibility
+    try {
+      const result = await jxa(`
+        ObjC.import("CoreGraphics");
+        ObjC.import("AppKit");
+        var pt = $.CGPointMake(${_x}, ${_y});
+        // Get frontmost app as a fallback
+        var app = $.NSWorkspace.sharedWorkspace.frontmostApplication;
+        JSON.stringify({bundleId: app.bundleIdentifier.js, displayName: app.localizedName.js});
+      `)
+      return JSON.parse(result)
+    } catch {
+      return null
+    }
+  },
+
+  async listInstalled(): Promise<InstalledApp[]> {
+    try {
+      const result = await osascript(`
+        tell application "System Events"
+          set appList to ""
+          repeat with appFile in (every file of folder "Applications" of startup disk whose name ends with ".app")
+            set appPath to POSIX path of (appFile as alias)
+            set appName to name of appFile
+            set appList to appList & appPath & "|" & appName & "\\n"
+          end repeat
+          return appList
+        end tell
+      `)
+      return result.split('\n').filter(Boolean).map(line => {
+        const [path, name] = line.split('|', 2)
+        // Derive bundleId from Info.plist would be ideal, but use path-based fallback
+        const displayName = (name ?? '').replace(/\.app$/, '')
+        return {
+          bundleId: `com.app.${displayName.toLowerCase().replace(/\s+/g, '-')}`,
+          displayName,
+          path: path ?? '',
+        }
+      })
+    } catch {
+      return []
+    }
+  },
+
+  iconDataUrl(_path: string): string | null {
+    return null
+  },
+
+  listRunning(): RunningApp[] {
+    try {
+      const raw = jxaSync(`
+        var apps = Application("System Events").applicationProcesses.whose({backgroundOnly: false});
+        var result = [];
+        for (var i = 0; i < apps.length; i++) {
+          try {
+            var a = apps[i];
+            result.push({bundleId: a.bundleIdentifier(), displayName: a.name()});
+          } catch(e) {}
+        }
+        JSON.stringify(result);
+      `)
+      return JSON.parse(raw)
+    } catch {
+      return []
+    }
+  },
+
+  async open(bundleId: string): Promise<void> {
+    await osascript(`tell application id "${bundleId}" to activate`)
+  },
+
+  async unhide(bundleIds: string[]): Promise<void> {
+    for (const bundleId of bundleIds) {
+      await osascript(`
+        tell application "System Events"
+          set visible of application process (name of application process whose bundle identifier is "${bundleId}") to true
+        end tell
+      `)
+    }
+  },
 }
 
+// ---------------------------------------------------------------------------
+// ScreenshotAPI
+// ---------------------------------------------------------------------------
+
 interface ScreenshotAPI {
   captureExcluding(
-    allowedBundleIds: string[],
-    quality: number,
-    targetW: number,
-    targetH: number,
-    displayId?: number,
+    allowedBundleIds: string[], quality: number,
+    targetW: number, targetH: number, displayId?: number,
   ): Promise<ScreenshotResult>
   captureRegion(
     allowedBundleIds: string[],
-    x: number,
-    y: number,
-    w: number,
-    h: number,
-    outW: number,
-    outH: number,
-    quality: number,
-    displayId?: number,
+    x: number, y: number, w: number, h: number,
+    outW: number, outH: number, quality: number, displayId?: number,
   ): Promise<ScreenshotResult>
 }
 
+async function captureScreenToBase64(args: string[]): Promise<{ base64: string; width: number; height: number }> {
+  const tmpFile = join(tmpdir(), `cu-screenshot-${Date.now()}.png`)
+  const proc = Bun.spawn(['screencapture', ...args, tmpFile], {
+    stdout: 'pipe', stderr: 'pipe',
+  })
+  await proc.exited
+
+  try {
+    const buf = readFileSync(tmpFile)
+    const base64 = buf.toString('base64')
+    // Parse PNG header for dimensions (bytes 16-23)
+    const width = buf.readUInt32BE(16)
+    const height = buf.readUInt32BE(20)
+    return { base64, width, height }
+  } finally {
+    try { unlinkSync(tmpFile) } catch {}
+  }
+}
+
+const screenshotAPI: ScreenshotAPI = {
+  async captureExcluding(
+    _allowedBundleIds: string[],
+    _quality: number,
+    _targetW: number,
+    _targetH: number,
+    displayId?: number,
+  ): Promise<ScreenshotResult> {
+    const args = ['-x'] // silent
+    if (displayId !== undefined) {
+      args.push('-D', String(displayId))
+    }
+    return captureScreenToBase64(args)
+  },
+
+  async captureRegion(
+    _allowedBundleIds: string[],
+    x: number, y: number, w: number, h: number,
+    _outW: number, _outH: number, _quality: number,
+    displayId?: number,
+  ): Promise<ScreenshotResult> {
+    const args = ['-x', '-R', `${x},${y},${w},${h}`]
+    if (displayId !== undefined) {
+      args.push('-D', String(displayId))
+    }
+    return captureScreenToBase64(args)
+  },
+}
+
+// ---------------------------------------------------------------------------
+// ComputerUseAPI — Main export
+// ---------------------------------------------------------------------------
+
 export class ComputerUseAPI {
-  declare apps: AppsAPI
-  declare display: DisplayAPI
-  declare screenshot: ScreenshotAPI
+  apps: AppsAPI = appsAPI
+  display: DisplayAPI = displayAPI
+  screenshot: ScreenshotAPI = screenshotAPI
 
-  declare resolvePrepareCapture: (
+  async resolvePrepareCapture(
     allowedBundleIds: string[],
-    surrogateHost: string,
+    _surrogateHost: string,
     quality: number,
     targetW: number,
     targetH: number,
     displayId?: number,
-    autoResolve?: boolean,
-    doHide?: boolean,
-  ) => Promise<ResolvePrepareCaptureResult>
+    _autoResolve?: boolean,
+    _doHide?: boolean,
+  ): Promise<ResolvePrepareCaptureResult> {
+    return this.screenshot.captureExcluding(allowedBundleIds, quality, targetW, targetH, displayId)
+  }
 }

+ 102 - 0
packages/color-diff-napi/src/__tests__/color-diff.test.ts

@@ -0,0 +1,102 @@
+import { describe, expect, test } from "bun:test";
+import { __test } from "../index";
+
+const { ansi256FromRgb, colorToEscape, detectColorMode, detectLanguage, tokenize } = __test;
+
+describe("ansi256FromRgb", () => {
+	test("black maps to index 16", () => {
+		expect(ansi256FromRgb(0, 0, 0)).toBe(16);
+	});
+
+	test("pure red maps to cube red", () => {
+		expect(ansi256FromRgb(255, 0, 0)).toBe(196);
+	});
+
+	test("pure green maps to cube green", () => {
+		expect(ansi256FromRgb(0, 255, 0)).toBe(46);
+	});
+
+	test("pure blue maps to cube blue", () => {
+		expect(ansi256FromRgb(0, 0, 255)).toBe(21);
+	});
+
+	test("grey values map to grey ramp", () => {
+		const idx = ansi256FromRgb(128, 128, 128);
+		// Should be in the grey ramp range (232-255)
+		expect(idx).toBeGreaterThanOrEqual(232);
+		expect(idx).toBeLessThanOrEqual(255);
+	});
+});
+
+describe("colorToEscape", () => {
+	test("palette index < 8 uses standard ANSI codes", () => {
+		const color = { r: 1, g: 0, b: 0, a: 0 }; // palette index 1
+		expect(colorToEscape(color, true, "truecolor")).toBe("\x1b[31m"); // fg red
+		expect(colorToEscape(color, false, "truecolor")).toBe("\x1b[41m"); // bg red
+	});
+
+	test("palette index 8-15 uses bright ANSI codes", () => {
+		const color = { r: 9, g: 0, b: 0, a: 0 }; // bright red
+		expect(colorToEscape(color, true, "truecolor")).toBe("\x1b[91m");
+	});
+
+	test("alpha=1 returns terminal default", () => {
+		const color = { r: 0, g: 0, b: 0, a: 1 };
+		expect(colorToEscape(color, true, "truecolor")).toBe("\x1b[39m");
+		expect(colorToEscape(color, false, "truecolor")).toBe("\x1b[49m");
+	});
+
+	test("truecolor uses RGB escape", () => {
+		const color = { r: 100, g: 150, b: 200, a: 255 };
+		expect(colorToEscape(color, true, "truecolor")).toBe("\x1b[38;2;100;150;200m");
+	});
+
+	test("color256 uses 256-color escape", () => {
+		const color = { r: 100, g: 150, b: 200, a: 255 };
+		const result = colorToEscape(color, true, "color256");
+		expect(result).toMatch(/^\x1b\[38;5;\d+m$/);
+	});
+});
+
+describe("detectColorMode", () => {
+	test("returns ansi for ansi-containing theme names", () => {
+		expect(detectColorMode("ansi")).toBe("ansi");
+		expect(detectColorMode("base16-ansi-dark")).toBe("ansi");
+	});
+
+	test("returns truecolor or color256 for non-ansi themes", () => {
+		const mode = detectColorMode("monokai");
+		expect(["truecolor", "color256"]).toContain(mode);
+	});
+});
+
+describe("detectLanguage", () => {
+	test("detects language from file extension", () => {
+		expect(detectLanguage("index.ts")).toBe("ts");
+		expect(detectLanguage("main.py")).toBe("py");
+		expect(detectLanguage("style.css")).toBe("css");
+	});
+
+	test("detects language from known filenames", () => {
+		expect(detectLanguage("Makefile")).toBe("makefile");
+		expect(detectLanguage("Dockerfile")).toBe("dockerfile");
+	});
+
+	test("returns null for unknown extensions", () => {
+		expect(detectLanguage("file.xyz123")).toBeNull();
+	});
+});
+
+describe("tokenize", () => {
+	test("returns array of tokens", () => {
+		const result = tokenize("hello world");
+		expect(Array.isArray(result)).toBe(true);
+		expect(result.length).toBeGreaterThan(0);
+	});
+
+	test("preserves original text when joined", () => {
+		const text = "foo bar baz";
+		const tokens = tokenize(text);
+		expect(tokens.join("")).toBe(text);
+	});
+});

+ 163 - 0
scripts/health-check.ts

@@ -0,0 +1,163 @@
+#!/usr/bin/env bun
+/**
+ * 代码健康度检查脚本
+ *
+ * 汇总项目各维度指标,输出健康度报告:
+ * - 代码规模(文件数、代码行数)
+ * - Lint 问题数(Biome)
+ * - 测试结果(Bun test)
+ * - 冗余代码(Knip)
+ * - 构建状态
+ */
+
+import { $ } from "bun";
+
+const DIVIDER = "─".repeat(60);
+
+interface Metric {
+	label: string;
+	value: string | number;
+	status: "ok" | "warn" | "error" | "info";
+}
+
+const metrics: Metric[] = [];
+
+function add(label: string, value: string | number, status: Metric["status"] = "info") {
+	metrics.push({ label, value, status });
+}
+
+function icon(status: Metric["status"]): string {
+	switch (status) {
+		case "ok":
+			return "[OK]";
+		case "warn":
+			return "[!!]";
+		case "error":
+			return "[XX]";
+		case "info":
+			return "[--]";
+	}
+}
+
+// ---------------------------------------------------------------------------
+// 1. 代码规模
+// ---------------------------------------------------------------------------
+async function checkCodeSize() {
+	const tsFiles = await $`find src -name '*.ts' -o -name '*.tsx' | grep -v node_modules`.text();
+	const fileCount = tsFiles.trim().split("\n").filter(Boolean).length;
+	add("TypeScript 文件数", fileCount, "info");
+
+	const loc = await $`find src -name '*.ts' -o -name '*.tsx' | grep -v node_modules | xargs wc -l | tail -1`.text();
+	const totalLines = loc.trim().split(/\s+/)[0] ?? "?";
+	add("总代码行数 (src/)", totalLines, "info");
+}
+
+// ---------------------------------------------------------------------------
+// 2. Lint 检查
+// ---------------------------------------------------------------------------
+async function checkLint() {
+	try {
+		const result = await $`bunx biome check src/ 2>&1`.quiet().nothrow().text();
+		const errorMatch = result.match(/Found (\d+) errors?/);
+		const warnMatch = result.match(/Found (\d+) warnings?/);
+		const errors = errorMatch ? Number.parseInt(errorMatch[1]) : 0;
+		const warnings = warnMatch ? Number.parseInt(warnMatch[1]) : 0;
+		add("Lint 错误", errors, errors === 0 ? "ok" : errors < 100 ? "warn" : "info");
+		add("Lint 警告", warnings, warnings === 0 ? "ok" : "info");
+	} catch {
+		add("Lint 检查", "执行失败", "error");
+	}
+}
+
+// ---------------------------------------------------------------------------
+// 3. 测试
+// ---------------------------------------------------------------------------
+async function checkTests() {
+	try {
+		const result = await $`bun test 2>&1`.quiet().nothrow().text();
+		const passMatch = result.match(/(\d+) pass/);
+		const failMatch = result.match(/(\d+) fail/);
+		const pass = passMatch ? Number.parseInt(passMatch[1]) : 0;
+		const fail = failMatch ? Number.parseInt(failMatch[1]) : 0;
+		add("测试通过", pass, pass > 0 ? "ok" : "warn");
+		add("测试失败", fail, fail === 0 ? "ok" : "error");
+	} catch {
+		add("测试", "执行失败", "error");
+	}
+}
+
+// ---------------------------------------------------------------------------
+// 4. 冗余代码
+// ---------------------------------------------------------------------------
+async function checkUnused() {
+	try {
+		const result = await $`bunx knip-bun 2>&1`.quiet().nothrow().text();
+		const unusedFiles = result.match(/Unused files \((\d+)\)/);
+		const unusedExports = result.match(/Unused exports \((\d+)\)/);
+		const unusedDeps = result.match(/Unused dependencies \((\d+)\)/);
+		add("未使用文件", unusedFiles?.[1] ?? "0", "info");
+		add("未使用导出", unusedExports?.[1] ?? "0", "info");
+		add("未使用依赖", unusedDeps?.[1] ?? "0", unusedDeps && Number(unusedDeps[1]) > 0 ? "warn" : "ok");
+	} catch {
+		add("冗余代码检查", "执行失败", "error");
+	}
+}
+
+// ---------------------------------------------------------------------------
+// 5. 构建
+// ---------------------------------------------------------------------------
+async function checkBuild() {
+	try {
+		const result = await $`bun run build 2>&1`.quiet().nothrow();
+		if (result.exitCode === 0) {
+			// 获取产物大小
+			const stat = Bun.file("dist/cli.js");
+			const mb = (stat.size / 1024 / 1024).toFixed(1);
+			const size = `${mb} MB`;
+			add("构建状态", "成功", "ok");
+			add("产物大小 (dist/cli.js)", size, "info");
+		} else {
+			add("构建状态", "失败", "error");
+		}
+	} catch {
+		add("构建", "执行失败", "error");
+	}
+}
+
+// ---------------------------------------------------------------------------
+// Run
+// ---------------------------------------------------------------------------
+console.log("");
+console.log(DIVIDER);
+console.log("  代码健康度检查报告");
+console.log(`  ${new Date().toLocaleString("zh-CN")}`);
+console.log(DIVIDER);
+
+await checkCodeSize();
+await checkLint();
+await checkTests();
+await checkUnused();
+await checkBuild();
+
+console.log("");
+for (const m of metrics) {
+	const tag = icon(m.status);
+	console.log(`  ${tag}  ${m.label.padEnd(20)} ${m.value}`);
+}
+
+const errorCount = metrics.filter((m) => m.status === "error").length;
+const warnCount = metrics.filter((m) => m.status === "warn").length;
+
+console.log("");
+console.log(DIVIDER);
+if (errorCount > 0) {
+	console.log(`  结果: ${errorCount} 个错误, ${warnCount} 个警告`);
+} else if (warnCount > 0) {
+	console.log(`  结果: 无错误, ${warnCount} 个警告`);
+} else {
+	console.log("  结果: 全部通过");
+}
+console.log(DIVIDER);
+console.log("");
+
+process.exit(errorCount > 0 ? 1 : 0);

+ 58 - 0
src/utils/__tests__/array.test.ts

@@ -0,0 +1,58 @@
+import { describe, expect, test } from "bun:test";
+import { count, intersperse, uniq } from "../array";
+
+describe("intersperse", () => {
+	test("inserts separator between elements", () => {
+		const result = intersperse([1, 2, 3], () => 0);
+		expect(result).toEqual([1, 0, 2, 0, 3]);
+	});
+
+	test("returns empty array for empty input", () => {
+		expect(intersperse([], () => 0)).toEqual([]);
+	});
+
+	test("returns single element without separator", () => {
+		expect(intersperse([1], () => 0)).toEqual([1]);
+	});
+
+	test("passes index to separator function", () => {
+		const result = intersperse(["a", "b", "c"], (i) => `sep-${i}`);
+		expect(result).toEqual(["a", "sep-1", "b", "sep-2", "c"]);
+	});
+});
+
+describe("count", () => {
+	test("counts matching elements", () => {
+		expect(count([1, 2, 3, 4, 5], (x) => x > 3)).toBe(2);
+	});
+
+	test("returns 0 for empty array", () => {
+		expect(count([], () => true)).toBe(0);
+	});
+
+	test("returns 0 when nothing matches", () => {
+		expect(count([1, 2, 3], (x) => x > 10)).toBe(0);
+	});
+
+	test("counts all when everything matches", () => {
+		expect(count([1, 2, 3], () => true)).toBe(3);
+	});
+});
+
+describe("uniq", () => {
+	test("removes duplicates", () => {
+		expect(uniq([1, 2, 2, 3, 3, 3])).toEqual([1, 2, 3]);
+	});
+
+	test("preserves order of first occurrence", () => {
+		expect(uniq([3, 1, 2, 1, 3])).toEqual([3, 1, 2]);
+	});
+
+	test("handles empty array", () => {
+		expect(uniq([])).toEqual([]);
+	});
+
+	test("works with strings", () => {
+		expect(uniq(["a", "b", "a"])).toEqual(["a", "b"]);
+	});
+});

+ 63 - 0
src/utils/__tests__/set.test.ts

@@ -0,0 +1,63 @@
+import { describe, expect, test } from "bun:test";
+import { difference, every, intersects, union } from "../set";
+
+describe("difference", () => {
+	test("returns elements in a but not in b", () => {
+		const result = difference(new Set([1, 2, 3]), new Set([2, 3, 4]));
+		expect(result).toEqual(new Set([1]));
+	});
+
+	test("returns empty set when a is subset of b", () => {
+		expect(difference(new Set([1, 2]), new Set([1, 2, 3]))).toEqual(new Set());
+	});
+
+	test("returns a when b is empty", () => {
+		expect(difference(new Set([1, 2]), new Set())).toEqual(new Set([1, 2]));
+	});
+});
+
+describe("intersects", () => {
+	test("returns true when sets share elements", () => {
+		expect(intersects(new Set([1, 2]), new Set([2, 3]))).toBe(true);
+	});
+
+	test("returns false when sets are disjoint", () => {
+		expect(intersects(new Set([1, 2]), new Set([3, 4]))).toBe(false);
+	});
+
+	test("returns false for empty sets", () => {
+		expect(intersects(new Set(), new Set([1]))).toBe(false);
+		expect(intersects(new Set([1]), new Set())).toBe(false);
+	});
+});
+
+describe("every", () => {
+	test("returns true when a is subset of b", () => {
+		expect(every(new Set([1, 2]), new Set([1, 2, 3]))).toBe(true);
+	});
+
+	test("returns false when a has elements not in b", () => {
+		expect(every(new Set([1, 4]), new Set([1, 2, 3]))).toBe(false);
+	});
+
+	test("returns true for empty a", () => {
+		expect(every(new Set(), new Set([1, 2]))).toBe(true);
+	});
+});
+
+describe("union", () => {
+	test("combines both sets", () => {
+		const result = union(new Set([1, 2]), new Set([3, 4]));
+		expect(result).toEqual(new Set([1, 2, 3, 4]));
+	});
+
+	test("deduplicates shared elements", () => {
+		const result = union(new Set([1, 2]), new Set([2, 3]));
+		expect(result).toEqual(new Set([1, 2, 3]));
+	});
+
+	test("handles empty sets", () => {
+		expect(union(new Set(), new Set([1]))).toEqual(new Set([1]));
+		expect(union(new Set([1]), new Set())).toEqual(new Set([1]));
+	});
+});