scenri 0.20.2 → 0.20.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.20.3](https://github.com/tonygorb/Scenri/compare/v0.20.2...v0.20.3) (2026-09-27)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **cli:** a skipped install script says how to allow it and clear npx's copy ([7afece2](https://github.com/tonygorb/Scenri/commit/7afece286e58e61ead28cac5186559781a368096))
9
+ * **cli:** the desktop offer names Settings > Local access ([1ea3992](https://github.com/tonygorb/Scenri/commit/1ea399235c21eb87127605ba834afed607be946e))
10
+ * **content:** the court sneakers tile, redrawn from words that agree with themselves ([1686d0d](https://github.com/tonygorb/Scenri/commit/1686d0df176b8745d0c4e76cfd6b91bc279d5eaf))
11
+ * public pages, films and first-run text describe Scenri as it is ([563f35e](https://github.com/tonygorb/Scenri/commit/563f35eab31f725df25057e6022cb1cf01eecf8f))
12
+
3
13
  ## [0.20.2](https://github.com/tonygorb/Scenri/compare/v0.20.1...v0.20.2) (2026-09-27)
4
14
 
5
15
 
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  <br><br>
8
8
 
9
9
  **The open studio for brand-consistent AI visuals.**<br>
10
- Define a client's brand once. Then generate, branch, and art-direct on-brand images through a version tree, running entirely on your own machine and your own AI accounts.
10
+ Define a client's brand once. Then put its products and people into scenes, art-direct each shot in plain words, and refine it step by step. Open source, running entirely on your own machine and your own AI accounts.
11
11
 
12
12
  [![CI](https://github.com/tonygorb/scenri/actions/workflows/ci.yml/badge.svg)](https://github.com/tonygorb/scenri/actions/workflows/ci.yml)
13
13
  [![npm](https://img.shields.io/npm/v/scenri)](https://www.npmjs.com/package/scenri)
@@ -21,9 +21,9 @@ npx scenri
21
21
 
22
22
  <sub>Needs [Node.js](https://nodejs.org) 22 or newer. New to any of this? The [install guide](https://github.com/tonygorb/scenri/blob/main/docs/INSTALL.md) walks through every step.</sub>
23
23
 
24
- <img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/demo.gif" alt="The Scenri composer: a dollar sign opens a product picker and Ridgeline Trail is chosen, an at sign picks the presenter Paloma, a slash picks the Chalk Steps scene, a line of written direction is typed after the three chips, the shot renders as a card in the wall, and it opens to show the picture beside the brief that made it" width="820">
24
+ <img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/demo.gif" alt="Scenri's Create page: in the composer a dollar sign picks the product Cropped Puffer, an at sign picks the presenter Kwame, a slash picks the Container Blue scene, and a line of written direction follows the three chips. Generate turns a new card into a dithered swirl where the picture is being made, the swirl develops into a man in an orange puffer jacket leaning on a cobalt shipping container, and the shot opens beside the prompt that made it" width="820">
25
25
 
26
- <sub>Pick a product, pick a presenter, pick a scene, then write the direction. That is the brief.</sub>
26
+ <sub>Pick a product, pick a presenter, pick a scene, then write the direction. That is the prompt.</sub>
27
27
 
28
28
  </div>
29
29
 
@@ -33,27 +33,27 @@ npx scenri
33
33
 
34
34
  Most AI image tools give you a prompt box and a slot machine. Scenri gives you the part that actually takes the time: **art direction**.
35
35
 
36
- Every shot keeps its lineage. Branch an edit from any shot, put the two side by side, and the drift heatmap shows you exactly where the model changed your product when it should not have.
36
+ Every shot keeps its history. Open it, say what to change, and each refinement joins a trail under the picture, so the original and every step stay one click apart. Refine any step again, or use **Reuse setup** to start a new shot from the same prompt and chips.
37
37
 
38
38
  It runs as a local server on your own computer. Your brands, your images, and your keys stay on your disk. To use it from a phone or tablet on the same Wi-Fi, open Settings, Local access, and scan the QR code.
39
39
 
40
- ## What a brief produces
40
+ ## What a prompt produces
41
41
 
42
- The film above ends on the shot its brief made: the Ridgeline Trail product, the presenter Paloma, the Chalk Steps scene, and a paragraph of written direction. The chips carry the identity; the prose does the art direction. Here are eight more shots from the home wall, each built exactly that way:
42
+ The film above ends on one of the examples from the Home wall: the Cropped Puffer product, the presenter Kwame, the Container Blue scene, and a short paragraph of written direction, of which the film types the first line. The chips carry the identity; the words do the art direction. Here are eight more shots from the Home wall, each built the same way from chips and written direction:
43
43
 
44
44
  <!-- One paragraph, no whitespace between the tags: a newline here renders as
45
45
  a fixed word-space, and 4 x 24.5% plus three spaces overflows a phone screen
46
- and wraps 3+1. With zero gaps the rows stay four wide at every width. --><p align="center"><img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/readme-paper-garden.jpg" title="Collagen in a paper garden" alt="A lilac Tanner Fielding collagen pouch standing among giant cut-paper flowers in coral, yellow and violet" width="24.5%"><img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/readme-foil-jump.jpg" title="Mid-air in the foil tent" alt="A woman in an orange puffer jacket jumping inside a tent of crinkled silver foil" width="24.5%"><img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/readme-ruby-pour.jpg" title="Aperitivo in a ruby pour" alt="A Fenner Ross aperitivo bottle on a pale ledge while a thick ruby syrup pours and drips down beside it" width="24.5%"><img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/readme-pink-smoke.jpg" title="Gold hoops in pink smoke" alt="A woman in profile with a low bun and a chunky gold hoop earring, wrapped in clouds of pink smoke" width="24.5%"><img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/readme-travertine-chair.jpg" title="Oak lounge chair in travertine light" alt="An oak lounge chair with cream boucle cushions beside a travertine block, window light striping the wall" width="24.5%"><img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/readme-balloon-knot.jpg" title="Headphones in the balloon knot" alt="A man with his eyes closed in pale blue over-ear headphones, sunk in a tangle of pink, orange and blue balloons" width="24.5%"><img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/readme-ember-runner.jpg" title="Trail runner on the embers" alt="A trail running shoe resting on a charred log with embers glowing beneath it" width="24.5%"><img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/readme-arch-lean.jpg" title="Leaning into the green arch" alt="A woman in cobalt trousers leaning inside a lime green arch on a sky blue set, holding a violet can" width="24.5%"></p>
46
+ and wraps 3+1. With zero gaps the rows stay four wide at every width. --><p align="center"><img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/readme-paper-garden.jpg" title="Collagen in a paper garden" alt="A lilac Tanner Fielding collagen pouch standing among giant cut-paper flowers in coral, yellow and violet" width="24.5%"><img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/readme-foil-jump.jpg" title="Mid-air in the foil tent" alt="A woman in an orange puffer jacket jumping inside a tent of crinkled silver foil" width="24.5%"><img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/readme-ruby-pour.jpg" title="Aperitivo under a ruby pour" alt="A Fenner Ross aperitivo bottle on a pale pink ledge while a thick ruby syrup pours into a glass of ice beside it and drips over the edge" width="24.5%"><img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/readme-pink-smoke.jpg" title="Gold hoops in pink smoke" alt="A woman in profile with a low bun and a chunky gold hoop earring, wrapped in clouds of pink smoke" width="24.5%"><img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/readme-travertine-chair.jpg" title="Oak lounge chair in travertine light" alt="An oak lounge chair with cream boucle cushions beside a travertine block, window light striping the wall" width="24.5%"><img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/readme-balloon-knot.jpg" title="Headphones in the balloon knot" alt="A man with his eyes closed in pale blue over-ear headphones, sunk in a tangle of pink, orange and blue balloons" width="24.5%"><img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/readme-boulder-runner.jpg" title="Trail shoe on lichened granite" alt="A white and orange trail running shoe resting on lichen-covered granite high above a mountain valley" width="24.5%"><img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/readme-arch-lean.jpg" title="Leaning into the green arch" alt="A woman in cobalt trousers leaning inside a lime green arch on a sky blue set, holding a violet can" width="24.5%"></p>
47
47
 
48
- All 100 examples on the home wall work the same way. Open one and it loads back into the composer as the brief that made it, ready to change.
48
+ All 110 examples on the Home wall work the same way. Open one and it loads back into the composer as the prompt that made it, ready to change.
49
49
 
50
50
  ## What you get out of the box
51
51
 
52
- <img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/library.gif" alt="A slow scroll down the Scenri home wall: rows of finished example shots across product categories, with the composer docked at the bottom of the screen" width="820">
52
+ <img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/library.gif" alt="Scenri's Home for a new brand: the four ways to start and the example tabs over the wall of finished shots, a slow scroll down the wall, then the Presenters library and the Scenes library, each opening with its offer to make your own above the catalog" width="820">
53
53
 
54
- A library of scenes, presenters and demo products, so the app is useful before you have uploaded anything of your own. Filter by category, bookmark what fits the brand, or build your own from a few reference photos.
54
+ A library of 42 scenes, 19 presenters and 30 demo products, so the app is useful before you have uploaded anything of your own. Filter by category and star what fits the brand. Add your own products from their photos, and make your own presenters and scenes in a short conversation: describe a person or a place, or start from photos, and approve what Scenri draws before it is saved.
55
55
 
56
- The brand kit sits one gesture away in Settings and is the part that keeps output consistent. Name, mark, palette and rules, set once and carried into every shot that asks for them.
56
+ The brand kit lives in Settings and is the part that keeps output consistent. Name, mark, palette and rules, set once and carried into every shot that asks for them.
57
57
 
58
58
  ## Run it
59
59
 
@@ -67,11 +67,11 @@ That is the whole install. npm asks once whether to proceed, downloads the curre
67
67
 
68
68
  On Windows, if PowerShell answers `npx.ps1 cannot be loaded because running scripts is disabled on this system`, that is a stock Windows setting, not a fault: type `npx.cmd scenri` instead, or use Command Prompt. Still no administrator required.
69
69
 
70
- On macOS and Windows, Scenri then asks once whether to put a **Scenri icon on your desktop**. Say yes and from then on a double-click starts Scenri and opens it in your browser, no terminal needed; the icon works offline and keeps working across updates. Said no? Settings, then About, then **Add to desktop**, or `npx scenri desktop` in a terminal. Scenri stays what it is, a local server and your browser: the icon is a launcher, not an app.
70
+ On macOS and Windows, Scenri then asks once whether to put a **Scenri icon on your desktop**. Say yes and from then on a double-click starts Scenri and opens it in your browser, no terminal needed; the icon works offline and keeps working across updates. Said no? Settings, then Local access, then **Add to desktop**, or `npx scenri desktop` in a terminal. Scenri stays what it is, a local server and your browser: the icon is a launcher, not an app.
71
71
 
72
- Generation runs on **Codex CLI**, an official helper from OpenAI that draws on your own ChatGPT plan. No API key to paste, and Scenri never charges you. Each image draws on your plan's Codex usage. You do not have to set it up by hand: if it is missing, Scenri offers to install it and to sign you in, both from the app. No ChatGPT plan? Add your own key from an image provider in Settings instead, see [Engines](#engines).
72
+ Generation runs on **Codex CLI**, an official helper from OpenAI that draws on your own paid ChatGPT plan (Codex image generation is not part of the Free plan). No API key to paste, and Scenri never charges you. Each image spends some of your plan's Codex usage. You do not have to set it up by hand: if it is missing, Scenri offers to install it and to sign you in, both from the app. No ChatGPT plan? Add your own key from an image provider in Settings instead, see [Engines](#engines).
73
73
 
74
- Two dependencies (`better-sqlite3` and `sharp`) ship native binaries, so on recent npm you may be asked to approve their install scripts once. Something not starting? See [troubleshooting](https://github.com/tonygorb/scenri/blob/main/docs/INSTALL.md#troubleshooting).
74
+ One dependency, `better-sqlite3`, runs an install script that fetches its native binary. The npm that ships with current Node releases (npm 11) runs it without asking. npm 12 blocks dependency install scripts by default, so if `npm --version` prints 12, allow this one once with `npm config set allow-scripts=better-sqlite3 --location=user` before you run `npx scenri`. Something not starting? See [troubleshooting](https://github.com/tonygorb/scenri/blob/main/docs/INSTALL.md#troubleshooting).
75
75
 
76
76
  <details>
77
77
  <summary>Run from source</summary>
@@ -88,35 +88,37 @@ pnpm dev # starts the server on 127.0.0.1:4747
88
88
 
89
89
  ## First five minutes
90
90
 
91
- 1. **Paste a website URL.** Scenri reads the public page and drafts the kit: name, palette, logo, tone.
92
- 2. **Describe a shot.** In the composer, `$` reaches for a product, `@` for a presenter, `/` for a scene, and `#` for a colour. Everything between them is your own words. The composer compiles a brief you can inspect before it runs.
93
- 3. **Generate on Codex CLI.** Runs on your own ChatGPT plan, so there is no key and no per-image charge from us.
94
- 4. **Branch an edit** from any shot, then hit compare. The heatmap shows what moved.
95
- 5. **Add your own key** in Settings to run on OpenRouter, Replicate or fal instead.
91
+ Scenri offers to walk you through your first shot, one step at a time, and Learn holds the rest. The short version:
92
+
93
+ 1. **Paste a website URL.** Scenri reads the public pages and drafts the kit: name, logo, palette. If the site sells things, it offers to add the products too.
94
+ 2. **Describe a shot.** In the composer, `$` reaches for a product, `@` for a presenter, `/` for a scene, and `#` for a colour. Everything between them is your own words.
95
+ 3. **Generate on Codex CLI.** Runs on your own paid ChatGPT plan, so there is no key and no per-image charge from us.
96
+ 4. **Refine it.** Open the shot and say what to change. Each refinement joins the trail under the picture.
97
+ 5. **Add your own key** in Settings, Providers to run on OpenRouter, Replicate or fal instead.
96
98
 
97
99
  ## Why it is built this way
98
100
 
99
- - **Iteration is the product.** A version tree, not a prompt box. Branch, compare, keep the winners.
101
+ - **Iteration is the product.** Not a prompt box: every shot keeps its trail of refinements, and the ones you star collect in Keepers.
100
102
  - **Your brands are files, not hostages.** `.brand` is an open, documented format under a permissive license. Email one to a client. Any tool can adopt it.
101
- - **Your AI, your cost.** Bring your own Codex CLI session or an API key. Experiments cost raw API price, or nothing at all on a local session. No credits that burn on a miss.
102
- - **Local first, and it means it.** No account, no telemetry, no upload. The server binds to your machine only. Scenri makes exactly two requests on its own behalf: a version-number check against npm every six hours so updates can announce themselves (and, when one is found, the download of that release from npm, staged locally until you choose to restart), and a one-time download of the library imagery archive, cached locally forever after. Nothing about you or your work is ever sent, and both turn off: in Settings or `SCENRI_NO_UPDATE_CHECK=1` for the first, `SCENRI_NO_CONTENT_FETCH=1` for the second ([how updates work](https://github.com/tonygorb/scenri/blob/main/docs/updates.md)).
103
+ - **Your AI, your cost.** Bring your own ChatGPT plan through Codex CLI, or an API key. You pay the provider directly, at its own price. Scenri sells no credits and adds no markup.
104
+ - **Local first, and it means it.** No account, no telemetry, nothing uploaded to us. Scenri runs on this computer, and answers other devices on your own network only when they bring its six-digit code (Settings, Local access). It makes exactly two requests on its own behalf: a version-number check against npm every six hours so updates can announce themselves (and, when one is found, the download of that release from npm, staged locally until you choose to restart), and the download of the library imagery archive from GitHub, once for each library version a release pins. Nothing about you or your work is sent in either, and both turn off: in Settings or `SCENRI_NO_UPDATE_CHECK=1` for the first, `SCENRI_NO_CONTENT_FETCH=1` for the second ([how updates work](https://github.com/tonygorb/scenri/blob/main/docs/updates.md), [what goes where](https://github.com/tonygorb/scenri/blob/main/PRIVACY.md)).
103
105
 
104
106
  ## Engines
105
107
 
106
108
  | Engine | What it costs you | Needs | Carries a Product or Presenter |
107
109
  |---|---|---|---|
108
- | **Codex CLI** | your ChatGPT plan | a ChatGPT account; the app installs and signs in for you | yes, up to 5 references |
109
- | OpenRouter | about $0.04 a generation | API key | yes, up to 4 references |
110
- | Replicate | about $0.003 a generation | API token | no |
111
- | fal | about $0.003 a generation | API key | no |
110
+ | **Codex CLI** | your ChatGPT plan's Codex usage | a paid ChatGPT plan; the app installs Codex and signs you in | yes, up to 5 references |
111
+ | OpenRouter | about $0.04 an image | API key | yes, up to 4 references |
112
+ | Replicate | about $0.003 an image | API token | no |
113
+ | fal | about $0.006 an image (fal bills per megapixel) | API key | no |
112
114
 
113
115
  Codex CLI is the default because it needs no key and because it carries the most reference images: a shot that has to keep both a product and a person accurate needs the room.
114
116
 
115
- It is not free. Every ChatGPT plan comes with some Codex usage and each image spends a little of it. OpenAI meters that, not Scenri, so spend caps do not apply to this engine.
117
+ It is not free. Paid ChatGPT plans include Codex usage, and each image spends some of it: OpenAI says image generation uses it three to five times faster than a plain turn. OpenAI meters that, not Scenri, so spend caps do not apply to this engine.
116
118
 
117
- **Without a ChatGPT plan**, use your own provider key. OpenRouter is the one to pick if your shots name a Product or a Presenter. Replicate and fal take no reference images, so Scenri refuses those briefs on them rather than generating something that only looks right.
119
+ **Without a paid ChatGPT plan**, use your own provider key. OpenRouter is the one to pick if your shots name a Product or a Presenter. Replicate and fal take no reference images, so Scenri refuses those shots on them rather than generating something that only looks right. Edits on Replicate and fal cost more than a new image.
118
120
 
119
- Keys are stored in your local library folder, sent only to that provider, and never returned by the API. Set a monthly spend cap per engine in Settings.
121
+ Keys are stored in your local library folder, sent only to that provider, and never returned by the API. Set a monthly spend cap per engine in Settings, Providers.
120
122
 
121
123
  Your Codex session is yours: Scenri runs the official `codex` commands on your machine and never reads, copies or stores the credential. That is also why Scenri never pools user plans: a plan is licensed to the person who pays for it, not to a service reselling it to other people. Any hosted version of Scenri, if one ever exists, would run API-priced engines only.
122
124
 
@@ -136,7 +138,7 @@ Your Codex session is yours: Scenri runs the official `codex` commands on your m
136
138
 
137
139
  `OPENROUTER_API_KEY`, `REPLICATE_API_TOKEN` and `FAL_KEY` are read from the environment as an alternative to entering them in Settings.
138
140
 
139
- **Phones and tablets.** Scenri also answers on this computer's Wi-Fi or Ethernet address, so a phone on the same network can open it: Settings, Local access shows a QR code. Scenri has no accounts, so every device other than this computer has to bring a six-digit code, which rides inside the QR code and the link; typing the bare address asks for it, wrong guesses are slowed to a crawl, and **New code** signs every device out. The first time, macOS or Windows may ask whether Node may accept incoming connections: allow it, or phones cannot reach Scenri. Setting `SCENRI_HOST=127.0.0.1` keeps Scenri to this computer alone. Over plain http the code travels unencrypted, like everything else, so treat phone access as convenience on a network you trust.
141
+ **Phones and tablets.** Scenri also answers on this computer's Wi-Fi or Ethernet address, so a phone on the same network can open it: Settings, Local access shows a QR code. Scenri has no accounts, so every device other than this computer has to bring a six-digit code, which rides inside the QR code and the link; typing the bare address asks for it, ten wrong guesses from one device lock it out for ten minutes, and **New code** signs every device out. The first time, macOS or Windows may ask whether Node may accept incoming connections: allow it, or phones cannot reach Scenri. Setting `SCENRI_HOST=127.0.0.1` keeps Scenri to this computer alone. Over plain http the code travels unencrypted, like everything else, so treat phone access as convenience on a network you trust.
140
142
 
141
143
  ## Layout
142
144
 
@@ -146,7 +148,7 @@ Your Codex session is yours: Scenri runs the official `codex` commands on your m
146
148
  | `packages/brand-spec` | the `.brand` schema, validator, and URL auto-builder |
147
149
  | `packages/core` | brands, shot history, the version tree, image store, cost ledger (SQLite) |
148
150
  | `packages/catalog` | product catalog import: Shopify, WooCommerce, Webflow, generic |
149
- | `packages/engines/*` | engine adapters: `codex`, `openrouter`, `replicate`, `fal`, `demo` |
151
+ | `packages/engines/*` | engine adapters: `codex`, `openrouter`, `replicate`, `fal`, and `demo`, a placeholder used by the tests |
150
152
  | `apps/studio` | the React studio the CLI serves |
151
153
 
152
154
  One package publishes to npm: **`scenri`**, the CLI, which bundles everything else. The rest are internal.
@@ -155,7 +157,7 @@ One package publishes to npm: **`scenri`**, the CLI, which bundles everything el
155
157
 
156
158
  Start with [CONTRIBUTING.md](https://github.com/tonygorb/scenri/blob/main/CONTRIBUTING.md). Engine adapters are the friendliest surface: one file, one interface, well covered by tests.
157
159
 
158
- Accessibility is tracked in [docs/A11Y-BACKLOG.md](https://github.com/tonygorb/scenri/blob/main/docs/A11Y-BACKLOG.md). Every Biome a11y rule runs at `error`, so the backlog is currently empty and a new defect fails CI rather than joining a list.
160
+ Accessibility is tracked in [docs/A11Y-BACKLOG.md](https://github.com/tonygorb/scenri/blob/main/docs/A11Y-BACKLOG.md). Biome's recommended accessibility rules run at `error`, so the backlog is currently empty and a new defect fails CI rather than joining a list.
159
161
 
160
162
  Found a security problem? Please report it privately. See [SECURITY.md](https://github.com/tonygorb/scenri/blob/main/SECURITY.md).
161
163
 
@@ -3,7 +3,8 @@ import { resolve } from 'path';
3
3
 
4
4
  // src/bootError.ts
5
5
  var INSTALL_GUIDE = "https://github.com/tonygorb/scenri/blob/main/docs/INSTALL.md";
6
- var NATIVE_MARKERS = ["NODE_MODULE_VERSION", "Could not locate the bindings file", "ERR_DLOPEN_FAILED"];
6
+ var NATIVE_MARKERS = ["NODE_MODULE_VERSION", "ERR_DLOPEN_FAILED"];
7
+ var NEVER_BUILT = "Could not locate the bindings file";
7
8
  function portBusyLines(port) {
8
9
  const next = port + 1;
9
10
  return [
@@ -18,6 +19,17 @@ function bootErrorLines(err) {
18
19
  const first = raw.split("\n")[0];
19
20
  const code = err?.code;
20
21
  const haystack = `${typeof code === "string" ? code : ""} ${raw}`;
22
+ if (haystack.includes(NEVER_BUILT)) {
23
+ return [
24
+ "Scenri could not start: a native component failed to load.",
25
+ `(${first})`,
26
+ "Its install script did not run: npm 12 skips install scripts until they are allowed.",
27
+ "Fix: allow it once, clear the copy npx kept, then start again:",
28
+ " npm config set allow-scripts=better-sqlite3 --location=user",
29
+ " npm cache npx rm --force",
30
+ " npx scenri"
31
+ ];
32
+ }
21
33
  if (NATIVE_MARKERS.some((marker) => haystack.includes(marker))) {
22
34
  return [
23
35
  "Scenri could not start: a native component failed to load.",
@@ -54,5 +66,5 @@ function anotherScenriLines(port, theirs, ours) {
54
66
  }
55
67
 
56
68
  export { anotherScenriLines, bootErrorLines, portBusyLines, shouldAdoptRunning };
57
- //# sourceMappingURL=chunk-CMLJGPYC.js.map
58
- //# sourceMappingURL=chunk-CMLJGPYC.js.map
69
+ //# sourceMappingURL=chunk-GTDE2BS2.js.map
70
+ //# sourceMappingURL=chunk-GTDE2BS2.js.map
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { bootErrorLines } from './chunk-CMLJGPYC.js';
2
+ import { bootErrorLines } from './chunk-GTDE2BS2.js';
3
3
  import { fileURLToPath } from 'url';
4
4
 
5
5
  // src/args.ts
@@ -13,8 +13,8 @@ function shouldOfferDesktop(i) {
13
13
  return true;
14
14
  }
15
15
  var OFFER_QUESTION = " Add Scenri to your desktop? Then you can open it without a terminal. [Y/n] ";
16
- var LATER_LINE = " Not now. Add it later with: npx scenri desktop, or from Settings > About.";
17
- var STILL_RUNNING_LINE = " Scenri is running anyway. Add the icon later from Settings > About.";
16
+ var LATER_LINE = " Not now. Add it later with: npx scenri desktop, or from Settings > Local access.";
17
+ var STILL_RUNNING_LINE = " Scenri is running anyway. Add the icon later from Settings > Local access.";
18
18
  var ASK_TIMEOUT_MS = 12e4;
19
19
  function parseOfferAnswer(raw) {
20
20
  const answer = raw.trim().toLowerCase();
@@ -67,5 +67,5 @@ function askOnTerminal(question, timeoutMs = ASK_TIMEOUT_MS, io = {
67
67
  }
68
68
 
69
69
  export { OFFER_QUESTION, askOnTerminal, offerDesktop, parseOfferAnswer, shouldOfferDesktop };
70
- //# sourceMappingURL=offer-W6NJXMWT.js.map
71
- //# sourceMappingURL=offer-W6NJXMWT.js.map
70
+ //# sourceMappingURL=offer-FT2W2SQ7.js.map
71
+ //# sourceMappingURL=offer-FT2W2SQ7.js.map
package/dist/serve.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createUpdateChecker, classify, isReleaseTriplet, findNpm, stageVersion } from './chunk-YK37H6DM.js';
2
- import { shouldAdoptRunning, anotherScenriLines, portBusyLines } from './chunk-CMLJGPYC.js';
2
+ import { shouldAdoptRunning, anotherScenriLines, portBusyLines } from './chunk-GTDE2BS2.js';
3
3
  import { SchemaTooNewError, defaultHome, createCore, BUDGET_EXHAUSTED, SpendCapError, EDIT_REFERENCE_ROLE_DIRECTIVE, REFERENCE_ROLE_DIRECTIVE, ratioLabel, searchTerms, nameMatches, budgetSize, SCHEMA_VERSION, ASPECT_TOLERANCE } from './chunk-2EKTEMJY.js';
4
4
  import { detectInstallKind } from './chunk-PAIAXRAC.js';
5
5
  export { detectInstallKind } from './chunk-PAIAXRAC.js';
@@ -16312,6 +16312,21 @@ function registerImageRoutes(app, deps) {
16312
16312
  var HEADLINES_KEPT = 5;
16313
16313
  var FIRST_PUBLIC = "0.2.0";
16314
16314
  var RELEASES = [
16315
+ {
16316
+ version: "0.20.3",
16317
+ date: "2026-09-27",
16318
+ title: "A clear fix when npm 12 stops Scenri from starting",
16319
+ sections: [
16320
+ {
16321
+ heading: "Install",
16322
+ body: "If npm 12 skipped the install script Scenri's database needs, the terminal now says so and prints the three commands that fix it, instead of blaming a Node update."
16323
+ },
16324
+ {
16325
+ heading: "Fixes",
16326
+ body: "The Court sneakers in the cobalt room example on Home is redrawn, and the desktop icon offer in the terminal now points to Settings > Local access."
16327
+ }
16328
+ ]
16329
+ },
16315
16330
  {
16316
16331
  version: "0.20.2",
16317
16332
  date: "2026-09-27",
@@ -20126,7 +20141,7 @@ async function run3() {
20126
20141
  const ownEntry = fileURLToPath(import.meta.url);
20127
20142
  const { addToDesktop, installDeps } = await import('./cli-PXPCJR7V.js');
20128
20143
  const { refreshLauncher } = await import('./refresh-5HBDLLLB.js');
20129
- const { askOnTerminal, offerDesktop, shouldOfferDesktop } = await import('./offer-W6NJXMWT.js');
20144
+ const { askOnTerminal, offerDesktop, shouldOfferDesktop } = await import('./offer-FT2W2SQ7.js');
20130
20145
  const { launcherInstalled } = await import('./paths-6ZXRZUHY.js');
20131
20146
  const meta = readMeta();
20132
20147
  void refreshLauncher({ ...installDeps(ownEntry), ownEntry, installKind, pkg: meta.name }).then((r) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scenri",
3
- "version": "0.20.2",
3
+ "version": "0.20.3",
4
4
  "author": "Tony Gorb <hello@scenri.co>",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
@@ -18,7 +18,7 @@
18
18
  },
19
19
  {
20
20
  "t": "text",
21
- "v": " She sits on the checker floor of the cobalt room with her knees up, the sole of one court sneaker toward the lens. She wears a cream cable cardigan, a pleated navy mini skirt, white crew socks and the court sneakers. Low and wide at 24mm, the room leaning in around her."
21
+ "v": " She sits on the checker floor of the cobalt room with one knee drawn up and her forearm resting across it, the other leg stretched out toward the lens, heel on the floor and toe tipped up, so the sole of that court sneaker faces the camera. She wears a cream cable cardigan, a pleated navy mini skirt, white crew socks and the court sneakers. Low and wide at 24mm, the room leaning in around her."
22
22
  },
23
23
  {
24
24
  "t": "format",