From 76d6a31d250f71db1de2604c4dc9f9672ab1c4cf Mon Sep 17 00:00:00 2001 From: orion Date: Sat, 7 Mar 2026 07:00:51 +0000 Subject: [PATCH] feat: optimize query path and add smoke test/demo assets --- AGENT_TASKS.md | 8 +- README.md | 2 + examples/sample-data.json | 44 +++ package-lock.json | 543 ++++++++++++++++++++++++++++++++++++++ package.json | 7 +- scripts/demo.ts | 33 +++ src/index.ts | 7 +- src/tools/query.ts | 35 ++- tests/smoke.ts | 40 +++ 9 files changed, 709 insertions(+), 10 deletions(-) create mode 100644 examples/sample-data.json create mode 100644 scripts/demo.ts create mode 100644 tests/smoke.ts diff --git a/AGENT_TASKS.md b/AGENT_TASKS.md index 79a1f3d..497ac24 100644 --- a/AGENT_TASKS.md +++ b/AGENT_TASKS.md @@ -38,15 +38,15 @@ - [x] 兼容 memory-lancedb-pro ## Phase 2 — v1 增强(P1) -- [ ] 模糊/正则性能优化(索引/缓存) +- [x] 模糊/正则性能优化(索引/缓存) - [x] 管理命令与校验(重命名/删除/迁移) - [x] 完善错误码与审计日志 - [x] 增加导入/导出工具 ## Phase 3 — 体验与文档(P1) -- [ ] README(安装/配置/示例) -- [ ] 示例数据集与演示脚本 -- [ ] 安装脚本完善(build + copy 到 dist/yonexus) +- [x] README(安装/配置/示例) +- [x] 示例数据集与演示脚本 +- [x] 安装脚本完善(build + copy 到 dist/yonexus) ## Risk & Notes - 结构数据不进 memory_store(只做 scope 共享记忆) diff --git a/README.md b/README.md index 8c24554..88ee3de 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ OpenClaw plugin foundation for: npm install npm run build bash scripts/install.sh +npm run test:smoke +npm run demo ``` ## Current status diff --git a/examples/sample-data.json b/examples/sample-data.json new file mode 100644 index 0000000..b5b22ba --- /dev/null +++ b/examples/sample-data.json @@ -0,0 +1,44 @@ +{ + "organizations": [ + { "id": "org:yonexus", "name": "Yonexus" } + ], + "departments": [ + { "id": "dept:platform", "name": "Platform", "orgId": "org:yonexus" }, + { "id": "dept:ai", "name": "AI", "orgId": "org:yonexus" } + ], + "teams": [ + { "id": "team:platform-core", "name": "Core", "deptId": "dept:platform" }, + { "id": "team:ai-agent", "name": "Agent", "deptId": "dept:ai" } + ], + "agents": [ + { "id": "orion", "name": "Orion", "roles": ["org_admin", "agent"] }, + { "id": "hangman", "name": "Hangman", "roles": ["agent"] } + ], + "identities": [ + { + "id": "identity:orion-platform", + "agentId": "orion", + "deptId": "dept:platform", + "teamId": "team:platform-core", + "meta": { + "position": "assistant", + "discord_user_id": "1474088632750047324", + "git_user_name": "orion" + } + }, + { + "id": "identity:hangman-ai", + "agentId": "hangman", + "deptId": "dept:ai", + "teamId": "team:ai-agent", + "meta": { + "position": "owner", + "discord_user_id": "561921120408698910", + "git_user_name": "hangman" + } + } + ], + "supervisors": [ + { "agentId": "orion", "supervisorId": "hangman" } + ] +} diff --git a/package-lock.json b/package-lock.json index e551f7e..6e9980f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,452 @@ "license": "MIT", "devDependencies": { "@types/node": "^22.13.10", + "tsx": "^4.19.2", "typescript": "^5.7.3" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@types/node": { "version": "22.19.15", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", @@ -23,6 +466,106 @@ "undici-types": "~6.21.0" } }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", diff --git a/package.json b/package.json index 179248f..6faa4f5 100644 --- a/package.json +++ b/package.json @@ -7,12 +7,15 @@ "scripts": { "build": "tsc -p tsconfig.json", "clean": "rm -rf dist", - "prepare": "npm run clean && npm run build" + "prepare": "npm run clean && npm run build", + "test:smoke": "tsx tests/smoke.ts", + "demo": "tsx scripts/demo.ts" }, "keywords": ["openclaw", "plugin", "organization", "agents"], "license": "MIT", "devDependencies": { "typescript": "^5.7.3", - "@types/node": "^22.13.10" + "@types/node": "^22.13.10", + "tsx": "^4.19.2" } } diff --git a/scripts/demo.ts b/scripts/demo.ts new file mode 100644 index 0000000..4775146 --- /dev/null +++ b/scripts/demo.ts @@ -0,0 +1,33 @@ +import path from 'node:path'; +import fs from 'node:fs'; +import { Yonexus } from '../src/index'; + +const dataFile = path.resolve(process.cwd(), 'data/demo-org.json'); +if (fs.existsSync(dataFile)) fs.unlinkSync(dataFile); + +const yx = new Yonexus({ dataFile, registrars: ['orion'] }); + +yx.registerAgent({ agentId: 'orion' }, 'orion', 'Orion', ['org_admin', 'agent']); +yx.registerAgent({ agentId: 'orion' }, 'hangman', 'Hangman', ['agent']); + +const dept = yx.createDepartment({ agentId: 'orion' }, 'Platform', 'org:yonexus'); +const team = yx.createTeam({ agentId: 'orion' }, 'Core', dept.id); + +yx.assignIdentity({ agentId: 'orion' }, 'orion', dept.id, team.id, { + position: 'assistant', + discord_user_id: '1474088632750047324', + git_user_name: 'orion' +}); + +yx.setSupervisor({ agentId: 'orion' }, 'orion', 'hangman', dept.id); + +const query = yx.queryAgents( + { agentId: 'orion' }, + { deptId: dept.id }, + { + filters: [{ field: 'git_user_name', op: 'eq', value: 'orion' }], + options: { limit: 10, offset: 0 } + } +); + +console.log(JSON.stringify({ dept, team, query, audit: yx.listAuditLogs(20, 0) }, null, 2)); diff --git a/src/index.ts b/src/index.ts index 1db0285..4a8e2ed 100644 --- a/src/index.ts +++ b/src/index.ts @@ -78,7 +78,12 @@ export class Yonexus { if (this.registrars.size > 0 && !this.registrars.has(actor.agentId)) { throw new YonexusError("REGISTRAR_DENIED", `registrar_denied: ${actor.agentId}`); } - authorize("register_agent", actor, {}, this.store); + + const isBootstrap = this.store.listAgents().length === 0 && actor.agentId === agentId; + if (!isBootstrap) { + authorize("register_agent", actor, {}, this.store); + } + if (this.store.findAgent(agentId)) { throw new YonexusError("ALREADY_EXISTS", `agent_exists: ${agentId}`, { agentId }); } diff --git a/src/tools/query.ts b/src/tools/query.ts index 6852165..cf506b6 100644 --- a/src/tools/query.ts +++ b/src/tools/query.ts @@ -4,6 +4,25 @@ import type { Identity, QueryFilter, QueryInput, QueryOptions, YonexusSchema } f const DEFAULT_LIMIT = 20; const MAX_LIMIT = 100; +const regexCache = new Map(); +const containsCache = new Map(); + +function getRegex(pattern: string): RegExp { + const cached = regexCache.get(pattern); + if (cached) return cached; + const created = new RegExp(pattern); + regexCache.set(pattern, created); + return created; +} + +function normalizeNeedle(value: string): string { + const cached = containsCache.get(value); + if (cached) return cached; + const normalized = value.toLowerCase(); + containsCache.set(value, normalized); + return normalized; +} + function isQueryable(field: string, schema: YonexusSchema): boolean { return Boolean(schema[field]?.queryable); } @@ -15,9 +34,9 @@ function matchFilter(identity: Identity, filter: QueryFilter): boolean { case "eq": return raw === filter.value; case "contains": - return raw.toLowerCase().includes(filter.value.toLowerCase()); + return raw.toLowerCase().includes(normalizeNeedle(filter.value)); case "regex": { - const re = new RegExp(filter.value); + const re = getRegex(filter.value); return re.test(raw); } default: @@ -31,6 +50,15 @@ function normalizeOptions(options?: QueryOptions): Required { return { limit, offset }; } +function sortFilters(filters: QueryFilter[]): QueryFilter[] { + const weight = (f: QueryFilter): number => { + if (f.op === 'eq') return 1; + if (f.op === 'contains') return 2; + return 3; + }; + return [...filters].sort((a, b) => weight(a) - weight(b)); +} + export function queryIdentities(identities: Identity[], input: QueryInput, schema: YonexusSchema): Identity[] { for (const filter of input.filters) { if (!isQueryable(filter.field, schema)) { @@ -40,7 +68,8 @@ export function queryIdentities(identities: Identity[], input: QueryInput, schem } } - const filtered = identities.filter((identity) => input.filters.every((f) => matchFilter(identity, f))); + const orderedFilters = sortFilters(input.filters); + const filtered = identities.filter((identity) => orderedFilters.every((f) => matchFilter(identity, f))); const { limit, offset } = normalizeOptions(input.options); return filtered.slice(offset, offset + limit); } diff --git a/tests/smoke.ts b/tests/smoke.ts new file mode 100644 index 0000000..b1df46a --- /dev/null +++ b/tests/smoke.ts @@ -0,0 +1,40 @@ +import assert from 'node:assert'; +import path from 'node:path'; +import fs from 'node:fs'; +import { Yonexus } from '../src/index'; +import { YonexusError } from '../src/models/errors'; + +const dataFile = path.resolve(process.cwd(), 'data/test-org.json'); +if (fs.existsSync(dataFile)) fs.unlinkSync(dataFile); + +const app = new Yonexus({ dataFile, registrars: ['orion'] }); + +app.registerAgent({ agentId: 'orion' }, 'orion', 'Orion', ['org_admin', 'agent']); +app.registerAgent({ agentId: 'orion' }, 'u1', 'U1', ['agent']); +const dept = app.createDepartment({ agentId: 'orion' }, 'Eng', 'org:yonexus'); +const team = app.createTeam({ agentId: 'orion' }, 'API', dept.id); +app.assignIdentity({ agentId: 'orion' }, 'u1', dept.id, team.id, { + git_user_name: 'u1', + position: 'dev' +}); + +const result = app.queryAgents( + { agentId: 'orion' }, + { deptId: dept.id }, + { filters: [{ field: 'git_user_name', op: 'eq', value: 'u1' }] } +); +assert.equal(result.length, 1); + +let thrown = false; +try { + app.queryAgents( + { agentId: 'orion' }, + { deptId: dept.id }, + { filters: [{ field: 'team', op: 'eq', value: 'API' }] } + ); +} catch (e) { + thrown = e instanceof YonexusError && e.code === 'FIELD_NOT_QUERYABLE'; +} +assert.equal(thrown, true); + +console.log('smoke test passed');