From 5854ccd4d80f30845a29f7f62c9fd88ee7fefd29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Thu, 20 Aug 2026 03:53:30 +0000 Subject: [PATCH] atelier: map the platform, record what stays untouched, add sharp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit no platform code references atelier and atelier references no platform code, so this extraction has no reverse-dependency seam to design around. also records the database-separate decision (collections and provider_settings both have rows on the live server) and adds sharp as a direct dependency for the image-inspection code the sidecar needs — promoted from an existing transitive resolution, no new package versions pulled in. --- BLOCKERS.md | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++ bun.lock | 43 +++++++------- package.json | 1 + 3 files changed, 180 insertions(+), 21 deletions(-) create mode 100644 BLOCKERS.md diff --git a/BLOCKERS.md b/BLOCKERS.md new file mode 100644 index 00000000..7ac9fe2e --- /dev/null +++ b/BLOCKERS.md @@ -0,0 +1,157 @@ +# Atelier extraction — blockers and decisions made without asking + +Unattended run, started 2026-08-20. Per the kickoff prompt: decisions made here without being able to +ask, in the order found. Nobody is awake; each entry says what I did and why, then the run continues. + +## Map, before anything moved + +Grepped the whole platform for `atelier` (case-insensitive, every `.ts`/`.tsx`/`.md`/`.json`/`.cjs` +file). Two hits, both incidental: + +- `src/workspaces/officerdev/src/apps/Chat/components/chat-images.test.ts:10` — a test fixture using + `https://atelier.officer.dev/images/....png` as an example markdown image URL. Not a dependency. +- `src/servers/api/chat/task-restart.ts:181` — a comment mentioning a stray `bun dev` process that once + ran from `~/dockers/officer-atelier` and got respawned by accident. Historical, not a dependency. + +**No platform code imports anything from Atelier, and Atelier imports nothing from the platform.** +Unlike music/offscale, there is no reverse dependency to find and no "this stays behind" piece. It comes +out whole, the way offscale did before the imports were found — except here there really are none. + +Also checked: `ecosystem.config.cjs` (platform's own) has no `atelier` entry — the standalone service is +managed by its OWN `ecosystem.config.cjs` inside `~/officer-dev/dockers/officer-atelier/`, entirely +outside this repository. + +## The standalone service stays running, untouched + +`officer-atelier` (PM2, port 3002) is a **live production service** — its own `ecosystem.config.cjs` says +plainly "this is the instance the mobile app talks to, so it holds still." It serves +`https://atelier.officer.dev` via nginx and holds real data (87 generations, 2 collections, 5 providers, +1 provider_settings row, checked directly against Postgres). + +I am not stopping it, not touching its nginx config, not touching its PM2 entry, and not touching the +mobile app. This extraction adds a new, additive plugin inside the platform; cutting the mobile app over +to it (or deciding whether to) is the owner's call, not mine to make unattended. Recorded here so nobody +reads the plugin's existence as "the old service is now redundant, kill it" — it is not redundant until +someone repoints the mobile app. + +## Decision 3 — database rows checked, both non-empty → database stays separate + +Queried the live `atelier` Postgres database directly (via a throwaway `postgres` client, same +credentials as `.env.production.local`): + +``` +collections 2 +collection_items 73 +provider_settings 1 +providers 5 +generations 87 +``` + +`collections` (2) and `provider_settings` (1) are the two tables the brief named — both non-empty. Per +decision 3: **the `atelier` database stays separate**, not folded into `officer_db`. No +`plugins/atelier/db/schema.ts` (that convention is for `officer_db`-prefixed tables); the sidecar keeps +its own Postgres connection to the `atelier` database, exactly as today, just running from inside the +plugin's `sidecar/` directory instead of a standalone repo. + +**The brief undersold the schema.** It says "tables: collections, provider_settings" — the real schema +has five: `generations` (the actual data — 87 rows, prompts/images/costs), `collection_items` (the join +table), `providers` (configured adapter+model+key rows — 5 of them; a newer, richer replacement for +`provider_settings` that the brief doesn't mention at all), plus the two named. `generations` alone +being non-empty would have settled decision 3 on its own. Following the letter of decision 3 (check the +two named tables) rather than re-deriving it from the fuller schema, since both answers point the same +way here. + +**Migration to `officer_db` is a real follow-up**, not done here: fold `collections`, `collection_items`, +`providers`, `provider_settings`, `generations` into `officer_db` as `atelier_*`-prefixed tables once +someone is awake to watch a real data migration. Until then the plugin's sidecar owns a second Postgres +connection, to a second database, which is new territory for this plugin system (every other plugin's +`db/` is officer_db-prefixed tables) — documented in PLUGIN.md. + +## Postgres connection for the separate database + +The sidecar needs its own connection string. Added `ATELIER_POSTGRES_URL` as a new platform `.env` +variable (same pattern as `OFFICERDEV_POSTGRES_URL` for officer's own database) rather than routing it +through the secret store — a database URL carrying a password is handled as a plain `.env` value +everywhere else in this codebase (`OFFICERDEV_POSTGRES_URL` itself), and the secret store's own docs +scope it to "every encryption and signing key the platform uses" plus foreign bearer credentials +(APNs, FCM, webhooks) — not connection strings. Kept consistent with the existing convention rather than +inventing a new one. + +**Not set automatically.** I don't have a safe way to guess whether the owner wants the plugin sidecar +to point at the *existing* `atelier` database (sharing state with the still-running standalone service — +two writers to one queue table, which `generations.status` and the `claimableGenerations`/`markRunning` +claim logic were written assuming is single-process) or a fresh one. Left unset, with a clear startup +error, and documented in PLUGIN.md. **Do not just paste the standalone service's `POSTGRES_URL` in and +restart both** — two processes racing the same `generations` queue is exactly the hazard `markRunning`'s +atomic claim exists to prevent for restarts of *one* process, not for two live ones. + +## Decision 2 — Providers as an Officer settings screen, against a direct precedent + +Found `IntegrationsSettings/index.tsx`'s own comment, written when email was extracted on 2026-08-18: + +> "Email account management left with the feature (`plugins/email/`). A plugin contributes panels, not +> settings sections, so it now lives inside the email workspace itself." + +That is the opposite of decision 2, written five days before this run, in the exact settings screen +decision 2 points Atelier's Providers into. Per the brief's own instructions ("if you find evidence one +is wrong, write it down, follow the decision anyway"): implemented Providers as a genuine platform +settings section — `IntegrationsSettings` gained an "Atelier" entry under the Enterprise tab — as new +**platform** code (`src/apps/officer-web/...`) that talks to `/api/atelier/providers` over HTTP via +`useClient`, same as any other authenticated screen. It does not import anything from +`plugins/atelier/`, so it does not violate "a plugin importing platform code is fine; the reverse is +not" — there is no import in either direction, only an HTTP call, which is what every frontend/backend +pair in this codebase already is. + +Recording the contradiction here rather than silently picking the email precedent, since decision 2 was +explicit and pre-made. + +## Images served through an authenticated route, not the old `PUBLIC_URL` scheme + +The standalone service served `/images/*` unauthenticated, off Bun's top-level route table, with +`PUBLIC_URL` baked into every response so the URL was durable outside the process. Per "What you may NOT +do", that is gone: images are served from `GET /api/atelier/images/:filename`, mounted like every other +route, behind the permission gate. + +The frontend needs `` to carry a credential a browser cannot put in an `Authorization` header. +Used the platform's existing `?token=` escape hatch — already read by both +`permission-gate.ts:37` and `user-middleware.ts:22`, already used by `FileViewer/file-types.ts` for +exactly this (`getRawUrl`, `getAudioTracksUrl`, ...) for the file browser's raw/audio/subtitle routes. +Not a new mechanism, not an unauthenticated route: the same query-token path every other authenticated +media URL in this app already takes. + +## `sharp` added as a direct dependency + +Atelier's `inspect.ts` uses `sharp` to measure width/height/alpha-channel/opacity of a written image — +load-bearing (it is how a `background: transparent` request that silently produced an opaque image gets +caught). `sharp@0.33.5` was already resolved in `bun.lock` as an optional transitive dependency of +`next`, so promoting it to a direct dependency of the platform added no new resolution — verified the +lockfile diff is exactly that promotion, nothing else moved. Done via the documented dance: +`frozenLockfile` commented out in `bunfig.toml` for one `bun install`, restored immediately after, +diff reviewed before committing both files. + +## Publishing to the Marketplace — not done, left as the one open step + +`plugins/atelier/` is its own local git repository (`git init`, per "Done means"), but I did not create +`gitea.officer.dev/plugins/atelier` or push to it. I found no Gitea API token or credential available to +this session, and creating a new repository on a shared Gitea instance and registering it in the +marketplace catalogue (`scripts/marketplace-sync.ts`, `BUNDLED` in `src/servers/plugins/marketplace.ts`) +is exactly the kind of action with effects beyond this local checkout that the run's own ground rules +say to flag rather than take unilaterally, especially with no verified way to confirm it succeeded. + +Left for the owner: + +``` +cd plugins/atelier +git remote add origin git@gitea.officer.dev:plugins/atelier.git # after creating the repo on Gitea +git push -u origin main +cd ../.. +bun --env-file=.env scripts/marketplace-sync.ts +``` + +Then add a `BUNDLED` entry in `src/servers/plugins/marketplace.ts` (offline fallback), matching the +other three plugins' entries. + +Until that happens, the plugin still installs fine from a checkout that already has the directory +(`plugins/atelier/` committed to its own local repo, present on disk) — it just cannot be discovered by +the Marketplace screen on a machine that has never had it, per "Done means" #2. This is the one piece of +"done" I could not complete unattended. diff --git a/bun.lock b/bun.lock index f0e590eb..26d3e592 100644 --- a/bun.lock +++ b/bun.lock @@ -122,6 +122,7 @@ "rehype-sanitize": "^6.0.0", "rehype-slug": "^6.0.0", "remark-gfm": "^4.0.1", + "sharp": "^0.33.5", "shiki": "^3.22.0", "sonner": "^2.0.7", "sounds": "workspace:*", @@ -445,21 +446,21 @@ "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], - "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], - "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], - "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], - "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], - "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], - "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], - "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], @@ -2417,6 +2418,20 @@ "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], + "@img/sharp-darwin-arm64/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + + "@img/sharp-darwin-x64/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + + "@img/sharp-linux-arm/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + + "@img/sharp-linux-arm64/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + + "@img/sharp-linux-x64/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + + "@img/sharp-linuxmusl-arm64/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + + "@img/sharp-linuxmusl-x64/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], @@ -2599,20 +2614,6 @@ "sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], - "sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], - - "sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], - - "sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], - - "sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], - - "sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], - - "sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], - - "sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], - "sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], "sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], diff --git a/package.json b/package.json index 031bedc2..b4971e65 100644 --- a/package.json +++ b/package.json @@ -152,6 +152,7 @@ "rehype-sanitize": "^6.0.0", "rehype-slug": "^6.0.0", "remark-gfm": "^4.0.1", + "sharp": "^0.33.5", "shiki": "^3.22.0", "sonner": "^2.0.7", "sounds": "workspace:*",