atelier: map the platform, record what stays untouched, add sharp

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.
This commit is contained in:
2026-08-20 03:53:30 +00:00
parent 597597f50e
commit 5854ccd4d8
3 changed files with 180 additions and 21 deletions
+157
View File
@@ -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 `<img src>` 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.
+22 -21
View File
@@ -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=="],
+1
View File
@@ -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:*",