diff --git a/.component-builds/kbdb_upsert_block/package.json b/.component-builds/kbdb_upsert_block/package.json deleted file mode 100644 index 45e9118..0000000 --- a/.component-builds/kbdb_upsert_block/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "arcrun-kbdb-upsert-block", - "version": "1.0.0", - "private": true, - "type": "module", - "dependencies": { - "hono": "^4.7.0" - }, - "devDependencies": { - "@cloudflare/workers-types": "^4.20250408.0", - "typescript": "^5.4.0", - "wrangler": "^4.0.0" - } -} diff --git a/.component-builds/kbdb_upsert_block/pnpm-lock.yaml b/.component-builds/kbdb_upsert_block/pnpm-lock.yaml deleted file mode 100644 index b8ea2c1..0000000 --- a/.component-builds/kbdb_upsert_block/pnpm-lock.yaml +++ /dev/null @@ -1,898 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - hono: - specifier: ^4.7.0 - version: 4.12.18 - devDependencies: - '@cloudflare/workers-types': - specifier: ^4.20250408.0 - version: 4.20260511.1 - typescript: - specifier: ^5.4.0 - version: 5.9.3 - wrangler: - specifier: ^4.0.0 - version: 4.90.1(@cloudflare/workers-types@4.20260511.1) - -packages: - - '@cloudflare/kv-asset-handler@0.5.0': - resolution: {integrity: sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==} - engines: {node: '>=22.0.0'} - - '@cloudflare/unenv-preset@2.16.1': - resolution: {integrity: sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==} - peerDependencies: - unenv: 2.0.0-rc.24 - workerd: '>1.20260305.0 <2.0.0-0' - peerDependenciesMeta: - workerd: - optional: true - - '@cloudflare/workerd-darwin-64@1.20260508.1': - resolution: {integrity: sha512-IT3r6VgiSwIesL4AJbxjgxvIxwWZqM7BKkhYAzOKHl4GF2M0TxeOahUIXd+CYXVZgHX8ceEg+MXbEehPelJyNg==} - engines: {node: '>=16'} - cpu: [x64] - os: [darwin] - - '@cloudflare/workerd-darwin-arm64@1.20260508.1': - resolution: {integrity: sha512-JTVsisOJPcNKw0qovPjqyBWYahfdhUh7/9NICiG5wxaEQ45PYKdoqNq0hOAAIqvqoxsKZBvTgcPTJREPqk7avA==} - engines: {node: '>=16'} - cpu: [arm64] - os: [darwin] - - '@cloudflare/workerd-linux-64@1.20260508.1': - resolution: {integrity: sha512-zO38pCc27YlsZiPYcaZnosy0/t7abXrRU3VEO1oKfUvnaCpHgphDG+VsrmHL+kntda6hrtNwg2jLeMAqqIjnjw==} - engines: {node: '>=16'} - cpu: [x64] - os: [linux] - - '@cloudflare/workerd-linux-arm64@1.20260508.1': - resolution: {integrity: sha512-XhJa780Ia6MNIrtxn/ruZHS79b9pu5EKPfRNReaUqxy8erPT2fs93axMfFoS9kIkcaRRj/1TOUKcTeAMoywY7w==} - engines: {node: '>=16'} - cpu: [arm64] - os: [linux] - - '@cloudflare/workerd-windows-64@1.20260508.1': - resolution: {integrity: sha512-QdDOK3B/Ul1s3QmIwDrFyx9230to6LsNmWcVR8w+TYjNZuRPzqQBgusp78LO7MlqCoEl9dvIcN00jkJnLtBSfw==} - engines: {node: '>=16'} - cpu: [x64] - os: [win32] - - '@cloudflare/workers-types@4.20260511.1': - resolution: {integrity: sha512-FA+si7cOq9i/gtCHhIc0XJL0l1F/ApF+m00752Aj7WZFJrj3ZulT2T8/+rT3BabMT0QEnqFEGIqCgrmqhgEfMg==} - - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - - '@emnapi/runtime@1.10.0': - resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - - '@esbuild/aix-ppc64@0.27.3': - resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.27.3': - resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.27.3': - resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.27.3': - resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.27.3': - resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.27.3': - resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.27.3': - resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.27.3': - resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.27.3': - resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.27.3': - resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.27.3': - resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.27.3': - resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.27.3': - resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.27.3': - resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.27.3': - resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.27.3': - resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.27.3': - resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.27.3': - resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.27.3': - resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.27.3': - resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.27.3': - resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.27.3': - resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.27.3': - resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.27.3': - resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.27.3': - resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.27.3': - resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@img/colour@1.1.0': - resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} - engines: {node: '>=18'} - - '@img/sharp-darwin-arm64@0.34.5': - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [darwin] - - '@img/sharp-darwin-x64@0.34.5': - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} - cpu: [arm64] - os: [darwin] - - '@img/sharp-libvips-darwin-x64@1.2.4': - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-linux-arm64@1.2.4': - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-arm@1.2.4': - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-ppc64@1.2.4': - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-riscv64@1.2.4': - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-arm@0.34.5': - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - libc: [musl] - - '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [wasm32] - - '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [win32] - - '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ia32] - os: [win32] - - '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [win32] - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - - '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - - '@poppinss/colors@4.1.6': - resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} - - '@poppinss/dumper@0.6.5': - resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==} - - '@poppinss/exception@1.2.3': - resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} - - '@sindresorhus/is@7.2.0': - resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} - engines: {node: '>=18'} - - '@speed-highlight/core@1.2.15': - resolution: {integrity: sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==} - - blake3-wasm@2.1.5: - resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} - - cookie@1.1.1: - resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} - engines: {node: '>=18'} - - detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} - - error-stack-parser-es@1.0.5: - resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} - - esbuild@0.27.3: - resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} - engines: {node: '>=18'} - hasBin: true - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - hono@4.12.18: - resolution: {integrity: sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ==} - engines: {node: '>=16.9.0'} - - kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - - miniflare@4.20260508.0: - resolution: {integrity: sha512-h3aG+PA8jEH76V4ZtBAbs3g7kjMfHJUF8hPvxeeajLTKwir+G+dqfBODg5yF9MT29LqrZKCRQRqzfHPWX4kCIg==} - engines: {node: '>=22.0.0'} - hasBin: true - - path-to-regexp@6.3.0: - resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - semver@7.8.0: - resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} - engines: {node: '>=10'} - hasBin: true - - sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - - supports-color@10.2.2: - resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} - engines: {node: '>=18'} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true - - undici@7.24.8: - resolution: {integrity: sha512-6KQ/+QxK49Z/p3HO6E5ZCZWNnCasyZLa5ExaVYyvPxUwKtbCPMKELJOqh7EqOle0t9cH/7d2TaaTRRa6Nhs4YQ==} - engines: {node: '>=20.18.1'} - - unenv@2.0.0-rc.24: - resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} - - workerd@1.20260508.1: - resolution: {integrity: sha512-VlnjyH3AjVddpSK7J54nsCVgf8i2733pl8GjKttfNi7vN/hEjjAk20d2b1nDToOLKvRQpTewRnVkqaaeGHCaAw==} - engines: {node: '>=16'} - hasBin: true - - wrangler@4.90.1: - resolution: {integrity: sha512-u2KrieKSMfRM0toTst/CfDtcRraeoVjmcExcMWgILM/ytq3qcDhuOAULoZSyPHzma43lfLJy1BC544drFyqe1A==} - engines: {node: '>=22.0.0'} - hasBin: true - peerDependencies: - '@cloudflare/workers-types': ^4.20260508.1 - peerDependenciesMeta: - '@cloudflare/workers-types': - optional: true - - ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - youch-core@0.3.3: - resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} - - youch@4.1.0-beta.10: - resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} - -snapshots: - - '@cloudflare/kv-asset-handler@0.5.0': {} - - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260508.1)': - dependencies: - unenv: 2.0.0-rc.24 - optionalDependencies: - workerd: 1.20260508.1 - - '@cloudflare/workerd-darwin-64@1.20260508.1': - optional: true - - '@cloudflare/workerd-darwin-arm64@1.20260508.1': - optional: true - - '@cloudflare/workerd-linux-64@1.20260508.1': - optional: true - - '@cloudflare/workerd-linux-arm64@1.20260508.1': - optional: true - - '@cloudflare/workerd-windows-64@1.20260508.1': - optional: true - - '@cloudflare/workers-types@4.20260511.1': {} - - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - - '@emnapi/runtime@1.10.0': - dependencies: - tslib: 2.8.1 - optional: true - - '@esbuild/aix-ppc64@0.27.3': - optional: true - - '@esbuild/android-arm64@0.27.3': - optional: true - - '@esbuild/android-arm@0.27.3': - optional: true - - '@esbuild/android-x64@0.27.3': - optional: true - - '@esbuild/darwin-arm64@0.27.3': - optional: true - - '@esbuild/darwin-x64@0.27.3': - optional: true - - '@esbuild/freebsd-arm64@0.27.3': - optional: true - - '@esbuild/freebsd-x64@0.27.3': - optional: true - - '@esbuild/linux-arm64@0.27.3': - optional: true - - '@esbuild/linux-arm@0.27.3': - optional: true - - '@esbuild/linux-ia32@0.27.3': - optional: true - - '@esbuild/linux-loong64@0.27.3': - optional: true - - '@esbuild/linux-mips64el@0.27.3': - optional: true - - '@esbuild/linux-ppc64@0.27.3': - optional: true - - '@esbuild/linux-riscv64@0.27.3': - optional: true - - '@esbuild/linux-s390x@0.27.3': - optional: true - - '@esbuild/linux-x64@0.27.3': - optional: true - - '@esbuild/netbsd-arm64@0.27.3': - optional: true - - '@esbuild/netbsd-x64@0.27.3': - optional: true - - '@esbuild/openbsd-arm64@0.27.3': - optional: true - - '@esbuild/openbsd-x64@0.27.3': - optional: true - - '@esbuild/openharmony-arm64@0.27.3': - optional: true - - '@esbuild/sunos-x64@0.27.3': - optional: true - - '@esbuild/win32-arm64@0.27.3': - optional: true - - '@esbuild/win32-ia32@0.27.3': - optional: true - - '@esbuild/win32-x64@0.27.3': - optional: true - - '@img/colour@1.1.0': {} - - '@img/sharp-darwin-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.4 - optional: true - - '@img/sharp-darwin-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.4 - optional: true - - '@img/sharp-libvips-darwin-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-darwin-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm@1.2.4': - optional: true - - '@img/sharp-libvips-linux-ppc64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-riscv64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-s390x@1.2.4': - optional: true - - '@img/sharp-libvips-linux-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - optional: true - - '@img/sharp-linux-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.4 - optional: true - - '@img/sharp-linux-arm@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.4 - optional: true - - '@img/sharp-linux-ppc64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.4 - optional: true - - '@img/sharp-linux-riscv64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.2.4 - optional: true - - '@img/sharp-linux-s390x@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.4 - optional: true - - '@img/sharp-linux-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - optional: true - - '@img/sharp-wasm32@0.34.5': - dependencies: - '@emnapi/runtime': 1.10.0 - optional: true - - '@img/sharp-win32-arm64@0.34.5': - optional: true - - '@img/sharp-win32-ia32@0.34.5': - optional: true - - '@img/sharp-win32-x64@0.34.5': - optional: true - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/sourcemap-codec@1.5.5': {} - - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - - '@poppinss/colors@4.1.6': - dependencies: - kleur: 4.1.5 - - '@poppinss/dumper@0.6.5': - dependencies: - '@poppinss/colors': 4.1.6 - '@sindresorhus/is': 7.2.0 - supports-color: 10.2.2 - - '@poppinss/exception@1.2.3': {} - - '@sindresorhus/is@7.2.0': {} - - '@speed-highlight/core@1.2.15': {} - - blake3-wasm@2.1.5: {} - - cookie@1.1.1: {} - - detect-libc@2.1.2: {} - - error-stack-parser-es@1.0.5: {} - - esbuild@0.27.3: - 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 - - fsevents@2.3.3: - optional: true - - hono@4.12.18: {} - - kleur@4.1.5: {} - - miniflare@4.20260508.0: - dependencies: - '@cspotcode/source-map-support': 0.8.1 - sharp: 0.34.5 - undici: 7.24.8 - workerd: 1.20260508.1 - ws: 8.18.0 - youch: 4.1.0-beta.10 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - path-to-regexp@6.3.0: {} - - pathe@2.0.3: {} - - semver@7.8.0: {} - - sharp@0.34.5: - dependencies: - '@img/colour': 1.1.0 - detect-libc: 2.1.2 - semver: 7.8.0 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.5 - '@img/sharp-darwin-x64': 0.34.5 - '@img/sharp-libvips-darwin-arm64': 1.2.4 - '@img/sharp-libvips-darwin-x64': 1.2.4 - '@img/sharp-libvips-linux-arm': 1.2.4 - '@img/sharp-libvips-linux-arm64': 1.2.4 - '@img/sharp-libvips-linux-ppc64': 1.2.4 - '@img/sharp-libvips-linux-riscv64': 1.2.4 - '@img/sharp-libvips-linux-s390x': 1.2.4 - '@img/sharp-libvips-linux-x64': 1.2.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - '@img/sharp-linux-arm': 0.34.5 - '@img/sharp-linux-arm64': 0.34.5 - '@img/sharp-linux-ppc64': 0.34.5 - '@img/sharp-linux-riscv64': 0.34.5 - '@img/sharp-linux-s390x': 0.34.5 - '@img/sharp-linux-x64': 0.34.5 - '@img/sharp-linuxmusl-arm64': 0.34.5 - '@img/sharp-linuxmusl-x64': 0.34.5 - '@img/sharp-wasm32': 0.34.5 - '@img/sharp-win32-arm64': 0.34.5 - '@img/sharp-win32-ia32': 0.34.5 - '@img/sharp-win32-x64': 0.34.5 - - supports-color@10.2.2: {} - - tslib@2.8.1: - optional: true - - typescript@5.9.3: {} - - undici@7.24.8: {} - - unenv@2.0.0-rc.24: - dependencies: - pathe: 2.0.3 - - workerd@1.20260508.1: - optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260508.1 - '@cloudflare/workerd-darwin-arm64': 1.20260508.1 - '@cloudflare/workerd-linux-64': 1.20260508.1 - '@cloudflare/workerd-linux-arm64': 1.20260508.1 - '@cloudflare/workerd-windows-64': 1.20260508.1 - - wrangler@4.90.1(@cloudflare/workers-types@4.20260511.1): - dependencies: - '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260508.1) - blake3-wasm: 2.1.5 - esbuild: 0.27.3 - miniflare: 4.20260508.0 - path-to-regexp: 6.3.0 - unenv: 2.0.0-rc.24 - workerd: 1.20260508.1 - optionalDependencies: - '@cloudflare/workers-types': 4.20260511.1 - fsevents: 2.3.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - ws@8.18.0: {} - - youch-core@0.3.3: - dependencies: - '@poppinss/exception': 1.2.3 - error-stack-parser-es: 1.0.5 - - youch@4.1.0-beta.10: - dependencies: - '@poppinss/colors': 4.1.6 - '@poppinss/dumper': 0.6.5 - '@speed-highlight/core': 1.2.15 - cookie: 1.1.1 - youch-core: 0.3.3 diff --git a/.component-builds/kbdb_upsert_block/pnpm-workspace.yaml b/.component-builds/kbdb_upsert_block/pnpm-workspace.yaml deleted file mode 100644 index d4d28e2..0000000 --- a/.component-builds/kbdb_upsert_block/pnpm-workspace.yaml +++ /dev/null @@ -1,8 +0,0 @@ -allowBuilds: - esbuild: true - sharp: true - workerd: true -onlyBuiltDependencies: - - esbuild - - sharp - - workerd diff --git a/.component-builds/kbdb_upsert_block/src/index.ts b/.component-builds/kbdb_upsert_block/src/index.ts deleted file mode 100644 index 560e7b9..0000000 --- a/.component-builds/kbdb_upsert_block/src/index.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * arcrun WASM 零件 Worker (kbdb_upsert_block) - * POST / → JSON input → WASM (WASI preview1) → JSON output - * SDD: polaris/mira/.agents/specs/mira-app/design.md §3.5.12.4.1 - * matrix/arcrun/.agents/specs/arcrun/arcrun.md 三-B 新零件加入紀錄 - */ - -import componentWasm from '../component.wasm' assert { type: 'webassembly' }; -import { Hono } from 'hono'; -import { cors } from 'hono/cors'; -import { createWasiShim, type WasiHostFunctions } from '../../../cypher-executor/src/lib/wasi-shim'; - -const app = new Hono(); -app.use('*', cors()); - -app.get('/', (c) => c.json({ ok: true, component: 'kbdb_upsert_block' })); - -app.post('/', async (c) => { - let input: unknown; - try { - input = await c.req.json(); - } catch { - return c.json({ success: false, error: 'request body must be JSON' }, 400); - } - - try { - const result = await runWasm(input); - return c.json(result); - } catch (e) { - return c.json( - { success: false, error: e instanceof Error ? e.message : String(e) }, - 500, - ); - } -}); - -export default app; - -async function runWasm(input: unknown): Promise { - const hostFunctions: WasiHostFunctions = { - http_request: async (url, method, headersJson, body) => { - const headers: Record = {}; - if (headersJson) { - try { - const parsed = JSON.parse(headersJson); - if (parsed && typeof parsed === 'object') { - for (const [k, v] of Object.entries(parsed as Record)) { - if (typeof v === 'string') headers[k] = v; - } - } - } catch {} - } - const init: RequestInit = { method, headers }; - if (body && method.toUpperCase() !== 'GET' && method.toUpperCase() !== 'HEAD') { - init.body = body; - } - const res = await fetch(url, init); - const text = await res.text(); - // 修架構債(同 http_request):非 2xx 包成帶 "error" key 的 envelope, - // 讓 WASM 端既有的 error 判定正確識別失敗(原本只回 body 丟掉 status → 4xx 被判 success)。 - if (!res.ok) { - return JSON.stringify({ error: `HTTP ${res.status}`, status: res.status, body: text }); - } - return text; - }, - }; - - const shim = createWasiShim(JSON.stringify(input), hostFunctions); - const instance = await WebAssembly.instantiate( - componentWasm as WebAssembly.Module, - shim.imports, - ); - shim.setMemory(instance.exports.memory as WebAssembly.Memory); - await shim.run(instance); - - const stdout = shim.getStdout().trim(); - const stderr = shim.getStderr().trim(); - if (stderr) console.error('[kbdb_upsert_block wasm stderr]', stderr); - if (!stdout) throw new Error('WASM component produced no output'); - return JSON.parse(stdout); -} diff --git a/.component-builds/kbdb_upsert_block/tsconfig.json b/.component-builds/kbdb_upsert_block/tsconfig.json deleted file mode 100644 index b65fda7..0000000 --- a/.component-builds/kbdb_upsert_block/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ES2022", - "moduleResolution": "bundler", - "lib": ["ES2022"], - "types": ["@cloudflare/workers-types"], - "strict": true, - "noEmit": true - } -} diff --git a/.component-builds/kbdb_upsert_block/wrangler.toml b/.component-builds/kbdb_upsert_block/wrangler.toml deleted file mode 100644 index 01e6dfd..0000000 --- a/.component-builds/kbdb_upsert_block/wrangler.toml +++ /dev/null @@ -1,12 +0,0 @@ -name = "arcrun-kbdb-upsert-block" -main = "src/index.ts" -compatibility_date = "2025-02-19" -compatibility_flags = ["nodejs_compat"] -workers_dev = true - -[vars] -COMPONENT_ID = "kbdb_upsert_block" - -[[routes]] -pattern = "kbdb-upsert-block.arcrun.dev/*" -zone_name = "arcrun.dev" diff --git a/.component-builds/km_writer/package.json b/.component-builds/km_writer/package.json deleted file mode 100644 index a32f485..0000000 --- a/.component-builds/km_writer/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "arcrun-km-writer", - "version": "1.0.0", - "private": true, - "type": "module", - "dependencies": { - "hono": "^4.7.0" - }, - "devDependencies": { - "@cloudflare/workers-types": "^4.20250408.0", - "typescript": "^5.4.0", - "wrangler": "^4.0.0" - } -} diff --git a/.component-builds/km_writer/pnpm-lock.yaml b/.component-builds/km_writer/pnpm-lock.yaml deleted file mode 100644 index 9d03336..0000000 --- a/.component-builds/km_writer/pnpm-lock.yaml +++ /dev/null @@ -1,898 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - hono: - specifier: ^4.7.0 - version: 4.12.14 - devDependencies: - '@cloudflare/workers-types': - specifier: ^4.20250408.0 - version: 4.20260420.1 - typescript: - specifier: ^5.4.0 - version: 5.9.3 - wrangler: - specifier: ^4.0.0 - version: 4.83.0(@cloudflare/workers-types@4.20260420.1) - -packages: - - '@cloudflare/kv-asset-handler@0.4.2': - resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==} - engines: {node: '>=18.0.0'} - - '@cloudflare/unenv-preset@2.16.0': - resolution: {integrity: sha512-8ovsRpwzPoEqPUzoErAYVv8l3FMZNeBVQfJTvtzP4AgLSRGZISRfuChFxHWUQd3n6cnrwkuTGxT+2cGo8EsyYg==} - peerDependencies: - unenv: 2.0.0-rc.24 - workerd: 1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0 - peerDependenciesMeta: - workerd: - optional: true - - '@cloudflare/workerd-darwin-64@1.20260415.1': - resolution: {integrity: sha512-dsxaKsQm3LnPGNPEdsRv09QN3Y4DqCw7kX5j6noKqbAtro2jTr95sVlYM1jUxZ5FkOl1f7SXgaKKB9t5H5Nkbg==} - engines: {node: '>=16'} - cpu: [x64] - os: [darwin] - - '@cloudflare/workerd-darwin-arm64@1.20260415.1': - resolution: {integrity: sha512-+JgSgVA49KyKteHRA1SnonE4Zn5Ei5zdAp5FQMxFmXI8qulZw4Hl7safXxRyK4i9sTO8gl7TFOKO5Q64VPvSDQ==} - engines: {node: '>=16'} - cpu: [arm64] - os: [darwin] - - '@cloudflare/workerd-linux-64@1.20260415.1': - resolution: {integrity: sha512-tU+9pwsqCy8afOVlGtiWrWQc/fedQK4SRm4KPIAt+zOiQWDxWASm6YGBUJis5c648WN80yz47qnmdDi8DQNOcA==} - engines: {node: '>=16'} - cpu: [x64] - os: [linux] - - '@cloudflare/workerd-linux-arm64@1.20260415.1': - resolution: {integrity: sha512-bR9uITnV19r5NQ14xnypi2xHXu2iQvfYV8cVgx0JouFUmWwTEEAwFVojDdssGq93VHX9hr/pi2IRUZeegbYBog==} - engines: {node: '>=16'} - cpu: [arm64] - os: [linux] - - '@cloudflare/workerd-windows-64@1.20260415.1': - resolution: {integrity: sha512-4NuMLlerI0Ijua3Ir8HXQ+qyNvCUDEG5gDco5Om+sAiK6rnWiz+aGoSlbB8W16yW9QAgzCstbmXLiVknUBflfQ==} - engines: {node: '>=16'} - cpu: [x64] - os: [win32] - - '@cloudflare/workers-types@4.20260420.1': - resolution: {integrity: sha512-DHT9JnSn9cIiCSdL76OxW+Xvc1+ml1CWzWvgVwreoHQ+E604aeFxPPHp9X7nE+XRWm2NH4l0OgtxUI5T/nuI3g==} - - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - - '@emnapi/runtime@1.10.0': - resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - - '@esbuild/aix-ppc64@0.27.3': - resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.27.3': - resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.27.3': - resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.27.3': - resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.27.3': - resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.27.3': - resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.27.3': - resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.27.3': - resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.27.3': - resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.27.3': - resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.27.3': - resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.27.3': - resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.27.3': - resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.27.3': - resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.27.3': - resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.27.3': - resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.27.3': - resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.27.3': - resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.27.3': - resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.27.3': - resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.27.3': - resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.27.3': - resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.27.3': - resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.27.3': - resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.27.3': - resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.27.3': - resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@img/colour@1.1.0': - resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} - engines: {node: '>=18'} - - '@img/sharp-darwin-arm64@0.34.5': - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [darwin] - - '@img/sharp-darwin-x64@0.34.5': - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} - cpu: [arm64] - os: [darwin] - - '@img/sharp-libvips-darwin-x64@1.2.4': - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-linux-arm64@1.2.4': - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-arm@1.2.4': - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-ppc64@1.2.4': - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-riscv64@1.2.4': - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-arm@0.34.5': - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - libc: [musl] - - '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [wasm32] - - '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [win32] - - '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ia32] - os: [win32] - - '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [win32] - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - - '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - - '@poppinss/colors@4.1.6': - resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} - - '@poppinss/dumper@0.6.5': - resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==} - - '@poppinss/exception@1.2.3': - resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} - - '@sindresorhus/is@7.2.0': - resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} - engines: {node: '>=18'} - - '@speed-highlight/core@1.2.15': - resolution: {integrity: sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==} - - blake3-wasm@2.1.5: - resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} - - cookie@1.1.1: - resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} - engines: {node: '>=18'} - - detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} - - error-stack-parser-es@1.0.5: - resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} - - esbuild@0.27.3: - resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} - engines: {node: '>=18'} - hasBin: true - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - hono@4.12.14: - resolution: {integrity: sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==} - engines: {node: '>=16.9.0'} - - kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - - miniflare@4.20260415.0: - resolution: {integrity: sha512-JoExRWN4YBI2luA5BoSMFEgi8rQWXUGzo3mtE+58VXCLV3jj/Xnk5Yeqs/IXWz8Es5GJIaq6BtsixDvAxXSIng==} - engines: {node: '>=18.0.0'} - hasBin: true - - path-to-regexp@6.3.0: - resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} - engines: {node: '>=10'} - hasBin: true - - sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - - supports-color@10.2.2: - resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} - engines: {node: '>=18'} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true - - undici@7.24.8: - resolution: {integrity: sha512-6KQ/+QxK49Z/p3HO6E5ZCZWNnCasyZLa5ExaVYyvPxUwKtbCPMKELJOqh7EqOle0t9cH/7d2TaaTRRa6Nhs4YQ==} - engines: {node: '>=20.18.1'} - - unenv@2.0.0-rc.24: - resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} - - workerd@1.20260415.1: - resolution: {integrity: sha512-phyPjRnx+mQDfkhN9ENPioL1L0SdhYs4S0YmJK/xF9Oga+ykNfdSy1MHnsOj8yqnOV96zcVQMx32dJ0r3pq0jQ==} - engines: {node: '>=16'} - hasBin: true - - wrangler@4.83.0: - resolution: {integrity: sha512-gw5g3LCiuAqVWxaoKY6+quE0HzAUEFb/FV3oAlNkE1ttd4XP3FiV91XDkkzUCcdqxS4WjhQvPhIDBNdhEi8P0A==} - engines: {node: '>=20.3.0'} - hasBin: true - peerDependencies: - '@cloudflare/workers-types': ^4.20260415.1 - peerDependenciesMeta: - '@cloudflare/workers-types': - optional: true - - ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - youch-core@0.3.3: - resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} - - youch@4.1.0-beta.10: - resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} - -snapshots: - - '@cloudflare/kv-asset-handler@0.4.2': {} - - '@cloudflare/unenv-preset@2.16.0(unenv@2.0.0-rc.24)(workerd@1.20260415.1)': - dependencies: - unenv: 2.0.0-rc.24 - optionalDependencies: - workerd: 1.20260415.1 - - '@cloudflare/workerd-darwin-64@1.20260415.1': - optional: true - - '@cloudflare/workerd-darwin-arm64@1.20260415.1': - optional: true - - '@cloudflare/workerd-linux-64@1.20260415.1': - optional: true - - '@cloudflare/workerd-linux-arm64@1.20260415.1': - optional: true - - '@cloudflare/workerd-windows-64@1.20260415.1': - optional: true - - '@cloudflare/workers-types@4.20260420.1': {} - - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - - '@emnapi/runtime@1.10.0': - dependencies: - tslib: 2.8.1 - optional: true - - '@esbuild/aix-ppc64@0.27.3': - optional: true - - '@esbuild/android-arm64@0.27.3': - optional: true - - '@esbuild/android-arm@0.27.3': - optional: true - - '@esbuild/android-x64@0.27.3': - optional: true - - '@esbuild/darwin-arm64@0.27.3': - optional: true - - '@esbuild/darwin-x64@0.27.3': - optional: true - - '@esbuild/freebsd-arm64@0.27.3': - optional: true - - '@esbuild/freebsd-x64@0.27.3': - optional: true - - '@esbuild/linux-arm64@0.27.3': - optional: true - - '@esbuild/linux-arm@0.27.3': - optional: true - - '@esbuild/linux-ia32@0.27.3': - optional: true - - '@esbuild/linux-loong64@0.27.3': - optional: true - - '@esbuild/linux-mips64el@0.27.3': - optional: true - - '@esbuild/linux-ppc64@0.27.3': - optional: true - - '@esbuild/linux-riscv64@0.27.3': - optional: true - - '@esbuild/linux-s390x@0.27.3': - optional: true - - '@esbuild/linux-x64@0.27.3': - optional: true - - '@esbuild/netbsd-arm64@0.27.3': - optional: true - - '@esbuild/netbsd-x64@0.27.3': - optional: true - - '@esbuild/openbsd-arm64@0.27.3': - optional: true - - '@esbuild/openbsd-x64@0.27.3': - optional: true - - '@esbuild/openharmony-arm64@0.27.3': - optional: true - - '@esbuild/sunos-x64@0.27.3': - optional: true - - '@esbuild/win32-arm64@0.27.3': - optional: true - - '@esbuild/win32-ia32@0.27.3': - optional: true - - '@esbuild/win32-x64@0.27.3': - optional: true - - '@img/colour@1.1.0': {} - - '@img/sharp-darwin-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.4 - optional: true - - '@img/sharp-darwin-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.4 - optional: true - - '@img/sharp-libvips-darwin-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-darwin-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm@1.2.4': - optional: true - - '@img/sharp-libvips-linux-ppc64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-riscv64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-s390x@1.2.4': - optional: true - - '@img/sharp-libvips-linux-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - optional: true - - '@img/sharp-linux-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.4 - optional: true - - '@img/sharp-linux-arm@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.4 - optional: true - - '@img/sharp-linux-ppc64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.4 - optional: true - - '@img/sharp-linux-riscv64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.2.4 - optional: true - - '@img/sharp-linux-s390x@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.4 - optional: true - - '@img/sharp-linux-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - optional: true - - '@img/sharp-wasm32@0.34.5': - dependencies: - '@emnapi/runtime': 1.10.0 - optional: true - - '@img/sharp-win32-arm64@0.34.5': - optional: true - - '@img/sharp-win32-ia32@0.34.5': - optional: true - - '@img/sharp-win32-x64@0.34.5': - optional: true - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/sourcemap-codec@1.5.5': {} - - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - - '@poppinss/colors@4.1.6': - dependencies: - kleur: 4.1.5 - - '@poppinss/dumper@0.6.5': - dependencies: - '@poppinss/colors': 4.1.6 - '@sindresorhus/is': 7.2.0 - supports-color: 10.2.2 - - '@poppinss/exception@1.2.3': {} - - '@sindresorhus/is@7.2.0': {} - - '@speed-highlight/core@1.2.15': {} - - blake3-wasm@2.1.5: {} - - cookie@1.1.1: {} - - detect-libc@2.1.2: {} - - error-stack-parser-es@1.0.5: {} - - esbuild@0.27.3: - 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 - - fsevents@2.3.3: - optional: true - - hono@4.12.14: {} - - kleur@4.1.5: {} - - miniflare@4.20260415.0: - dependencies: - '@cspotcode/source-map-support': 0.8.1 - sharp: 0.34.5 - undici: 7.24.8 - workerd: 1.20260415.1 - ws: 8.18.0 - youch: 4.1.0-beta.10 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - path-to-regexp@6.3.0: {} - - pathe@2.0.3: {} - - semver@7.7.4: {} - - sharp@0.34.5: - dependencies: - '@img/colour': 1.1.0 - detect-libc: 2.1.2 - semver: 7.7.4 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.5 - '@img/sharp-darwin-x64': 0.34.5 - '@img/sharp-libvips-darwin-arm64': 1.2.4 - '@img/sharp-libvips-darwin-x64': 1.2.4 - '@img/sharp-libvips-linux-arm': 1.2.4 - '@img/sharp-libvips-linux-arm64': 1.2.4 - '@img/sharp-libvips-linux-ppc64': 1.2.4 - '@img/sharp-libvips-linux-riscv64': 1.2.4 - '@img/sharp-libvips-linux-s390x': 1.2.4 - '@img/sharp-libvips-linux-x64': 1.2.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - '@img/sharp-linux-arm': 0.34.5 - '@img/sharp-linux-arm64': 0.34.5 - '@img/sharp-linux-ppc64': 0.34.5 - '@img/sharp-linux-riscv64': 0.34.5 - '@img/sharp-linux-s390x': 0.34.5 - '@img/sharp-linux-x64': 0.34.5 - '@img/sharp-linuxmusl-arm64': 0.34.5 - '@img/sharp-linuxmusl-x64': 0.34.5 - '@img/sharp-wasm32': 0.34.5 - '@img/sharp-win32-arm64': 0.34.5 - '@img/sharp-win32-ia32': 0.34.5 - '@img/sharp-win32-x64': 0.34.5 - - supports-color@10.2.2: {} - - tslib@2.8.1: - optional: true - - typescript@5.9.3: {} - - undici@7.24.8: {} - - unenv@2.0.0-rc.24: - dependencies: - pathe: 2.0.3 - - workerd@1.20260415.1: - optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260415.1 - '@cloudflare/workerd-darwin-arm64': 1.20260415.1 - '@cloudflare/workerd-linux-64': 1.20260415.1 - '@cloudflare/workerd-linux-arm64': 1.20260415.1 - '@cloudflare/workerd-windows-64': 1.20260415.1 - - wrangler@4.83.0(@cloudflare/workers-types@4.20260420.1): - dependencies: - '@cloudflare/kv-asset-handler': 0.4.2 - '@cloudflare/unenv-preset': 2.16.0(unenv@2.0.0-rc.24)(workerd@1.20260415.1) - blake3-wasm: 2.1.5 - esbuild: 0.27.3 - miniflare: 4.20260415.0 - path-to-regexp: 6.3.0 - unenv: 2.0.0-rc.24 - workerd: 1.20260415.1 - optionalDependencies: - '@cloudflare/workers-types': 4.20260420.1 - fsevents: 2.3.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - ws@8.18.0: {} - - youch-core@0.3.3: - dependencies: - '@poppinss/exception': 1.2.3 - error-stack-parser-es: 1.0.5 - - youch@4.1.0-beta.10: - dependencies: - '@poppinss/colors': 4.1.6 - '@poppinss/dumper': 0.6.5 - '@speed-highlight/core': 1.2.15 - cookie: 1.1.1 - youch-core: 0.3.3 diff --git a/.component-builds/km_writer/pnpm-workspace.yaml b/.component-builds/km_writer/pnpm-workspace.yaml deleted file mode 100644 index d4d28e2..0000000 --- a/.component-builds/km_writer/pnpm-workspace.yaml +++ /dev/null @@ -1,8 +0,0 @@ -allowBuilds: - esbuild: true - sharp: true - workerd: true -onlyBuiltDependencies: - - esbuild - - sharp - - workerd diff --git a/.component-builds/km_writer/src/index.ts b/.component-builds/km_writer/src/index.ts deleted file mode 100644 index 67a169e..0000000 --- a/.component-builds/km_writer/src/index.ts +++ /dev/null @@ -1,83 +0,0 @@ -/** - * arcrun API component Worker (km_writer) - * - * POST / → JSON input → WASM (WASI preview1 stdin/stdout) → JSON output - * - * 提供 http_request host function,讓 WASM 零件呼叫 Mira /km/* API。 - */ - -import componentWasm from '../component.wasm' assert { type: 'webassembly' }; -import { Hono } from 'hono'; -import { cors } from 'hono/cors'; -import { createWasiShim, type WasiHostFunctions } from '../../../cypher-executor/src/lib/wasi-shim'; - -const app = new Hono(); -app.use('*', cors()); - -app.get('/', (c) => c.json({ ok: true, component: 'km_writer' })); - -app.post('/', async (c) => { - let input: unknown; - try { - input = await c.req.json(); - } catch { - return c.json({ success: false, error: 'request body must be JSON' }, 400); - } - - try { - const result = await runWasm(input); - return c.json(result); - } catch (e) { - return c.json( - { success: false, error: e instanceof Error ? e.message : String(e) }, - 500, - ); - } -}); - -export default app; - -async function runWasm(input: unknown): Promise { - const hostFunctions: WasiHostFunctions = { - http_request: async (url, method, headersJson, body) => { - const headers: Record = {}; - if (headersJson) { - try { - const parsed = JSON.parse(headersJson); - if (parsed && typeof parsed === 'object') { - for (const [k, v] of Object.entries(parsed as Record)) { - if (typeof v === 'string') headers[k] = v; - } - } - } catch { - // ignore header parse errors - } - } - const init: RequestInit = { method, headers }; - if (body && method.toUpperCase() !== 'GET' && method.toUpperCase() !== 'HEAD') { - init.body = body; - } - const res = await fetch(url, init); - const text = await res.text(); - // 修架構債(同 http_request):非 2xx 包成帶 "error" key 的 envelope, - // 讓 WASM 端既有的 error 判定正確識別失敗(原本只回 body 丟掉 status → 4xx 被判 success)。 - if (!res.ok) { - return JSON.stringify({ error: `HTTP ${res.status}`, status: res.status, body: text }); - } - return text; - }, - }; - - const shim = createWasiShim(JSON.stringify(input), hostFunctions); - - const instance = await WebAssembly.instantiate( - componentWasm as WebAssembly.Module, - shim.imports, - ); - shim.setMemory(instance.exports.memory as WebAssembly.Memory); - await shim.run(instance); - - const stdout = shim.getStdout().trim(); - if (!stdout) throw new Error('WASM component produced no output'); - return JSON.parse(stdout); -} diff --git a/.component-builds/km_writer/tsconfig.json b/.component-builds/km_writer/tsconfig.json deleted file mode 100644 index b65fda7..0000000 --- a/.component-builds/km_writer/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ES2022", - "moduleResolution": "bundler", - "lib": ["ES2022"], - "types": ["@cloudflare/workers-types"], - "strict": true, - "noEmit": true - } -} diff --git a/.component-builds/km_writer/wrangler.toml b/.component-builds/km_writer/wrangler.toml deleted file mode 100644 index ef55e83..0000000 --- a/.component-builds/km_writer/wrangler.toml +++ /dev/null @@ -1,12 +0,0 @@ -name = "arcrun-km-writer" -main = "src/index.ts" -compatibility_date = "2025-02-19" -compatibility_flags = ["nodejs_compat"] -workers_dev = true - -[vars] -COMPONENT_ID = "km_writer" - -[[routes]] -pattern = "km-writer.arcrun.dev/*" -zone_name = "arcrun.dev" diff --git a/registry/components/kbdb_upsert_block/component.contract.yaml b/registry/components/kbdb_upsert_block/component.contract.yaml deleted file mode 100644 index 533ae3f..0000000 --- a/registry/components/kbdb_upsert_block/component.contract.yaml +++ /dev/null @@ -1,90 +0,0 @@ -canonical_id: "kbdb_upsert_block" -display_name: "KBDB Upsert Block" -category: "data" -version: "v1" -wasi_target: "preview1" -stability: "floating" -runtime_compat: - - "cf-workers" - - "workerd" - - "wazero" -constraints: - max_size_kb: 2048 - max_cold_start_ms: 50 - no_network_syscall: false - no_filesystem_syscall: true - io_model: "stdin_stdout_json" -input_schema: - type: object - required: [api_key, page_name, content] - properties: - api_key: - type: string - description: KBDB partner key(ak_xxx) - page_name: - type: string - description: 當 idempotency key。內部用 GET /blocks?page_name= 查找。 - content: - type: string - description: block 內容(PATCH 時覆寫,CREATE 時新建) - type: - type: string - description: block type(建立時用,PATCH 時忽略) - parent_id: - type: string - description: 父 block id(建立時用,PATCH 時忽略) - user_id: - type: string - description: 建立時帶入 + lookup 時用來 filter(同 page_name 多 user 共存場景) - source: - type: string - description: 來源標記 - tags_json: - type: string - description: tags JSON 字串(PATCH 時轉 array、CREATE 時直傳) - kbdb_url: - type: string - description: KBDB API base(預設 https://kbdb.finally.click) -output_schema: - type: object - properties: - success: - type: boolean - action: - type: string - enum: [created, patched] - description: 實際做了哪個動作 - data: - type: object - description: KBDB 回傳(含 block id 等) - error: - type: string - phase: - type: string - enum: [lookup, patch, create] - description: 出錯在哪個階段 -gherkin_tests: - - scenario: "缺 page_name" - given: '{"api_key":"ak_x","content":"hi"}' - then_contains: '"success":false' - - scenario: "建立新 block" - given: '{"api_key":"ak_x","page_name":"new-page-uniq","content":"hello"}' - then_contains: '"action":"created"' - - scenario: "PATCH 既有 block" - given: '{"api_key":"ak_x","page_name":"existing-page","content":"updated"}' - then_contains: '"action":"patched"' -tags: [data, storage, kbdb, upsert, primitive, idempotent] -description: | - Upsert:用 page_name 當 idempotency key。內部 GET 找有沒有同 page_name 的 block, - 找到就 PATCH 不到就 POST 新建。解 arcrun workflow 缺 IF/branch 能力的缺口 - (arcrun.md P1 #1)。mira 7B.3f index-entry per-entity 維護是第一個使用者。 -config_example: | - upsert_index_entry: - api_key: "{{api_key}}" - page_name: "index-{{entity}}" - parent_id: "{{mira_wiki_index_entities_id}}" - type: "index-entry" - user_id: "inkstone_mira_tools" - source: "ai-canon-wiki" - content: "{{compose_index_entry.data.text}}" - tags_json: '["mira-wiki", "ai-generated", "index"]' diff --git a/registry/components/kbdb_upsert_block/go.mod b/registry/components/kbdb_upsert_block/go.mod deleted file mode 100644 index 9c91451..0000000 --- a/registry/components/kbdb_upsert_block/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module kbdb_upsert_block - -go 1.21 diff --git a/registry/components/kbdb_upsert_block/main.go b/registry/components/kbdb_upsert_block/main.go deleted file mode 100644 index a4594ee..0000000 --- a/registry/components/kbdb_upsert_block/main.go +++ /dev/null @@ -1,280 +0,0 @@ -// kbdb_upsert_block — 用 page_name 當 idempotency key 做 upsert -// 內部:GET /blocks?page_name=X → user_id filter → 找到 PATCH /blocks/:id 沒找到 POST /blocks -// 解 arcrun workflow 沒 IF/branch 能力的缺口(arcrun.md P1 #1) -// 對應 SDD:polaris/mira/.agents/specs/mira-app/design.md §3.5.12.4.1 -// -//go:build tinygo - -package main - -import ( - "encoding/json" - "io" - "os" - "strconv" - "unsafe" -) - -//go:wasmimport u6u http_request -func hostHttpRequest( - urlPtr uintptr, urlLen uint32, - methodPtr uintptr, methodLen uint32, - headersPtr uintptr, headersLen uint32, - bodyPtr uintptr, bodyLen uint32, - outPtr uintptr, outLenPtr uintptr, -) uint32 - -type Input struct { - KBDBUrl string `json:"kbdb_url"` // optional - APIKey string `json:"api_key"` // 必填 - PageName string `json:"page_name"` // 必填,當 idempotency key - Content string `json:"content"` // 必填 - Type string `json:"type"` // optional(建立時用,PATCH 時忽略) - ParentID string `json:"parent_id"` // optional(建立時用,PATCH 時忽略) - UserID string `json:"user_id"` // optional(建立時用 + lookup filter) - Source string `json:"source"` // optional - TagsJSON string `json:"tags_json"` // optional(完整覆寫) - CreateOnly bool `json:"create_only"` // 2026-05-17 加:若 true + 已存在 → 不 PATCH,回 action="exists" - // 用於 stub creation 場景(避免 stub 覆寫已存在的 full wiki) -} - -var dummy [1]byte - -func safePtr(b []byte) (uintptr, uint32) { - if len(b) == 0 { - return uintptr(unsafe.Pointer(&dummy[0])), 0 - } - return uintptr(unsafe.Pointer(&b[0])), uint32(len(b)) -} - -func writeError(msg string) { - out, _ := json.Marshal(map[string]interface{}{"success": false, "error": msg}) - os.Stdout.Write(out) -} - -func writeResult(action string, data map[string]interface{}) { - out, _ := json.Marshal(map[string]interface{}{ - "success": true, - "action": action, - "data": data, - }) - os.Stdout.Write(out) -} - -// urlEncode:跟 kbdb_get 一致,避免引入 net/url -func urlEncode(s string) string { - var out []byte - for i := 0; i < len(s); i++ { - c := s[i] - if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || - c == '-' || c == '_' || c == '.' || c == '~' { - out = append(out, c) - } else { - const hex = "0123456789ABCDEF" - out = append(out, '%', hex[c>>4], hex[c&0x0f]) - } - } - return string(out) -} - -func httpCall(method, url string, headers map[string]string, body []byte) ([]byte, uint32) { - headersBytes, _ := json.Marshal(headers) - urlBytes := []byte(url) - methodBytes := []byte(method) - - outBuf := make([]byte, 1<<20) // 1MB - var outLen uint32 - - urlPtr, urlLen := safePtr(urlBytes) - methodPtr, methodLen := safePtr(methodBytes) - headersPtr, headersLenU := safePtr(headersBytes) - bodyPtr, bodyLenU := safePtr(body) - - result := hostHttpRequest( - urlPtr, urlLen, - methodPtr, methodLen, - headersPtr, headersLenU, - bodyPtr, bodyLenU, - uintptr(unsafe.Pointer(&outBuf[0])), uintptr(unsafe.Pointer(&outLen)), - ) - return outBuf[:outLen], result -} - -func main() { - raw, err := io.ReadAll(os.Stdin) - if err != nil { - writeError("failed to read stdin: " + err.Error()) - return - } - - var input Input - if err := json.Unmarshal(raw, &input); err != nil { - writeError("invalid input JSON: " + err.Error()) - return - } - - if input.APIKey == "" { - writeError("api_key 必填") - return - } - if input.PageName == "" { - writeError("page_name 必填(upsert 的 idempotency key)") - return - } - if input.Content == "" { - writeError("content 必填") - return - } - - kbdbURL := input.KBDBUrl - if kbdbURL == "" { - kbdbURL = "https://kbdb.finally.click" - } - - headers := map[string]string{ - "Authorization": "Bearer " + input.APIKey, - } - - // ── Step 1:lookup by page_name ──────────────────────────────────── - lookupURL := kbdbURL + "/blocks?page_name=" + urlEncode(input.PageName) + - "&limit=" + strconv.Itoa(10) - lookupResp, callResult := httpCall("GET", lookupURL, headers, nil) - if callResult != 0 { - writeError("KBDB lookup failed (host_http_request returned non-zero)") - return - } - - var lookupParsed struct { - Blocks []map[string]interface{} `json:"blocks"` - Count int `json:"count"` - Error interface{} `json:"error"` - } - if err := json.Unmarshal(lookupResp, &lookupParsed); err != nil { - writeError("KBDB lookup returned non-JSON: " + string(lookupResp)) - return - } - if lookupParsed.Error != nil { - errBytes, _ := json.Marshal(map[string]interface{}{ - "success": false, - "error": lookupParsed.Error, - "phase": "lookup", - }) - os.Stdout.Write(errBytes) - return - } - - // ── Step 2:找符合 user_id 的第一筆 ────────────────────────────── - var existing map[string]interface{} - for _, b := range lookupParsed.Blocks { - if input.UserID == "" { - existing = b - break - } - if uid, ok := b["user_id"].(string); ok && uid == input.UserID { - existing = b - break - } - } - - // ── Step 3:分支寫入 ─────────────────────────────────────────────── - postHeaders := map[string]string{ - "Content-Type": "application/json", - "Authorization": "Bearer " + input.APIKey, - } - - if existing != nil { - // CreateOnly 模式:已存在 → 不動,回 action="exists"(給 stub creation 用, - // 避免後續 raw 提到同 entity 時把完整 wiki 覆寫成 stub) - if input.CreateOnly { - writeResult("exists", existing) - return - } - - // PATCH 路徑 - existingID, _ := existing["id"].(string) - if existingID == "" { - writeError("lookup 找到 block 但 id 為空") - return - } - - patchBody := make(map[string]interface{}) - patchBody["content"] = input.Content - if input.Source != "" { - patchBody["source"] = input.Source - } - if input.TagsJSON != "" { - // PATCH endpoint 用 tags array 不是 tags_json string - var tagsArr []string - if err := json.Unmarshal([]byte(input.TagsJSON), &tagsArr); err == nil { - patchBody["tags"] = tagsArr - } - } - patchBodyBytes, _ := json.Marshal(patchBody) - - patchURL := kbdbURL + "/blocks/" + existingID - patchResp, callResult := httpCall("PATCH", patchURL, postHeaders, patchBodyBytes) - if callResult != 0 { - writeError("KBDB PATCH failed (host_http_request returned non-zero)") - return - } - var patchParsed map[string]interface{} - if err := json.Unmarshal(patchResp, &patchParsed); err != nil { - writeError("KBDB PATCH returned non-JSON: " + string(patchResp)) - return - } - if _, hasErr := patchParsed["error"]; hasErr { - errBytes, _ := json.Marshal(map[string]interface{}{ - "success": false, - "error": patchParsed["error"], - "phase": "patch", - }) - os.Stdout.Write(errBytes) - return - } - writeResult("patched", patchParsed) - return - } - - // CREATE 路徑 - postBody := make(map[string]interface{}) - postBody["content"] = input.Content - postBody["page_name"] = input.PageName - if input.Type != "" { - postBody["type"] = input.Type - } - if input.ParentID != "" { - postBody["parent_id"] = input.ParentID - } - if input.UserID != "" { - postBody["user_id"] = input.UserID - } - if input.Source != "" { - postBody["source"] = input.Source - } - if input.TagsJSON != "" { - postBody["tags_json"] = input.TagsJSON - } - postBodyBytes, _ := json.Marshal(postBody) - - postURL := kbdbURL + "/blocks" - postResp, callResult := httpCall("POST", postURL, postHeaders, postBodyBytes) - if callResult != 0 { - writeError("KBDB POST failed (host_http_request returned non-zero)") - return - } - var postParsed map[string]interface{} - if err := json.Unmarshal(postResp, &postParsed); err != nil { - writeError("KBDB POST returned non-JSON: " + string(postResp)) - return - } - if _, hasErr := postParsed["error"]; hasErr { - errBytes, _ := json.Marshal(map[string]interface{}{ - "success": false, - "error": postParsed["error"], - "phase": "create", - }) - os.Stdout.Write(errBytes) - return - } - writeResult("created", postParsed) -} diff --git a/registry/components/km_writer/component.contract.yaml b/registry/components/km_writer/component.contract.yaml deleted file mode 100644 index 6de54b2..0000000 --- a/registry/components/km_writer/component.contract.yaml +++ /dev/null @@ -1,67 +0,0 @@ -canonical_id: "km_writer" -display_name: "KM Writer" -category: "api" -version: "v1" -wasi_target: "preview1" -stability: "floating" -runtime_compat: - - "cf-workers" - - "workerd" -constraints: - max_size_kb: 2048 - max_cold_start_ms: 50 - no_network_syscall: false - no_filesystem_syscall: true - io_model: "stdin_stdout_json" -input_schema: - type: object - required: [action, mira_url, token] - properties: - action: - type: string - description: "操作類型:read_journal | read_journal_date | append_journal | list_pages | read_page | write_page" - enum: [read_journal, read_journal_date, append_journal, list_pages, read_page, write_page] - mira_url: - type: string - description: "Mira 服務基礎 URL(例:https://mira.uncle6.me)" - token: - type: string - description: "Mira MIRA_TOKEN(Bearer token)" - content: - type: string - description: "內容(append_journal / write_page 時必填)" - timestamp: - type: string - description: "ISO 8601 時間戳(append_journal 時選填,影響日期和時間顯示)" - date: - type: string - description: "日期 YYYY-MM-DD(read_journal_date 時必填)" - name: - type: string - description: "頁面名稱(read_page / write_page 時必填)" -output_schema: - type: object - properties: - success: - type: boolean - data: - type: object - description: "Mira API 回應資料" - error: - type: string - description: "錯誤訊息(success=false 時)" -gherkin_tests: - - scenario: "缺少 action" - given: '{"mira_url":"https://mira.uncle6.me","token":"abc"}' - then_contains: '{"success":false' - - scenario: "缺少 token" - given: '{"action":"list_pages","mira_url":"https://mira.uncle6.me"}' - then_contains: '{"success":false' -tags: [km, journal, logseq, mira, knowledge-management] -description: "讀寫 Mira leo-graph 的 journals 和 pages。透過 host function 呼叫 Mira /km/* API,支援讀取、新增日誌條目,以及讀寫頁面。" -config_example: | - append_to_journal: - action: "append_journal" - mira_url: "https://mira.uncle6.me" - token: "" - content: "今天完成了 arcrun km_writer 元件" diff --git a/registry/components/km_writer/go.mod b/registry/components/km_writer/go.mod deleted file mode 100644 index 185c5ee..0000000 --- a/registry/components/km_writer/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module component - -go 1.21 diff --git a/registry/components/km_writer/main.go b/registry/components/km_writer/main.go deleted file mode 100644 index a9f3b60..0000000 --- a/registry/components/km_writer/main.go +++ /dev/null @@ -1,177 +0,0 @@ -// km_writer — 讀寫 Mira leo-graph(journals + pages) -// 透過 host function 呼叫 Mira /km/* API -// -//go:build tinygo - -package main - -import ( - "encoding/json" - "fmt" - "io" - "os" - "unsafe" -) - -//go:wasmimport u6u http_request -func hostHttpRequest( - urlPtr uintptr, urlLen uint32, - methodPtr uintptr, methodLen uint32, - headersPtr uintptr, headersLen uint32, - bodyPtr uintptr, bodyLen uint32, - outPtr uintptr, outLenPtr uintptr, -) uint32 - -// Input actions: -// read_journal — GET today's journal (requires: mira_url, token) -// read_journal_date — GET journal by date (requires: mira_url, token, date) -// append_journal — POST append entry (requires: mira_url, token, content; optional: timestamp) -// list_pages — GET all pages (requires: mira_url, token) -// read_page — GET page by name (requires: mira_url, token, name) -// write_page — PUT write page (requires: mira_url, token, name, content) - -type Input struct { - Action string `json:"action"` - MiraURL string `json:"mira_url"` - Token string `json:"token"` - Content string `json:"content"` - Timestamp string `json:"timestamp"` - Date string `json:"date"` - Name string `json:"name"` -} - -func main() { - raw, err := io.ReadAll(os.Stdin) - if err != nil { - writeError("failed to read stdin: " + err.Error()) - return - } - var inp Input - if err := json.Unmarshal(raw, &inp); err != nil { - writeError("invalid input JSON: " + err.Error()) - return - } - if inp.Action == "" { - writeError("action 必填") - return - } - if inp.MiraURL == "" { - writeError("mira_url 必填") - return - } - if inp.Token == "" { - writeError("token 必填") - return - } - - authHeader := fmt.Sprintf(`{"Authorization":"Bearer %s","Content-Type":"application/json"}`, inp.Token) - - switch inp.Action { - case "read_journal": - result := doRequest(inp.MiraURL+"/km/journal", "GET", authHeader, "") - os.Stdout.Write(result) - - case "read_journal_date": - if inp.Date == "" { - writeError("date 必填(格式 YYYY-MM-DD)") - return - } - result := doRequest(inp.MiraURL+"/km/journal/"+inp.Date, "GET", authHeader, "") - os.Stdout.Write(result) - - case "append_journal": - if inp.Content == "" { - writeError("content 必填") - return - } - bodyMap := map[string]string{"content": inp.Content} - if inp.Timestamp != "" { - bodyMap["timestamp"] = inp.Timestamp - } - bodyBytes, _ := json.Marshal(bodyMap) - result := doRequest(inp.MiraURL+"/km/journal", "POST", authHeader, string(bodyBytes)) - os.Stdout.Write(result) - - case "list_pages": - result := doRequest(inp.MiraURL+"/km/pages", "GET", authHeader, "") - os.Stdout.Write(result) - - case "read_page": - if inp.Name == "" { - writeError("name 必填") - return - } - result := doRequest(inp.MiraURL+"/km/page/"+inp.Name, "GET", authHeader, "") - os.Stdout.Write(result) - - case "write_page": - if inp.Name == "" { - writeError("name 必填") - return - } - if inp.Content == "" { - writeError("content 必填") - return - } - bodyMap := map[string]string{"content": inp.Content} - bodyBytes, _ := json.Marshal(bodyMap) - result := doRequest(inp.MiraURL+"/km/page/"+inp.Name, "PUT", authHeader, string(bodyBytes)) - os.Stdout.Write(result) - - default: - writeError("未知 action: " + inp.Action) - } -} - -func doRequest(url, method, headersJSON, body string) []byte { - urlBytes := []byte(url) - methodBytes := []byte(method) - headersBytes := []byte(headersJSON) - bodyBytes := []byte(body) - - outBuf := make([]byte, 131072) // 128KB - var outLen uint32 - - if len(bodyBytes) == 0 { - bodyBytes = []byte{} - } - - var bodyPtr uintptr - var bodyLen uint32 - if len(bodyBytes) > 0 { - bodyPtr = uintptr(unsafe.Pointer(&bodyBytes[0])) - bodyLen = uint32(len(bodyBytes)) - } - - code := hostHttpRequest( - uintptr(unsafe.Pointer(&urlBytes[0])), uint32(len(urlBytes)), - uintptr(unsafe.Pointer(&methodBytes[0])), uint32(len(methodBytes)), - uintptr(unsafe.Pointer(&headersBytes[0])), uint32(len(headersBytes)), - bodyPtr, bodyLen, - uintptr(unsafe.Pointer(&outBuf[0])), uintptr(unsafe.Pointer(&outLen)), - ) - - if code != 0 { - out, _ := json.Marshal(map[string]interface{}{"success": false, "error": "HTTP request failed"}) - return out - } - - responseStr := string(outBuf[:outLen]) - - // Try to parse the response as JSON to forward it - var parsed interface{} - if err := json.Unmarshal([]byte(responseStr), &parsed); err != nil { - // Not JSON — wrap it - out, _ := json.Marshal(map[string]interface{}{"success": true, "data": responseStr}) - return out - } - - // Forward the parsed response as-is, wrapped in success - out, _ := json.Marshal(map[string]interface{}{"success": true, "data": parsed}) - return out -} - -func writeError(msg string) { - out, _ := json.Marshal(map[string]interface{}{"success": false, "error": msg}) - os.Stdout.Write(out) -} diff --git a/registry/examples/km-wiki-ingest/description.md b/registry/examples/km-wiki-ingest/description.md deleted file mode 100644 index da1660c..0000000 --- a/registry/examples/km-wiki-ingest/description.md +++ /dev/null @@ -1,74 +0,0 @@ -# km-wiki-ingest — 機械式 wiki 卡片 → KBDB ingest(Arcrun#8 / 頂層 SDD T2–T4) - -> Phase A 產物:機械 ingest 邏輯 + 乾跑證據 + workflow 設計。**不部署、不寫 live KBDB。** - -## 解決什麼問題 - -把各 repo 的 `system-dev/wiki/cards/**/*.md`(人工精耕卡)**機械地**(無 LLM)灌進 leo21c KBDB: - -- **卡片 → base entry**(`metadata.embed=true`,供語意搜尋)。 -- **`## 實體` → graph node**;**`## 關聯` 的 typed-edge(`A >> 關係 >> B`)與 `[[wikilink]]` → graph triplet**。 - -取代舊 `kbdb-ingest-plugin/scripts/ingest-cli.mjs` 的 `raw → Haiku → 三元組` 路:新路純解析卡片內既有結構,**決定性、零 token、零幻覺**。 - -## 形式選擇與理由(給總管) - -**形式 = Arcrun workflow(YAML 編排)+ 通用 `code` 零件(sandbox inline JS,Arcrun#10)承載卡片→envelope 解析 + 現成零件(`cron` / `http_request` / `foreach_control` / `kbdb_upsert_block`)。** 不再鑄 domain 零件 `km_wiki_card_parse`(Arcrun#10 裁定:一次性解析走通用逃生口)。 - -理由: - -1. **編排本來就是 arcrun 的主場**:cron 限速 drain、Gitea webhook 只吃 delta、foreach 小批、冪等 upsert——這些跟現成零件 1:1 對得上,且 leo 要「Arcrun workflow 慢慢做」、arcrun 哲學禁一次性腳本。 -2. **arcrun 唯一缺的是「卡片 → envelope」的解析**。那是一段**決定性純轉換**(無 LLM、無網路、無檔案)——正好是 `code` 零件 sandbox 的理想形狀(`stdin_stdout_json` + `no_network_syscall` + `no_filesystem_syscall`)。用通用 `code` 節點內聯這段 JS(而非鑄 domain 零件、也非在 YAML 裡塞 `string_ops` 正則):workflow 可讀、解析可單元測試、且 registry 不因一次性邏輯增生 domain 零件。 -3. **小批是結構性的,不是靠祈禱**:一卡一 tick,每卡在 graph worker 的 fan-out ≈ `7+4N+M` subrequest(notes 卡 N≈4/M≈5 → est 28~33,穩壓 CF 50 頂下);`code` 節點內聯解析會**預先把超大卡以 `source_uri` anchor 分段**,任何單一 graph 呼叫都不破頂。 - -**Phase A 交付**:純解析+打包核心(`lib/card-to-envelope.mjs`,現在就能跑,= `code` 節點內聯 JS 的權威來源)+乾跑驗證器(`lib/dry-run.mjs`,印出「將寫入什麼」)+本 workflow.yaml(parse_card = `code` 節點)。解析零件=通用 `code`(Arcrun#10 分支,已就緒待部署);本 example 不再自帶 domain 零件契約。部署被閘控,故 live 接線是「設計而非執行」。 - -## 診斷小結:fan-out 精確來源 + 小批為何解得掉 - -讀 `kbdb-graph-plugin` 現役寫入路徑(`triplet-ingest.ts` / `triplet-crud.ts` / `templates.ts` / `kbdb-client.ts`)逐行拆帳: - -``` -POST /triplets/ingest(graph worker 單次 invocation)對 base 的 subrequest: - ensurePluginTemplates(3) # 頂層一次 - + listRecordsByTemplate(1) # 抓同 source 現存 active(冪等分組) - + Σ_triplet [ createTriplet → ensurePluginTemplates(3) + createRecord(1) ] # ★ 每條邊重跑 ensure! - + persistNodes [ ensurePluginTemplates(3) + Σ_node createRecord(1) ] - + Σ_deprecated updateRecord(1) - = 7 + 4*N_triplets + M_nodes + D_deprecated -``` - -- **精確炸點還原**:07_01 單一 envelope 吞 `N=11, M=10, D=0` → `7+44+10 = 61 > 50` → 破頂半殘。**放大器=`createTriplet` 內每條邊都重呼 `ensurePluginTemplates`(3 個 GET)**,佔了 33/61。 -- **小批為何解得掉**:把「整檔一 envelope」改成「一卡一 envelope、必要時再 anchor 分段」,把 `N` 壓到讓 `7+4N+M ≤ 40`。notes 三卡實測 est 上限 = 33,全綠。超大卡(自測 20 邊/22 節點=114)→ 自動分 4 段,每段 ≤ 38。 -- **附帶建議(非本 Phase 必改)**:graph 端把 `createTriplet`/`persistNodes` 內重複的 `ensurePluginTemplates` 提到 ingest 入口只跑一次,可把每 envelope 省下 `3*(N+1)` 個 subrequest(單卡 est 33→約 18),批量還能更大。此為 graph-plugin 的可選優化,記此存查。 - -## 冪等設計 - -| 對象 | 冪等鍵 | 行為 | -|---|---|---| -| **entry** | `page_name`(穩定:`wikicard:/`)+ `metadata.content_hash` | 找到同 page_name:hash 相同 → skip;不同 → PATCH content(觸發重嵌)。沒有 → POST 新建。 | -| **triplet envelope** | `source.uri` + `source.content_hash` | graph 現役 per-source 冪等:同 hash 整包 no-op(`triplet-ingest.ts:65`)。 | -| **分段** | 各段 `source.uri = <基uri>#segNN` | 各段獨立 uri → 各自獨立冪等,**繞開 per-source content_hash 整包 skip**(否則同 uri 第 2 段起會被判定「已落地」而整包跳過)。節點只放進「首次引用它的段」,跨段不重送(避免 graph 重建 entity)。 | - -## 觸發(兩階段,對齊 SDD R3) - -- **Phase 0(一次性 backfill)**:`cron */2` 每 tick drain 一張卡(限速慢推),反覆跑到全庫清空。冪等 → 可續傳、重跑零寫入。 -- **穩態(日常增量)**:**Gitea push webhook → arcrun workflow**,只吃 `commits[].{added,modified}` 中的 `system-dev/wiki/cards/**/*.md`。⚠️ Gitea → Cloudflare(arcrun),**非 GitHub Actions**,不觸 GitHub flag 紅線(D4/D20)。量小、不撞頂、不限速。 - -## 乾跑證據(Phase A,不寫 live) - -``` -node lib/dry-run.mjs --repo-path --repo Leo/notes --self-test -``` - -對 `Leo/notes` 的 3 張卡實測:3 entries(embed=true)+ 3 envelopes、15 triplets、16 nodes; -**單次 graph 呼叫 subrequest 上限 = 33(< 50),無任一 envelope 破頂**。 -self-test 合成超大卡(不分段 est=114 會炸)→ 自動分 4 段、每段 ≤ 38,全綠。 - -## 待 live 部署 + 寫入(總管過 leo 閘用) - -1. **部署通用 `code` 零件**(Arcrun#10 分支 `feat/issue-10-code-component`,已就緒):`cd registry/components/code && npm install && npx wrangler deploy`(→ `code.arcrun.dev`)+ `register-component.sh code`。本 workflow 的 parse_card 以 `component: code` 引用它,解析 JS 已內聯在 workflow.yaml(= `lib/card-to-envelope.mjs` 邏輯)。不再部署 domain 零件 `km_wiki_card_parse`。 -2. **部署 workflow**:`km_wiki_ingest_drain`(cron drain);`wrangler` 直推 leo21c(**禁 `acr update`**——codeload 綁 GitHub 假綠,Arcrun#4)。 -3. **注入環境變數**(不放 repo):`repo=Leo/notes ref=main gitea_token kbdb_url=https://arcrun-kbdb.leo21c.workers.dev kbdb_api_key graph_url graph_api_key=leo`。`CLOUDFLARE_ACCOUNT_ID=leo21c`(別讓官方 58309b 污染)。 -4. **entry 寫入路徑確認**:若 `kbdb_upsert_block` 尚不透傳 `metadata_json`(需 `embed:true`/`content_hash`),entry 改用 `http_request` 直打 base `POST/PATCH /entries` 帶 `body_json.metadata_json`。 -5. **預期寫入量(Leo/notes 現況 3 卡)**:3 entries + 15 triplets + 16 node records(去重後更少);分 3 次 graph 呼叫(每次 ≤ 33 subrequest)+ 3 次 entry upsert。全庫鋪開時照 cron 一卡一 tick 慢推。 -6. **驗收**:ingest 後 `GET /embed/backfill/status` 應見 pending 上升→drain 後歸零、embedded 增加;三模式(關鍵字/語意/圖)curl 驗。 diff --git a/registry/examples/km-wiki-ingest/dry-run-evidence.json b/registry/examples/km-wiki-ingest/dry-run-evidence.json deleted file mode 100644 index ae2a62d..0000000 --- a/registry/examples/km-wiki-ingest/dry-run-evidence.json +++ /dev/null @@ -1,176 +0,0 @@ -{ - "repo": "Leo/notes", - "commit": "4b9a53c1c99d596c23b1b449fd79728610f6995b", - "budget": 40, - "ceiling": 50, - "cards": [ - { - "relPath": "system-dev/wiki/cards/notes/Gitea當後端編輯器全CF化網站構想.md", - "canonical": "Gitea當後端編輯器全CF化網站構想", - "entry": { - "page_name": "wikicard:Leo/notes/Gitea當後端編輯器全CF化網站構想", - "entry_type": "wiki_card", - "metadata.embed": true, - "content_hash": "64b402952fe0…", - "content_bytes": 2324, - "tags": [ - "系統設計", - "工具教學" - ] - }, - "envelopeCount": 1, - "envelopes": [ - { - "source.uri": "gitea:Leo/notes@system-dev/wiki/cards/notes/Gitea當後端編輯器全CF化網站構想.md", - "source.anchor": null, - "nodes": 6, - "triplets": 5, - "est_subrequests": 33, - "under_ceiling": true, - "sample_triplets": [ - "Gitea >> 類比於 >> WordPress (1)", - "Gitea >> 充當後端供稿給 >> Cloudflare Pages (1)", - "Quartz >> 目前負責轉譯給 >> Cloudflare Pages (1)", - "Cloudflare Artifacts >> 若提供 git 倉庫則可取代 >> Gitea (1)" - ], - "sample_nodes": [ - "Gitea — 可自架的 git 平台,編輯體驗近似 WordPress 後…", - "WordPress — 常見的內容管理後端,作為 Gitea 編輯體驗的類比對象。…", - "Cloudflare Pages — Cloudflare 的靜態站前端託管。…", - "Quartz — 目前把筆記轉成網站前端的工具。…" - ] - } - ] - }, - { - "relPath": "system-dev/wiki/cards/notes/Prompt能力即拆解自己邏輯的能力.md", - "canonical": "Prompt能力即拆解自己邏輯的能力", - "entry": { - "page_name": "wikicard:Leo/notes/Prompt能力即拆解自己邏輯的能力", - "entry_type": "wiki_card", - "metadata.embed": true, - "content_hash": "63296a663227…", - "content_bytes": 2109, - "tags": [ - "AI協作", - "工具教學", - "觀點主張" - ] - }, - "envelopeCount": 1, - "envelopes": [ - { - "source.uri": "gitea:Leo/notes@system-dev/wiki/cards/notes/Prompt能力即拆解自己邏輯的能力.md", - "source.anchor": null, - "nodes": 5, - "triplets": 5, - "est_subrequests": 32, - "under_ceiling": true, - "sample_triplets": [ - "Prompt 能力 >> 本質上等於 >> 邏輯拆解能力 (1)", - "邏輯拆解能力 >> 產出 >> pseudo code (1)", - "pseudo code >> 足以教會 >> AI (1)", - "Prompt能力即拆解自己邏輯的能力 >> 呼應 >> 程式化邏輯可圖解任何主題不限AI (1)" - ], - "sample_nodes": [ - "Prompt 能力 — 把腦中意圖轉成能指揮 AI 的指令的能力。…", - "邏輯拆解能力 — 把腦中隱性流程外顯成可陳述步驟的能力。…", - "pseudo code — 用類程式的步驟描述邏輯、尚未綁定特定語法的表達。…", - "AI — 需被人以指令/範例指揮才產出的生成模型。…" - ] - } - ] - }, - { - "relPath": "system-dev/wiki/cards/notes/程式化邏輯可圖解任何主題不限AI.md", - "canonical": "程式化邏輯可圖解任何主題不限AI", - "entry": { - "page_name": "wikicard:Leo/notes/程式化邏輯可圖解任何主題不限AI", - "entry_type": "wiki_card", - "metadata.embed": true, - "content_hash": "a9dbf5fc0f9a…", - "content_bytes": 2577, - "tags": [ - "工具教學", - "觀點主張", - "系統設計" - ] - }, - "envelopeCount": 1, - "envelopes": [ - { - "source.uri": "gitea:Leo/notes@system-dev/wiki/cards/notes/程式化邏輯可圖解任何主題不限AI.md", - "source.anchor": null, - "nodes": 5, - "triplets": 5, - "est_subrequests": 32, - "under_ceiling": true, - "sample_triplets": [ - "程式化邏輯 >> 可圖解 >> 亞洲金融風暴 (1)", - "流程圖解 >> 奠基於 >> 程式化邏輯 (1)", - "系統動力學 >> 類同於 >> 流程圖解 (1)", - "程式化邏輯可圖解任何主題不限AI >> 呼應 >> Prompt能力即拆解自己邏輯的能力 (1)" - ], - "sample_nodes": [ - "程式化邏輯 — 以程式的因果鏈結構來表述任一領域的邏輯。…", - "亞洲金融風暴 — 講者小 Lin 用長邏輯鏈敘述的金融事件案例。…", - "流程圖解 — 用 n8n 這類流程工具把邏輯視覺化講解的方法。…", - "系統動力學 — 以存量流量與回饋環圖解因果的建模工具。…" - ] - } - ] - } - ], - "totals": { - "cards": 3, - "entries_to_upsert": 3, - "triplet_envelopes": 3, - "total_triplets": 15, - "total_nodes": 16, - "max_est_subrequests_single_call": 33, - "ceiling": 50, - "budget": 40, - "any_envelope_over_ceiling": 0 - }, - "self_test": { - "note": "合成 20 邊 / 22 節點 的超大卡", - "if_single_envelope_est_subrequests": 114, - "would_crash_single": true, - "segmented_into": 4, - "per_segment": [ - { - "uri": "gitea:Leo/notes@system-dev/wiki/cards/notes/合成超大卡.md#seg01", - "anchor": "seg01", - "triplets": 6, - "nodes": 7, - "est_subrequests": 38, - "under_ceiling": true - }, - { - "uri": "gitea:Leo/notes@system-dev/wiki/cards/notes/合成超大卡.md#seg02", - "anchor": "seg02", - "triplets": 6, - "nodes": 6, - "est_subrequests": 37, - "under_ceiling": true - }, - { - "uri": "gitea:Leo/notes@system-dev/wiki/cards/notes/合成超大卡.md#seg03", - "anchor": "seg03", - "triplets": 6, - "nodes": 6, - "est_subrequests": 37, - "under_ceiling": true - }, - { - "uri": "gitea:Leo/notes@system-dev/wiki/cards/notes/合成超大卡.md#seg04", - "anchor": "seg04", - "triplets": 3, - "nodes": 3, - "est_subrequests": 22, - "under_ceiling": true - } - ], - "all_segments_under_ceiling": true - } -} diff --git a/registry/examples/km-wiki-ingest/lib/card-to-envelope.mjs b/registry/examples/km-wiki-ingest/lib/card-to-envelope.mjs deleted file mode 100644 index 51b8c8d..0000000 --- a/registry/examples/km-wiki-ingest/lib/card-to-envelope.mjs +++ /dev/null @@ -1,331 +0,0 @@ -// km-wiki-ingest — 機械式卡片→(entry + triplet envelope) 轉換核心(無 LLM、純函式) -// --------------------------------------------------------------------------- -// 取代舊 `kbdb-ingest-plugin/scripts/ingest-cli.mjs` 的 raw→Haiku 路: -// 舊路 = 讀裸筆記 → 呼叫 Haiku 萃 (s,p,o) → envelope(有 LLM、非決定性、耗 token)。 -// 新路 = 讀「已精耕卡片」(`system-dev/wiki/cards/**/*.md`)→ 直接解析卡片內既有的 -// `## 實體`(節點)、`## 關聯` 的 typed-edge(`A >> 關係 >> B`)與 `[[wikilink]]` -// → entry + triplet envelope。純機械、決定性、零 token。 -// -// 這支=通用 `code` 零件(Arcrun#10,sandbox inline JS)承載的解析邏輯本體。 -// workflow.yaml 的 parse_card 節點把本檔的 planCard 邏輯內聯進 code 零件的 config -// (去 import/export、raw NUL 分隔符改 u0000 escape、改用 code 沙箱注入的 sha256); -// 不再鑄 domain 零件 km_wiki_card_parse(Arcrun#10 裁定:一次性解析走通用逃生口)。 -// 本檔續留作「該內聯 JS 的權威來源 + 可單元測試的參考實作」(純函式、stdin→stdout JSON、無 fs/網路)。 -// -// 對齊契約:kbdb-ingest-plugin/contracts/ingest-candidate.json(envelope 形狀 / 禁止欄位)。 -// 對齊頂層 SDD:卡片→entry(metadata.embed=true,走 base API)、wikilink→triplet(走 graph)。 -// -// 鐵律:不碰儲存、不算向量、不建表。這支只「產出將寫入什麼」,實際 HTTP 由 workflow 打。 - -import { createHash } from 'node:crypto'; - -// --- CF subrequest 預算(防「Too many subrequests by single Worker invocation」,07_01 根因)--- -// -// graph worker 處理一次 POST /triplets/ingest 時,對 base 的每次 fetch = 1 subrequest。 -// 精確拆帳(讀 kbdb-graph-plugin/src/actions/triplet-ingest.ts + triplet-crud.ts + templates.ts): -// ingestEnvelope = ensurePluginTemplates(3) + listRecordsByTemplate(1) -// + Σ triplet [ createTriplet → ensurePluginTemplates(3) + createRecord(1) = 4 ] -// + persistNodes [ ensurePluginTemplates(3) + Σ node createRecord(1) ] -// + Σ deprecated updateRecord(1) -// ⟹ subreq(envelope) = 7 + 4*N_triplets + M_nodes + D_deprecated -// -// 07_01 實測炸點:N=11, M=10, D=0 → 7+44+10 = 61 > 50(CF 免費/bundled 上限)→ 炸半殘。 -// -// 對策 = 「一卡一 tick、每 envelope 壓在預算下、超大檔以 source_uri anchor 分段」。 -export const SUBREQ_CEILING = 50; // CF 單次 Worker invocation subrequest 硬上限(bundled) -export const SUBREQ_BUDGET = 40; // 我們的目標上限(留 10 給 D_deprecated 等變動) - -/** 精確估算「一個 envelope 打進 graph /triplets/ingest」會在 graph worker 內產生幾個 subrequest。 */ -export function estimateEnvelopeSubrequests(nTriplets, mNodes, dDeprecated = 0) { - return 7 + 4 * nTriplets + mNodes + dDeprecated; -} - -// --- sha256(content_hash 冪等鍵)--- -export function sha256(text) { - return createHash('sha256').update(text).digest('hex'); -} - -// --- frontmatter 解析(極簡 YAML:只吃我們卡片用到的 tags / gloss / pipeline_candidate)--- -function parseFrontmatter(md) { - const m = md.match(/^---\n([\s\S]*?)\n---\n?/); - if (!m) return { data: {}, body: md }; - const body = md.slice(m[0].length); - const data = {}; - for (const line of m[1].split('\n')) { - const kv = line.match(/^([A-Za-z_][\w-]*):\s*(.*)$/); - if (!kv) continue; - const key = kv[1]; - let val = kv[2].trim(); - if (val.startsWith('[') && val.endsWith(']')) { - // inline list: [a, b, c] - data[key] = val.slice(1, -1).split(',').map((s) => s.trim()).filter(Boolean); - } else if (val === 'true' || val === 'false') { - data[key] = val === 'true'; - } else { - data[key] = val; - } - } - return { data, body }; -} - -// --- 取某個 `## 標題` / `### 標題` 區塊的內文(到下一個同級或更高級標題為止)--- -function sectionBody(md, heading) { - // heading 例:'## 實體'、'### 內文知識關係' - const level = heading.match(/^#+/)[0].length; - const lines = md.split('\n'); - const out = []; - let inSec = false; - for (const line of lines) { - const h = line.match(/^(#+)\s+(.*)$/); - if (h) { - const thisLevel = h[1].length; - if (inSec) { - // 遇到同級或更高級標題 → 區塊結束 - if (thisLevel <= level) break; - } - // 標題文字「開頭相符」即算命中(容忍標題後帶括號補述) - if (!inSec && thisLevel === level && line.replace(/^#+\s+/, '').startsWith(heading.replace(/^#+\s+/, ''))) { - inSec = true; - continue; - } - } - if (inSec) out.push(line); - } - return out.join('\n'); -} - -// --- 實體行解析:`- **正規名**(別名1/別名2)— 描述`(別名、描述皆選填)--- -function parseEntities(md) { - const sec = sectionBody(md, '## 實體'); - const entities = []; - for (const raw of sec.split('\n')) { - const line = raw.trim(); - if (!line.startsWith('- ')) continue; - if (line.startsWith('- >') || line.startsWith('> ')) continue; // 跳過引言說明行 - const m = line.match(/^- \*\*(.+?)\*\*(?:((.+?)))?\s*(?:[—–\-]\s*(.*))?$/); - if (!m) continue; - const name = m[1].trim(); - if (!name) continue; - const aliases = m[2] - ? m[2].split(/[//、,]/).map((s) => s.trim()).filter((s) => s && s !== name) - : []; - const gloss = (m[3] || '').trim(); - entities.push({ name, aliases, gloss }); - } - return entities; -} - -// --- typed-edge 行解析:`A >> 謂詞 >> B`(端點可為裸實體名或 [[wikilink]])--- -function parseTypedEdges(sectionText) { - const edges = []; - for (const raw of (sectionText || '').split('\n')) { - const line = raw.trim(); - if (!line.startsWith('- ')) continue; - const body = line.slice(2).trim(); - if (body.startsWith('(') || body.startsWith('(')) continue; // 「(暫無…)」占位行 - const parts = body.split('>>'); - if (parts.length !== 3) continue; - const subject = stripWikilink(parts[0].trim()); - const predicate = parts[1].trim(); - const object = stripWikilink(parts[2].trim()); - if (!subject || !predicate || !object) continue; - edges.push({ subject, predicate, object }); - } - return edges; -} - -// [[notes/00-INDEX]] → notes/00-INDEX ;純字串則原樣回。 -function stripWikilink(s) { - const m = s.match(/^\[\[(.+?)\]\]$/); - return m ? m[1].trim() : s; -} - -// --- 抽所有 inline [[wikilink]](含 header 的 ← [[notes/00-INDEX]] 與內文)--- -function extractInlineWikilinks(md) { - const out = []; - const re = /\[\[(.+?)\]\]/g; - let m; - while ((m = re.exec(md)) !== null) out.push(m[1].trim()); - return out; -} - -// --- 卡片 canonical id:以檔名(去副檔名)為準,對齊 `## 卡片關係` 用的 [[基名]] 慣例 --- -export function cardCanonical(relPath) { - const base = relPath.split('/').pop().replace(/\.md$/, ''); - return base; -} - -/** - * 解析一張卡片 → { entry, nodes, triplets, meta }(尚未分段的原始產物)。 - * relPath:卡片相對 repo 根路徑(如 system-dev/wiki/cards/notes/Xxx.md)。 - * repo:如 'Leo/notes'。 - */ -export function parseCard(md, relPath, repo = 'Leo/notes') { - const { data: fm } = parseFrontmatter(md); - const canonical = cardCanonical(relPath); - const titleMatch = md.match(/^#\s+(.+)$/m); - const title = titleMatch ? titleMatch[1].trim() : canonical; - - // 1) 節點:## 實體 的正規名 + 別名 + gloss。 - const entities = parseEntities(md); - - // 2) 邊:內文知識關係(實體↔實體)+ 卡片關係(卡↔卡)+ inline wikilink(卡→卡 導覽/引用)。 - const intraEdges = parseTypedEdges(sectionBody(md, '### 內文知識關係')) - .map((e) => ({ ...e, confidence: 1.0 })); - const cardEdges = parseTypedEdges(sectionBody(md, '### 卡片關係')) - .map((e) => ({ ...e, confidence: 1.0 })); - - // inline wikilink(← [[notes/00-INDEX]] 等)→ 卡→卡「連結至」邊,去重、排除自環與已被 typed 邊覆蓋者。 - const typedPairs = new Set( - [...cardEdges].map((e) => `${e.subject}${e.object}`), - ); - const seenRef = new Set(); - const refEdges = []; - for (const target of extractInlineWikilinks(md)) { - const t = stripWikilink(target); - if (t === canonical || t === title) continue; // 自環 - if (typedPairs.has(`${canonical}${t}`)) continue; // 已有明確謂詞邊 - const key = `${canonical}${t}`; - if (seenRef.has(key)) continue; - seenRef.add(key); - refEdges.push({ subject: canonical, predicate: '連結至', object: t, confidence: 0.5 }); - } - - const triplets = [...intraEdges, ...cardEdges, ...refEdges]; - - // 3) 節點清單:卡片本身(canonical,帶 frontmatter gloss)+ 內文實體。 - // 卡對卡邊指到的「別張卡」不在此補 node —— 那張卡自己被 ingest 時會補自己的 node。 - const nodes = []; - const seenNode = new Set(); - const pushNode = (n) => { - const k = n.name.toLowerCase(); - if (!n.name || seenNode.has(k)) return; - seenNode.add(k); - nodes.push(n); - }; - pushNode({ name: canonical, gloss: fm.gloss || '', aliases: title && title !== canonical ? [title] : [] }); - for (const e of entities) pushNode({ name: e.name, gloss: e.gloss, aliases: e.aliases }); - - return { - entry: { - // base POST /entries(或 kbdb_upsert_block)用。metadata.embed=true → 語意可搜。 - page_name: `wikicard:${repo}/${canonical}`, // idempotency key(穩定) - entry_type: 'wiki_card', - content: md, // 卡片全文逐字(embed 對象) - tags: Array.isArray(fm.tags) ? fm.tags : [], - metadata: { - embed: true, // ★ base embed 模組讀此旗標 - source: `gitea:${repo}@${relPath}`, - content_hash: sha256(md), - kind: 'wiki_card', - repo, - canonical, - pipeline_candidate: fm.pipeline_candidate === true, - }, - }, - nodes, - triplets, - meta: { canonical, title, relPath, repo, contentHash: sha256(md) }, - }; -} - -/** - * 把一張卡片的 (nodes, triplets) 打包成「一個或多個」ingest envelope, - * 使每個 envelope 打進 graph 後的 subrequest 都 ≤ SUBREQ_BUDGET。 - * - * 分段規則(對應頂層 SDD R4 / issue #8 第4點): - * - 單 envelope 夠塞(7+4N+M ≤ budget)→ 不分段,uri = 基 uri(無 anchor)。 - * - 需分段 → 每段 uri = `<基uri>#seg{NN}`、anchor = `seg{NN}`。 - * 每段是「獨立 source_uri」→ 各自獨立冪等,繞開 graph 的 per-source content_hash 整包 skip - * (否則同 uri 第 2 段起會被 line 65 的 content_hash 命中而整包跳過)。 - * - 節點只放進「第一個引用到它的段」,跨段不重送(避免 graph persistNodes 重建 entity record)。 - */ -export function planEnvelopes(parsed, opts = {}) { - const budget = opts.budget ?? SUBREQ_BUDGET; - const repo = parsed.meta.repo; - const relPath = parsed.meta.relPath; - const baseUri = `gitea:${repo}@${relPath}`; - const contentHash = parsed.meta.contentHash; - const commit = opts.commit; - - const extractor = { - model: opts.extractorModel ?? 'mechanical/km-wiki-card-parse@1', - tier: 'deep', // 人工精耕卡=deep(決定性、非淺萃) - extracted_at: Math.floor(Date.now() / 1000), - }; - - const nodeByName = new Map(parsed.nodes.map((n) => [n.name, n])); - - // 貪婪打包:逐條 triplet 累進,段成本 = 7 + 4*(段內邊數) + (段內首見節點數)。 - const segments = []; - let cur = null; - const startSeg = () => { - cur = { triplets: [], nodeNames: new Set() }; - segments.push(cur); - }; - const segCost = (seg, extraEdges = 0, extraNodes = 0) => - estimateEnvelopeSubrequests(seg.triplets.length + extraEdges, seg.nodeNames.size + extraNodes); - - startSeg(); - for (const t of parsed.triplets) { - // 這條邊會新引入哪些節點(subject/object 命中 nodeByName 且本段尚未收) - const cand = [t.subject, t.object].filter( - (nm) => nodeByName.has(nm) && !cur.nodeNames.has(nm) && !anySegHas(segments, cur, nm), - ); - // 放得下?(含新增這條邊 + 新引入節點) - if (cur.triplets.length > 0 && segCost(cur, 1, cand.length) > budget) { - startSeg(); - } - cur.triplets.push(t); - for (const nm of [t.subject, t.object]) { - if (nodeByName.has(nm) && !anySegHas(segments, null, nm)) cur.nodeNames.add(nm); - } - } - - const multi = segments.length > 1; - const envelopes = segments.map((seg, i) => { - const anchor = multi ? `seg${String(i + 1).padStart(2, '0')}` : undefined; - const uri = multi ? `${baseUri}#${anchor}` : baseUri; - const nodes = [...seg.nodeNames].map((nm) => { - const n = nodeByName.get(nm); - const out = { name: n.name }; - if (n.gloss) out.gloss = n.gloss; - if (n.aliases && n.aliases.length) out.aliases = n.aliases; - out.embed = true; - return out; - }); - const source = { uri, content_hash: contentHash }; - if (anchor) source.anchor = anchor; - if (commit) source.commit = commit; - return { - source, - extractor, - nodes, - triplets: seg.triplets.map((t) => ({ - subject: t.subject, - predicate: t.predicate, - object: t.object, - confidence: t.confidence ?? 1.0, - })), - _estSubrequests: estimateEnvelopeSubrequests(seg.triplets.length, seg.nodeNames.size), - }; - }); - - // triplets≥1 是契約硬性;無邊的卡不產 envelope(仍會建 entry)。 - return envelopes.filter((e) => e.triplets.length >= 1); -} - -function anySegHas(segments, exclude, name) { - for (const s of segments) { - if (s === exclude) continue; - if (s.nodeNames.has(name)) return true; - } - return false; -} - -/** 一張卡片 → 完整 ingest 計畫(entry + envelopes)。planCard = parseCard + planEnvelopes。 */ -export function planCard(md, relPath, repo = 'Leo/notes', opts = {}) { - const parsed = parseCard(md, relPath, repo); - const envelopes = planEnvelopes(parsed, opts); - return { entry: parsed.entry, envelopes, meta: parsed.meta, nodeCount: parsed.nodes.length, tripletCount: parsed.triplets.length }; -} diff --git a/registry/examples/km-wiki-ingest/lib/dry-run.mjs b/registry/examples/km-wiki-ingest/lib/dry-run.mjs deleted file mode 100644 index 5679533..0000000 --- a/registry/examples/km-wiki-ingest/lib/dry-run.mjs +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/env node -// km-wiki-ingest 乾跑(dry-run)驗證器 — 不寫 live、不部署。 -// ------------------------------------------------------------- -// 註:解析在 live 由 workflow.yaml 的 parse_card = 通用 `code` 零件(sandbox inline JS)承載; -// 本驗證器直接 import card-to-envelope.mjs(=該 code 節點內聯 JS 的權威來源)跑同一份邏輯, -// 故 dry-run 的 envelope 結果與 code 節點在 live 的輸出等價(Arcrun#10 已單測證明逐欄全等)。 -// 對某 repo 的 system-dev/wiki/cards/**/*.md 跑機械解析 + envelope 打包, -// 輸出「將寫入什麼」:① entry 清單(page_name / entry_type / metadata.embed / content_hash) -// ② triplet envelope 清單(每段的 nodes / triplets / source.uri+anchor) -// ③ 每個 graph /triplets/ingest 呼叫的 subrequest 估算(證明壓在 CF 上限下) -// ④ 冪等鍵設計(entry=page_name+content_hash;triplet=source.uri+content_hash)。 -// -// 用法:node dry-run.mjs --repo-path [--repo Leo/notes] [--budget 40] [--json] [--full] -// --self-test 額外跑「合成超大卡」證明分段生效。 - -import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs'; -import path from 'node:path'; -import { execFileSync } from 'node:child_process'; -import { planCard, estimateEnvelopeSubrequests, SUBREQ_CEILING, SUBREQ_BUDGET } from './card-to-envelope.mjs'; - -function parseArgs(argv) { - const out = { repo: 'Leo/notes', budget: SUBREQ_BUDGET }; - for (let i = 0; i < argv.length; i++) { - const a = argv[i]; - if (a === '--repo-path') out.repoPath = argv[++i]; - else if (a === '--repo') out.repo = argv[++i]; - else if (a === '--budget') out.budget = Number(argv[++i]); - else if (a === '--json') out.json = true; - else if (a === '--full') out.full = true; - else if (a === '--self-test') out.selfTest = true; - } - return out; -} - -function walkCards(dir) { - const out = []; - if (!existsSync(dir)) return out; - for (const e of readdirSync(dir, { withFileTypes: true })) { - const p = path.join(dir, e.name); - if (e.isDirectory()) out.push(...walkCards(p)); - else if (e.name.endsWith('.md') && e.name !== '.gitkeep' && !e.name.startsWith('00-INDEX')) out.push(p); - } - return out; -} - -function gitCommit(repoPath) { - try { - return execFileSync('git', ['-C', repoPath, 'rev-parse', 'HEAD'], { encoding: 'utf8' }).trim(); - } catch { return undefined; } -} - -const args = parseArgs(process.argv.slice(2)); -if (!args.repoPath) { - console.error('用法: node dry-run.mjs --repo-path [--repo Leo/notes] [--budget 40] [--json] [--full] [--self-test]'); - process.exit(1); -} - -const cardsRoot = path.join(args.repoPath, 'system-dev', 'wiki', 'cards'); -const cardPaths = walkCards(cardsRoot); -const commit = gitCommit(args.repoPath); - -const report = { repo: args.repo, commit, budget: args.budget, ceiling: SUBREQ_CEILING, cards: [], totals: {} }; -let totEntries = 0, totEnvelopes = 0, totTriplets = 0, totNodes = 0, maxSub = 0, over = 0; - -for (const p of cardPaths) { - const rel = path.relative(args.repoPath, p); - const md = readFileSync(p, 'utf8'); - const plan = planCard(md, rel, args.repo, { budget: args.budget, commit }); - totEntries++; - totEnvelopes += plan.envelopes.length; - const cardTriplets = plan.envelopes.reduce((s, e) => s + e.triplets.length, 0); - const cardNodes = plan.envelopes.reduce((s, e) => s + e.nodes.length, 0); - totTriplets += cardTriplets; - totNodes += cardNodes; - for (const e of plan.envelopes) { - maxSub = Math.max(maxSub, e._estSubrequests); - if (e._estSubrequests > SUBREQ_CEILING) over++; - } - report.cards.push({ - relPath: rel, - canonical: plan.meta.canonical, - entry: { - page_name: plan.entry.page_name, - entry_type: plan.entry.entry_type, - 'metadata.embed': plan.entry.metadata.embed, - content_hash: plan.entry.metadata.content_hash.slice(0, 12) + '…', - content_bytes: Buffer.byteLength(plan.entry.content, 'utf8'), - tags: plan.entry.tags, - }, - envelopeCount: plan.envelopes.length, - envelopes: plan.envelopes.map((e) => ({ - 'source.uri': e.source.uri, - 'source.anchor': e.source.anchor ?? null, - nodes: e.nodes.length, - triplets: e.triplets.length, - est_subrequests: e._estSubrequests, - under_ceiling: e._estSubrequests <= SUBREQ_CEILING, - sample_triplets: e.triplets.slice(0, args.full ? 999 : 4).map((t) => `${t.subject} >> ${t.predicate} >> ${t.object} (${t.confidence})`), - sample_nodes: e.nodes.slice(0, args.full ? 999 : 4).map((n) => `${n.name}${n.gloss ? ' — ' + n.gloss.slice(0, 30) + '…' : ''}`), - })), - }); -} - -report.totals = { - cards: totEntries, - entries_to_upsert: totEntries, - triplet_envelopes: totEnvelopes, - total_triplets: totTriplets, - total_nodes: totNodes, - max_est_subrequests_single_call: maxSub, - ceiling: SUBREQ_CEILING, - budget: args.budget, - any_envelope_over_ceiling: over, -}; - -// --- self-test:合成一張「超大卡」(20 邊 + 22 節點)證明單 envelope 會爆、分段後每段壓在預算下 --- -if (args.selfTest) { - const entities = []; - const edges = []; - for (let i = 0; i < 22; i++) entities.push(`- **實體${i}**(別名${i})— 這是實體 ${i} 的一句描述。`); - for (let i = 0; i < 20; i++) edges.push(`- 實體${i} >> 關聯到 >> 實體${i + 1}`); - const bigCard = `---\ntags: [壓測]\ngloss: 合成超大卡,測分段。\n---\n# 合成超大卡\n\n← [[notes/00-INDEX]]\n\n## 實體\n${entities.join('\n')}\n\n## 關聯\n### 內文知識關係\n${edges.join('\n')}\n`; - const plan = planCard(bigCard, 'system-dev/wiki/cards/notes/合成超大卡.md', args.repo, { budget: args.budget }); - const single = estimateEnvelopeSubrequests(plan.tripletCount, plan.nodeCount); - report.self_test = { - note: '合成 20 邊 / 22 節點 的超大卡', - if_single_envelope_est_subrequests: single, - would_crash_single: single > SUBREQ_CEILING, - segmented_into: plan.envelopes.length, - per_segment: plan.envelopes.map((e) => ({ - uri: e.source.uri, anchor: e.source.anchor, triplets: e.triplets.length, nodes: e.nodes.length, est_subrequests: e._estSubrequests, under_ceiling: e._estSubrequests <= SUBREQ_CEILING, - })), - all_segments_under_ceiling: plan.envelopes.every((e) => e._estSubrequests <= SUBREQ_CEILING), - }; -} - -if (args.json) { - console.log(JSON.stringify(report, null, 2)); - process.exit(0); -} - -// --- 人類可讀輸出 --- -const L = (s = '') => console.log(s); -L(`\n================ km-wiki-ingest DRY-RUN(不寫 live)================`); -L(`repo=${report.repo} commit=${(commit || '(none)').slice(0, 12)} budget=${args.budget} CF_ceiling=${SUBREQ_CEILING}`); -L(`卡片來源根:${path.relative(args.repoPath, cardsRoot)} 找到 ${cardPaths.length} 張卡\n`); - -for (const c of report.cards) { - L(`── 卡片:${c.relPath}`); - L(` ENTRY(base POST /entries 或 kbdb_upsert_block,冪等鍵 page_name):`); - L(` page_name = ${c.entry.page_name}`); - L(` entry_type = ${c.entry.entry_type}`); - L(` metadata.embed= ${c.entry['metadata.embed']} content_hash=${c.entry.content_hash} bytes=${c.entry.content_bytes}`); - L(` tags = ${JSON.stringify(c.entry.tags)}`); - L(` TRIPLET ENVELOPE(s)(POST graph /triplets/ingest;分段數=${c.envelopeCount}):`); - for (const e of c.envelopes) { - L(` • uri=${e['source.uri']}${e['source.anchor'] ? ' anchor=' + e['source.anchor'] : ''}`); - L(` nodes=${e.nodes} triplets=${e.triplets} est_subrequests=${e.est_subrequests} ≤ceiling? ${e.under_ceiling ? 'YES' : 'NO ⚠️'}`); - for (const t of e.sample_triplets) L(` - ${t}`); - if (e.sample_nodes.length) L(` nodes: ${e.sample_nodes.join(' | ')}`); - } - L(''); -} - -L(`================ 彙總 ================`); -for (const [k, v] of Object.entries(report.totals)) L(` ${k.padEnd(34)} = ${v}`); -L(` 冪等設計:`); -L(` entry → page_name(穩定鍵)+ metadata.content_hash(比對是否改動 → 未改 skip、改動 PATCH 重嵌)`); -L(` triplet → source.uri + source.content_hash(graph 現役 per-source 冪等;同 hash 整包 no-op)`); -L(` 分段 → 各段獨立 source.uri(#segNN)→ 各自獨立冪等,繞開 per-source content_hash 整包 skip`); - -if (report.self_test) { - L(`\n================ SELF-TEST:超大檔分段 ================`); - const st = report.self_test; - L(` ${st.note}`); - L(` 若不分段(單 envelope)估算 subrequest = ${st.if_single_envelope_est_subrequests} → 會炸? ${st.would_crash_single ? 'YES(> ' + SUBREQ_CEILING + ')' : 'no'}`); - L(` 分段後段數 = ${st.segmented_into},每段:`); - for (const s of st.per_segment) L(` - ${s.anchor}: triplets=${s.triplets} nodes=${s.nodes} est=${s.est_subrequests} ≤ceiling? ${s.under_ceiling ? 'YES' : 'NO ⚠️'}`); - L(` 全部段壓在上限下? ${st.all_segments_under_ceiling ? 'YES ✅' : 'NO ⚠️'}`); -} -L(''); diff --git a/registry/examples/km-wiki-ingest/tags.json b/registry/examples/km-wiki-ingest/tags.json deleted file mode 100644 index ec4f038..0000000 --- a/registry/examples/km-wiki-ingest/tags.json +++ /dev/null @@ -1 +0,0 @@ -["ingest", "kbdb", "wiki", "mechanical", "cron", "webhook", "graph", "triplet", "no-llm"] diff --git a/registry/examples/km-wiki-ingest/workflow.yaml b/registry/examples/km-wiki-ingest/workflow.yaml deleted file mode 100644 index 0f1632d..0000000 --- a/registry/examples/km-wiki-ingest/workflow.yaml +++ /dev/null @@ -1,458 +0,0 @@ -name: km_wiki_ingest_drain -description: > - Phase 0 限速 drain:cron 每 tick 只處理「一張卡」→ 機械解析成 entry + triplet envelope - → 冪等寫 KBDB(base entry / graph triplet)。反覆跑直到全庫 drain 完。 - 來源=repo 的 system-dev/wiki/cards/**/*.md(人工精耕卡,非裸筆記,無 LLM)。 - 解析由通用 code 零件(sandbox inline JS)承載,不再鑄 domain 零件(Arcrun#10 裁定)。 - 穩態(Gitea push webhook 只處理 delta)見檔尾 §穩態變體。 - -# ── 為什麼「一 tick 一卡」=根治 07_01 的 Too many subrequests ── -# graph worker 處理一次 POST /triplets/ingest 的 subrequest = 7 + 4*N_triplets + M_nodes + D_deprecated。 -# 07_01 炸點:單一 envelope 吞整檔 N=11,M=10 → 61 > 50(CF bundled 上限)→ 半殘。 -# 對策:① 一卡一 tick(天然小批,notes 卡 ~N4/M5 → est 28~33,穩壓 50 下) -# ② code 節點的內聯解析會自動把超大卡以 source_uri anchor 分段(每段獨立冪等)。 -# ⟹ 任何單一 graph 呼叫都不會再破頂。 - -flow: - - "watch_cron >> ON_SUCCESS >> pick_next_card" - - "pick_next_card >> ON_SUCCESS >> fetch_card" - - "fetch_card >> ON_SUCCESS >> parse_card" - - "parse_card >> ON_SUCCESS >> upsert_entry" # 卡片 → base entry(embed=true),冪等 - - "upsert_entry >> ON_SUCCESS >> post_envelopes" # wikilink/typed-edge → graph triplet - - "post_envelopes >> 對每個 envelope >> post_one_envelope" # 分段時多段,各段獨立冪等 - -config: - # 1) 排程 tick:慢推。每 2 分鐘一張卡=限速(Phase 0 唯一需要 rate-limit 之處)。 - watch_cron: - component: cron - cron_expr: "*/2 * * * *" - description: "每 2 分鐘 drain 一張卡(限速慢推,避免 CF 額度與 subrequest 壓力)" - - # 2) 取下一張待處理卡(cursor drain)。用 Gitea contents API 列 cards 目錄 + 一個游標 block - # 記「處理到哪」。回傳單一 { rel_path, download_url, content_hash?(git blob sha) }。 - # 註:list + cursor 的細節可用 http_request(Gitea API) + set/string_ops 組;此處給語意佔位。 - pick_next_card: - component: http_request - method: GET - url: "https://git.uncle6.me/api/v1/repos/{{repo}}/contents/system-dev/wiki/cards?ref={{ref}}" - headers: - Authorization: "token {{gitea_token}}" - Accept: "application/json" - # 下游用 filter/set 取「游標之後第一張、且 .md、且非 00-INDEX」的一張。 - - # 3) 抓卡片全文(Gitea raw)。 - fetch_card: - component: http_request - method: GET - url: "{{pick_next_card.next.download_url}}" - headers: - Authorization: "token {{gitea_token}}" - - # 4) ★ 機械解析 —— 通用 code 零件(sandbox inline JS,無 LLM、無 fs/網路,stdin→stdout JSON)。 - # Arcrun#10 裁定:一次性解析邏輯走通用逃生口,不再鑄 domain 零件 km_wiki_card_parse。 - # 下面 code: 內聯的即 lib/card-to-envelope.mjs 的 planCard 邏輯(去 import/export、 - # raw NUL 分隔符改 \u0000 escape、改用 code 沙箱注入的 curated builtin sha256; - # 已單測證明與原模組輸出逐欄全等)。 - # input:卡片全文 md + 相對路徑 relPath + repo + opts.budget(subrequest 目標上限)。 - # output:{ success:true, data:{ entry, envelopes[], meta, nodeCount, tripletCount } } - # —— envelope 已分段、已估 subrequest。故下游改引用 parse_card.data.*。 - parse_card: - component: code - code: | - // km-wiki-ingest — 機械式卡片→(entry + triplet envelope) 轉換核心(無 LLM、純函式) - // --------------------------------------------------------------------------- - // 取代舊 `kbdb-ingest-plugin/scripts/ingest-cli.mjs` 的 raw→Haiku 路: - // 舊路 = 讀裸筆記 → 呼叫 Haiku 萃 (s,p,o) → envelope(有 LLM、非決定性、耗 token)。 - // 新路 = 讀「已精耕卡片」(`system-dev/wiki/cards/**/*.md`)→ 直接解析卡片內既有的 - // `## 實體`(節點)、`## 關聯` 的 typed-edge(`A >> 關係 >> B`)與 `[[wikilink]]` - // → entry + triplet envelope。純機械、決定性、零 token。 - // - // 這支=通用 `code` 零件(Arcrun#10,sandbox inline JS)承載的解析邏輯本體。 - // workflow.yaml 的 parse_card 節點把本檔的 planCard 邏輯內聯進 code 零件的 config - // (去 import/export、raw NUL 分隔符改 u0000 escape、改用 code 沙箱注入的 sha256); - // 不再鑄 domain 零件 km_wiki_card_parse(Arcrun#10 裁定:一次性解析走通用逃生口)。 - // 本檔續留作「該內聯 JS 的權威來源 + 可單元測試的參考實作」(純函式、stdin→stdout JSON、無 fs/網路)。 - // - // 對齊契約:kbdb-ingest-plugin/contracts/ingest-candidate.json(envelope 形狀 / 禁止欄位)。 - // 對齊頂層 SDD:卡片→entry(metadata.embed=true,走 base API)、wikilink→triplet(走 graph)。 - // - // 鐵律:不碰儲存、不算向量、不建表。這支只「產出將寫入什麼」,實際 HTTP 由 workflow 打。 - - // (import 移除:code 沙箱提供注入的 sha256 builtin) - // --- CF subrequest 預算(防「Too many subrequests by single Worker invocation」,07_01 根因)--- - // - // graph worker 處理一次 POST /triplets/ingest 時,對 base 的每次 fetch = 1 subrequest。 - // 精確拆帳(讀 kbdb-graph-plugin/src/actions/triplet-ingest.ts + triplet-crud.ts + templates.ts): - // ingestEnvelope = ensurePluginTemplates(3) + listRecordsByTemplate(1) - // + Σ triplet [ createTriplet → ensurePluginTemplates(3) + createRecord(1) = 4 ] - // + persistNodes [ ensurePluginTemplates(3) + Σ node createRecord(1) ] - // + Σ deprecated updateRecord(1) - // ⟹ subreq(envelope) = 7 + 4*N_triplets + M_nodes + D_deprecated - // - // 07_01 實測炸點:N=11, M=10, D=0 → 7+44+10 = 61 > 50(CF 免費/bundled 上限)→ 炸半殘。 - // - // 對策 = 「一卡一 tick、每 envelope 壓在預算下、超大檔以 source_uri anchor 分段」。 - const SUBREQ_CEILING = 50; // CF 單次 Worker invocation subrequest 硬上限(bundled) - const SUBREQ_BUDGET = 40; // 我們的目標上限(留 10 給 D_deprecated 等變動) - - /** 精確估算「一個 envelope 打進 graph /triplets/ingest」會在 graph worker 內產生幾個 subrequest。 */ - function estimateEnvelopeSubrequests(nTriplets, mNodes, dDeprecated = 0) { - return 7 + 4 * nTriplets + mNodes + dDeprecated; - } - - // --- sha256(content_hash 冪等鍵)--- - // (sha256 移除:使用 code 沙箱注入的 curated builtin sha256) - - // --- frontmatter 解析(極簡 YAML:只吃我們卡片用到的 tags / gloss / pipeline_candidate)--- - function parseFrontmatter(md) { - const m = md.match(/^---\n([\s\S]*?)\n---\n?/); - if (!m) return { data: {}, body: md }; - const body = md.slice(m[0].length); - const data = {}; - for (const line of m[1].split('\n')) { - const kv = line.match(/^([A-Za-z_][\w-]*):\s*(.*)$/); - if (!kv) continue; - const key = kv[1]; - let val = kv[2].trim(); - if (val.startsWith('[') && val.endsWith(']')) { - // inline list: [a, b, c] - data[key] = val.slice(1, -1).split(',').map((s) => s.trim()).filter(Boolean); - } else if (val === 'true' || val === 'false') { - data[key] = val === 'true'; - } else { - data[key] = val; - } - } - return { data, body }; - } - - // --- 取某個 `## 標題` / `### 標題` 區塊的內文(到下一個同級或更高級標題為止)--- - function sectionBody(md, heading) { - // heading 例:'## 實體'、'### 內文知識關係' - const level = heading.match(/^#+/)[0].length; - const lines = md.split('\n'); - const out = []; - let inSec = false; - for (const line of lines) { - const h = line.match(/^(#+)\s+(.*)$/); - if (h) { - const thisLevel = h[1].length; - if (inSec) { - // 遇到同級或更高級標題 → 區塊結束 - if (thisLevel <= level) break; - } - // 標題文字「開頭相符」即算命中(容忍標題後帶括號補述) - if (!inSec && thisLevel === level && line.replace(/^#+\s+/, '').startsWith(heading.replace(/^#+\s+/, ''))) { - inSec = true; - continue; - } - } - if (inSec) out.push(line); - } - return out.join('\n'); - } - - // --- 實體行解析:`- **正規名**(別名1/別名2)— 描述`(別名、描述皆選填)--- - function parseEntities(md) { - const sec = sectionBody(md, '## 實體'); - const entities = []; - for (const raw of sec.split('\n')) { - const line = raw.trim(); - if (!line.startsWith('- ')) continue; - if (line.startsWith('- >') || line.startsWith('> ')) continue; // 跳過引言說明行 - const m = line.match(/^- \*\*(.+?)\*\*(?:((.+?)))?\s*(?:[—–\-]\s*(.*))?$/); - if (!m) continue; - const name = m[1].trim(); - if (!name) continue; - const aliases = m[2] - ? m[2].split(/[//、,]/).map((s) => s.trim()).filter((s) => s && s !== name) - : []; - const gloss = (m[3] || '').trim(); - entities.push({ name, aliases, gloss }); - } - return entities; - } - - // --- typed-edge 行解析:`A >> 謂詞 >> B`(端點可為裸實體名或 [[wikilink]])--- - function parseTypedEdges(sectionText) { - const edges = []; - for (const raw of (sectionText || '').split('\n')) { - const line = raw.trim(); - if (!line.startsWith('- ')) continue; - const body = line.slice(2).trim(); - if (body.startsWith('(') || body.startsWith('(')) continue; // 「(暫無…)」占位行 - const parts = body.split('>>'); - if (parts.length !== 3) continue; - const subject = stripWikilink(parts[0].trim()); - const predicate = parts[1].trim(); - const object = stripWikilink(parts[2].trim()); - if (!subject || !predicate || !object) continue; - edges.push({ subject, predicate, object }); - } - return edges; - } - - // [[notes/00-INDEX]] → notes/00-INDEX ;純字串則原樣回。 - function stripWikilink(s) { - const m = s.match(/^\[\[(.+?)\]\]$/); - return m ? m[1].trim() : s; - } - - // --- 抽所有 inline [[wikilink]](含 header 的 ← [[notes/00-INDEX]] 與內文)--- - function extractInlineWikilinks(md) { - const out = []; - const re = /\[\[(.+?)\]\]/g; - let m; - while ((m = re.exec(md)) !== null) out.push(m[1].trim()); - return out; - } - - // --- 卡片 canonical id:以檔名(去副檔名)為準,對齊 `## 卡片關係` 用的 [[基名]] 慣例 --- - function cardCanonical(relPath) { - const base = relPath.split('/').pop().replace(/\.md$/, ''); - return base; - } - - /** - * 解析一張卡片 → { entry, nodes, triplets, meta }(尚未分段的原始產物)。 - * relPath:卡片相對 repo 根路徑(如 system-dev/wiki/cards/notes/Xxx.md)。 - * repo:如 'Leo/notes'。 - */ - function parseCard(md, relPath, repo = 'Leo/notes') { - const { data: fm } = parseFrontmatter(md); - const canonical = cardCanonical(relPath); - const titleMatch = md.match(/^#\s+(.+)$/m); - const title = titleMatch ? titleMatch[1].trim() : canonical; - - // 1) 節點:## 實體 的正規名 + 別名 + gloss。 - const entities = parseEntities(md); - - // 2) 邊:內文知識關係(實體↔實體)+ 卡片關係(卡↔卡)+ inline wikilink(卡→卡 導覽/引用)。 - const intraEdges = parseTypedEdges(sectionBody(md, '### 內文知識關係')) - .map((e) => ({ ...e, confidence: 1.0 })); - const cardEdges = parseTypedEdges(sectionBody(md, '### 卡片關係')) - .map((e) => ({ ...e, confidence: 1.0 })); - - // inline wikilink(← [[notes/00-INDEX]] 等)→ 卡→卡「連結至」邊,去重、排除自環與已被 typed 邊覆蓋者。 - const typedPairs = new Set( - [...cardEdges].map((e) => `${e.subject}\u0000${e.object}`), - ); - const seenRef = new Set(); - const refEdges = []; - for (const target of extractInlineWikilinks(md)) { - const t = stripWikilink(target); - if (t === canonical || t === title) continue; // 自環 - if (typedPairs.has(`${canonical}\u0000${t}`)) continue; // 已有明確謂詞邊 - const key = `${canonical}\u0000${t}`; - if (seenRef.has(key)) continue; - seenRef.add(key); - refEdges.push({ subject: canonical, predicate: '連結至', object: t, confidence: 0.5 }); - } - - const triplets = [...intraEdges, ...cardEdges, ...refEdges]; - - // 3) 節點清單:卡片本身(canonical,帶 frontmatter gloss)+ 內文實體。 - // 卡對卡邊指到的「別張卡」不在此補 node —— 那張卡自己被 ingest 時會補自己的 node。 - const nodes = []; - const seenNode = new Set(); - const pushNode = (n) => { - const k = n.name.toLowerCase(); - if (!n.name || seenNode.has(k)) return; - seenNode.add(k); - nodes.push(n); - }; - pushNode({ name: canonical, gloss: fm.gloss || '', aliases: title && title !== canonical ? [title] : [] }); - for (const e of entities) pushNode({ name: e.name, gloss: e.gloss, aliases: e.aliases }); - - return { - entry: { - // base POST /entries(或 kbdb_upsert_block)用。metadata.embed=true → 語意可搜。 - page_name: `wikicard:${repo}/${canonical}`, // idempotency key(穩定) - entry_type: 'wiki_card', - content: md, // 卡片全文逐字(embed 對象) - tags: Array.isArray(fm.tags) ? fm.tags : [], - metadata: { - embed: true, // ★ base embed 模組讀此旗標 - source: `gitea:${repo}@${relPath}`, - content_hash: sha256(md), - kind: 'wiki_card', - repo, - canonical, - pipeline_candidate: fm.pipeline_candidate === true, - }, - }, - nodes, - triplets, - meta: { canonical, title, relPath, repo, contentHash: sha256(md) }, - }; - } - - /** - * 把一張卡片的 (nodes, triplets) 打包成「一個或多個」ingest envelope, - * 使每個 envelope 打進 graph 後的 subrequest 都 ≤ SUBREQ_BUDGET。 - * - * 分段規則(對應頂層 SDD R4 / issue #8 第4點): - * - 單 envelope 夠塞(7+4N+M ≤ budget)→ 不分段,uri = 基 uri(無 anchor)。 - * - 需分段 → 每段 uri = `<基uri>#seg{NN}`、anchor = `seg{NN}`。 - * 每段是「獨立 source_uri」→ 各自獨立冪等,繞開 graph 的 per-source content_hash 整包 skip - * (否則同 uri 第 2 段起會被 line 65 的 content_hash 命中而整包跳過)。 - * - 節點只放進「第一個引用到它的段」,跨段不重送(避免 graph persistNodes 重建 entity record)。 - */ - function planEnvelopes(parsed, opts = {}) { - const budget = opts.budget ?? SUBREQ_BUDGET; - const repo = parsed.meta.repo; - const relPath = parsed.meta.relPath; - const baseUri = `gitea:${repo}@${relPath}`; - const contentHash = parsed.meta.contentHash; - const commit = opts.commit; - - const extractor = { - model: opts.extractorModel ?? 'mechanical/km-wiki-card-parse@1', - tier: 'deep', // 人工精耕卡=deep(決定性、非淺萃) - extracted_at: Math.floor(Date.now() / 1000), - }; - - const nodeByName = new Map(parsed.nodes.map((n) => [n.name, n])); - - // 貪婪打包:逐條 triplet 累進,段成本 = 7 + 4*(段內邊數) + (段內首見節點數)。 - const segments = []; - let cur = null; - const startSeg = () => { - cur = { triplets: [], nodeNames: new Set() }; - segments.push(cur); - }; - const segCost = (seg, extraEdges = 0, extraNodes = 0) => - estimateEnvelopeSubrequests(seg.triplets.length + extraEdges, seg.nodeNames.size + extraNodes); - - startSeg(); - for (const t of parsed.triplets) { - // 這條邊會新引入哪些節點(subject/object 命中 nodeByName 且本段尚未收) - const cand = [t.subject, t.object].filter( - (nm) => nodeByName.has(nm) && !cur.nodeNames.has(nm) && !anySegHas(segments, cur, nm), - ); - // 放得下?(含新增這條邊 + 新引入節點) - if (cur.triplets.length > 0 && segCost(cur, 1, cand.length) > budget) { - startSeg(); - } - cur.triplets.push(t); - for (const nm of [t.subject, t.object]) { - if (nodeByName.has(nm) && !anySegHas(segments, null, nm)) cur.nodeNames.add(nm); - } - } - - const multi = segments.length > 1; - const envelopes = segments.map((seg, i) => { - const anchor = multi ? `seg${String(i + 1).padStart(2, '0')}` : undefined; - const uri = multi ? `${baseUri}#${anchor}` : baseUri; - const nodes = [...seg.nodeNames].map((nm) => { - const n = nodeByName.get(nm); - const out = { name: n.name }; - if (n.gloss) out.gloss = n.gloss; - if (n.aliases && n.aliases.length) out.aliases = n.aliases; - out.embed = true; - return out; - }); - const source = { uri, content_hash: contentHash }; - if (anchor) source.anchor = anchor; - if (commit) source.commit = commit; - return { - source, - extractor, - nodes, - triplets: seg.triplets.map((t) => ({ - subject: t.subject, - predicate: t.predicate, - object: t.object, - confidence: t.confidence ?? 1.0, - })), - _estSubrequests: estimateEnvelopeSubrequests(seg.triplets.length, seg.nodeNames.size), - }; - }); - - // triplets≥1 是契約硬性;無邊的卡不產 envelope(仍會建 entry)。 - return envelopes.filter((e) => e.triplets.length >= 1); - } - - function anySegHas(segments, exclude, name) { - for (const s of segments) { - if (s === exclude) continue; - if (s.nodeNames.has(name)) return true; - } - return false; - } - - /** 一張卡片 → 完整 ingest 計畫(entry + envelopes)。planCard = parseCard + planEnvelopes。 */ - function planCard(md, relPath, repo = 'Leo/notes', opts = {}) { - const parsed = parseCard(md, relPath, repo); - const envelopes = planEnvelopes(parsed, opts); - return { entry: parsed.entry, envelopes, meta: parsed.meta, nodeCount: parsed.nodes.length, tripletCount: parsed.triplets.length }; - } - - // graph /triplets/ingest 是 strict schema,拒絕未知鍵。planEnvelopes 於 envelope 上掛的 - // 診斷鍵 _estSubrequests 不在 ingest-candidate 契約內 → post 前剝除所有 _-前綴鍵, - // 讓 post_one_envelope 的 body_json={{envelope}} 為契約乾淨 payload。 - // (leo21c 實測:不剝除會回 422 unrecognized_keys "_estSubrequests"。) - const __plan = planCard(input.md, input.relPath, input.repo, input.opts || {}); - __plan.envelopes = __plan.envelopes.map(function (e) { - const clean = {}; - for (const k in e) { if (k.charAt(0) !== '_') clean[k] = e[k]; } - return clean; - }); - return __plan; - - input: - md: "{{fetch_card.data.body}}" - relPath: "{{pick_next_card.next.rel_path}}" - repo: "{{repo}}" - opts: - budget: 40 # subrequest 目標上限(留 10 給 D_deprecated),超過自動 anchor 分段 - limits: - timeout_ms: 3000 # 純 CPU 解析;大卡也充裕 - max_output_bytes: 4194304 # envelope 陣列可能較大(4 MiB) - - # 5) 卡片 → base entry,冪等 upsert(page_name 當鍵;找到 PATCH、沒有 POST)。 - # metadata.embed=true → base embed 模組會補嵌 → 語意可搜。 - upsert_entry: - component: kbdb_upsert_block - api_key: "{{kbdb_api_key}}" - kbdb_url: "{{kbdb_url}}" - page_name: "{{parse_card.data.entry.page_name}}" - type: "{{parse_card.data.entry.entry_type}}" - content: "{{parse_card.data.entry.content}}" - source: "{{parse_card.data.entry.metadata.source}}" - tags_json: "{{parse_card.data.entry.tags_json}}" - # ⚠️ metadata.embed=true / content_hash 需經 base /entries 帶 metadata_json 落地; - # 若 kbdb_upsert_block 尚未透傳 metadata_json,改用 http_request 直打 base POST/PATCH /entries - # 帶 body_json.metadata_json(見 description.md §entry 冪等)。 - - # 6) triplet envelope(可能多段)→ 逐段 POST graph /triplets/ingest。 - # graph 端 per-source(uri+content_hash) 冪等:同 hash 整包 no-op;分段各段 uri 不同 → 各自獨立冪等。 - post_envelopes: - component: foreach_control - items: "{{parse_card.data.envelopes}}" - item_key: envelope - - post_one_envelope: - component: http_request - method: POST - url: "{{graph_url}}/triplets/ingest" - headers: - Content-Type: "application/json" - X-Arcrun-API-Key: "{{graph_api_key}}" - body_json: "{{envelope}}" # envelope 已符合 ingest-candidate.json 契約(禁止欄位已排除) - -# ── 執行環境變數(部署時注入;此檔不放密鑰)── -# repo=Leo/notes ref=main gitea_token= -# kbdb_url=https://arcrun-kbdb.leo21c.workers.dev kbdb_api_key= -# graph_url= graph_api_key=leo -# -# ═══════════════════════════════════════════════════════════════════════════ -# §穩態變體(km_wiki_ingest_delta):Gitea push webhook → 只處理 delta 檔 -# ═══════════════════════════════════════════════════════════════════════════ -# 觸發 = Gitea repo Settings → Webhooks → 指向 arcrun(cypher-executor) 的 workflow webhook URL。 -# ⚠️ 這是 Gitea → Cloudflare(arcrun),非 GitHub Actions → 不觸 GitHub flag 紅線(D4/D20)。 -# 只把上面 flow 的 watch_cron/pick_next_card 換成: -# input(webhook payload)>> ON_SUCCESS >> collect_changed -# collect_changed = 從 payload.commits[].{added,modified} 濾出 system-dev/wiki/cards/**/*.md -# >> foreach 檔 >> fetch_card >> parse_card >> upsert_entry >> post_envelopes(同上) -# 量小、天生不撞頂、不需限速;靠 graph/entry 冪等自動 skip 未變檔。