diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5b3fe946..f0ce912c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,7 +22,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0) cli: dependencies: @@ -244,7 +244,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0) packages/plugins/create-paperclip-plugin: dependencies: @@ -294,7 +294,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0) packages/plugins/examples/plugin-file-browser-example: dependencies: @@ -519,6 +519,9 @@ importers: pino-pretty: specifier: ^13.1.3 version: 13.1.3 + sharp: + specifier: ^0.34.5 + version: 0.34.5 ws: specifier: ^8.19.0 version: 8.19.0 @@ -541,6 +544,9 @@ importers: '@types/node': specifier: ^24.6.0 version: 24.12.0 + '@types/sharp': + specifier: ^0.32.0 + version: 0.32.0 '@types/supertest': specifier: ^6.0.2 version: 6.0.3 @@ -564,7 +570,7 @@ importers: version: 6.4.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0) ui: dependencies: @@ -679,7 +685,7 @@ importers: version: 6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0) packages: @@ -1219,6 +1225,9 @@ packages: cpu: [x64] os: [win32] + '@emnapi/runtime@1.9.1': + resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} + '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} @@ -1710,6 +1719,143 @@ packages: '@iconify/utils@3.1.0': resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==} + '@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] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + + '@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] + + '@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] + + '@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] + + '@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] + + '@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] + + '@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] + + '@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] + + '@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] + + '@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/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -3289,6 +3435,10 @@ packages: '@types/serve-static@2.2.0': resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} + '@types/sharp@0.32.0': + resolution: {integrity: sha512-OOi3kL+FZDnPhVzsfD37J88FNeZh6gQsGcLc95NbeURRGvmSjeXiDcyWzF2o3yh/gQAUn2uhh/e+CPCa5nwAxw==} + deprecated: This is a stub types definition. sharp provides its own type definitions, so you do not need this installed. + '@types/superagent@8.1.9': resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} @@ -5261,6 +5411,11 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + send@1.2.1: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} @@ -5275,6 +5430,10 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -6853,6 +7012,11 @@ snapshots: '@embedded-postgres/windows-x64@18.1.0-beta.16': optional: true + '@emnapi/runtime@1.9.1': + dependencies: + tslib: 2.8.1 + optional: true + '@epic-web/invariant@1.0.0': {} '@esbuild-kit/core-utils@3.3.2': @@ -7124,6 +7288,102 @@ snapshots: '@iconify/types': 2.0.0 mlly: 1.8.1 + '@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.9.1 + 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/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -9018,6 +9278,10 @@ snapshots: '@types/http-errors': 2.0.5 '@types/node': 25.2.3 + '@types/sharp@0.32.0': + dependencies: + sharp: 0.34.5 + '@types/superagent@8.1.9': dependencies: '@types/cookiejar': 2.1.5 @@ -11388,6 +11652,8 @@ snapshots: semver@6.3.1: {} + semver@7.7.4: {} + send@1.2.1: dependencies: debug: 4.4.3 @@ -11417,6 +11683,37 @@ snapshots: setprototypeof@1.2.0: {} + 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 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -11866,8 +12163,52 @@ snapshots: - terser - tsx - yaml + optional: true - vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + vite-node: 3.2.4(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 24.12.0 + jsdom: 28.1.0(@noble/hashes@2.0.1) + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 diff --git a/server/package.json b/server/package.json index 57865b2f..e8ead156 100644 --- a/server/package.json +++ b/server/package.json @@ -51,7 +51,6 @@ "@paperclipai/adapter-openclaw-gateway": "workspace:*", "@paperclipai/adapter-opencode-local": "workspace:*", "@paperclipai/adapter-pi-local": "workspace:*", - "hermes-paperclip-adapter": "0.1.1", "@paperclipai/adapter-utils": "workspace:*", "@paperclipai/db": "workspace:*", "@paperclipai/plugin-sdk": "workspace:*", @@ -66,12 +65,14 @@ "drizzle-orm": "^0.38.4", "embedded-postgres": "^18.1.0-beta.16", "express": "^5.1.0", + "hermes-paperclip-adapter": "0.1.1", "jsdom": "^28.1.0", "multer": "^2.0.2", "open": "^11.0.0", "pino": "^9.6.0", "pino-http": "^10.4.0", "pino-pretty": "^13.1.3", + "sharp": "^0.34.5", "ws": "^8.19.0", "zod": "^3.24.2" }, @@ -81,6 +82,7 @@ "@types/jsdom": "^28.0.0", "@types/multer": "^2.0.0", "@types/node": "^24.6.0", + "@types/sharp": "^0.32.0", "@types/supertest": "^6.0.2", "@types/ws": "^8.18.1", "cross-env": "^10.1.0", diff --git a/server/src/routes/agents.ts b/server/src/routes/agents.ts index 9481840b..45051bf5 100644 --- a/server/src/routes/agents.ts +++ b/server/src/routes/agents.ts @@ -47,6 +47,7 @@ import { assertBoard, assertCompanyAccess, getActorInfo } from "./authz.js"; import { findServerAdapter, listAdapterModels } from "../adapters/index.js"; import { redactEventPayload } from "../redaction.js"; import { redactCurrentUserValue } from "../log-redaction.js"; +import { renderOrgChartSvg, renderOrgChartPng, type OrgNode } from "./org-chart-svg.js"; import { runClaudeLogin } from "@paperclipai/adapter-claude-local/server"; import { DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX, @@ -821,6 +822,28 @@ export function agentRoutes(db: Db) { res.json(leanTree); }); + router.get("/companies/:companyId/org.svg", async (req, res) => { + const companyId = req.params.companyId as string; + assertCompanyAccess(req, companyId); + const tree = await svc.orgForCompany(companyId); + const leanTree = tree.map((node) => toLeanOrgNode(node as Record)); + const svg = renderOrgChartSvg(leanTree as unknown as OrgNode[]); + res.setHeader("Content-Type", "image/svg+xml"); + res.setHeader("Cache-Control", "no-cache"); + res.send(svg); + }); + + router.get("/companies/:companyId/org.png", async (req, res) => { + const companyId = req.params.companyId as string; + assertCompanyAccess(req, companyId); + const tree = await svc.orgForCompany(companyId); + const leanTree = tree.map((node) => toLeanOrgNode(node as Record)); + const png = await renderOrgChartPng(leanTree as unknown as OrgNode[]); + res.setHeader("Content-Type", "image/png"); + res.setHeader("Cache-Control", "no-cache"); + res.send(png); + }); + router.get("/companies/:companyId/agent-configurations", async (req, res) => { const companyId = req.params.companyId as string; await assertCanReadConfigurations(req, companyId); diff --git a/server/src/services/company-export-readme.ts b/server/src/services/company-export-readme.ts index 56725024..aa46d91a 100644 --- a/server/src/services/company-export-readme.ts +++ b/server/src/services/company-export-readme.ts @@ -87,10 +87,9 @@ export function generateReadme( lines.push(""); } - // Org chart as Mermaid diagram - const mermaid = generateOrgChartMermaid(manifest.agents); - if (mermaid) { - lines.push(mermaid); + // Org chart image (generated during export as images/org-chart.png) + if (manifest.agents.length > 0) { + lines.push("![Org Chart](images/org-chart.png)"); lines.push(""); } diff --git a/server/src/services/company-portability.ts b/server/src/services/company-portability.ts index 5a91efeb..3772fb25 100644 --- a/server/src/services/company-portability.ts +++ b/server/src/services/company-portability.ts @@ -42,11 +42,57 @@ import { agentService } from "./agents.js"; import { agentInstructionsService } from "./agent-instructions.js"; import { assetService } from "./assets.js"; import { generateReadme } from "./company-export-readme.js"; +import { renderOrgChartPng, type OrgNode } from "../routes/org-chart-svg.js"; import { companySkillService } from "./company-skills.js"; import { companyService } from "./companies.js"; import { issueService } from "./issues.js"; import { projectService } from "./projects.js"; +/** Build OrgNode tree from manifest agent list (slug + reportsToSlug). */ +function buildOrgTreeFromManifest(agents: CompanyPortabilityManifest["agents"]): OrgNode[] { + const ROLE_LABELS: Record = { + ceo: "Chief Executive", cto: "Technology", cmo: "Marketing", + cfo: "Finance", coo: "Operations", vp: "VP", manager: "Manager", + engineer: "Engineer", agent: "Agent", + }; + const bySlug = new Map(agents.map((a) => [a.slug, a])); + const childrenOf = new Map(); + for (const a of agents) { + const parent = a.reportsToSlug ?? null; + const list = childrenOf.get(parent) ?? []; + list.push(a); + childrenOf.set(parent, list); + } + const build = (parentSlug: string | null): OrgNode[] => { + const members = childrenOf.get(parentSlug) ?? []; + return members.map((m) => ({ + id: m.slug, + name: m.name, + role: ROLE_LABELS[m.role] ?? m.role, + status: "active", + reports: build(m.slug), + })); + }; + // Find roots: agents whose reportsToSlug is null or points to a non-existent slug + const roots = agents.filter((a) => !a.reportsToSlug || !bySlug.has(a.reportsToSlug)); + const rootSlugs = new Set(roots.map((r) => r.slug)); + // Start from null parent, but also include orphans + const tree = build(null); + for (const root of roots) { + if (root.reportsToSlug && !bySlug.has(root.reportsToSlug)) { + // Orphan root (parent slug doesn't exist) + tree.push({ + id: root.slug, + name: root.name, + role: ROLE_LABELS[root.role] ?? root.role, + status: "active", + reports: build(root.slug), + }); + } + } + return tree; +} + const DEFAULT_INCLUDE: CompanyPortabilityInclude = { company: true, agents: true, @@ -2422,6 +2468,17 @@ export function companyPortabilityService(db: Db, storage?: StorageService) { resolved.manifest.envInputs = dedupeEnvInputs(envInputs); resolved.warnings.unshift(...warnings); + // Generate org chart PNG from manifest agents + if (resolved.manifest.agents.length > 0) { + try { + const orgNodes = buildOrgTreeFromManifest(resolved.manifest.agents); + const pngBuffer = await renderOrgChartPng(orgNodes); + finalFiles["images/org-chart.png"] = bufferToPortableBinaryFile(pngBuffer, "image/png"); + } catch { + // Non-fatal: export still works without the org chart image + } + } + if (!input.selectedFiles || input.selectedFiles.some((entry) => normalizePortablePath(entry) === "README.md")) { finalFiles["README.md"] = generateReadme(resolved.manifest, { companyName: company.name, diff --git a/skills/paperclip/references/company-skills.md b/skills/paperclip/references/company-skills.md index 852424cc..a0ddd064 100644 --- a/skills/paperclip/references/company-skills.md +++ b/skills/paperclip/references/company-skills.md @@ -34,7 +34,42 @@ The canonical model is: ## Install A Skill Into The Company -Import from GitHub, a local path, or a `skills.sh`-style source string: +Import using a **skills.sh URL**, a key-style source string, a GitHub URL, or a local path. + +### Source types (in order of preference) + +| Source format | Example | When to use | +|---|---|---| +| **skills.sh URL** | `https://skills.sh/google-labs-code/stitch-skills/design-md` | When a user gives you a `skills.sh` link. This is the managed skill registry — **always prefer it when available**. | +| **Key-style string** | `google-labs-code/stitch-skills/design-md` | Shorthand for the same skill — `org/repo/skill-name` format. Equivalent to the skills.sh URL. | +| **GitHub URL** | `https://github.com/vercel-labs/agent-browser` | When the skill is in a GitHub repo but not on skills.sh. | +| **Local path** | `/abs/path/to/skill-dir` | When the skill is on disk (dev/testing only). | + +**Critical:** If a user gives you a `https://skills.sh/...` URL, use that URL or its key-style equivalent (`org/repo/skill-name`) as the `source`. Do **not** convert it to a GitHub URL — skills.sh is the managed registry and the source of truth for versioning, discovery, and updates. + +### Example: skills.sh import (preferred) + +```sh +curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/import" \ + -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "source": "https://skills.sh/google-labs-code/stitch-skills/design-md" + }' +``` + +Or equivalently using the key-style string: + +```sh +curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/import" \ + -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "source": "google-labs-code/stitch-skills/design-md" + }' +``` + +### Example: GitHub import ```sh curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/import" \ @@ -45,11 +80,6 @@ curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/ }' ``` -You can also use a source string such as: - -- `npx skills add https://github.com/vercel-labs/agent-browser --skill agent-browser` -- `vercel-labs/agent-browser/agent-browser` - If the task is to discover skills from the company project workspaces first: ```sh