scenri 0.2.2 → 0.3.0

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/ASSETS-LICENSE.md CHANGED
@@ -1,12 +1,12 @@
1
- # scenri assets license
1
+ # Scenri assets license
2
2
 
3
- scenri's code is AGPL-3.0-only (see LICENSE). The imagery and curated content are
3
+ Scenri's code is AGPL-3.0-only (see LICENSE). The imagery and curated content are
4
4
  licensed under these terms instead.
5
5
 
6
6
  This covers the starter content pack, everything under `templates/`, the library
7
- content scenri downloads at runtime, and the project's marketing imagery.
7
+ content Scenri downloads at runtime, and the project's marketing imagery.
8
8
 
9
- You may use this content freely within scenri, including for commercial work.
9
+ You may use this content freely within Scenri, including for commercial work.
10
10
  Everything you generate with it is yours.
11
11
 
12
12
  You may not redistribute, rebundle, resell, or republish the content itself, and
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.0](https://github.com/tonygorb/scenri/compare/v0.2.3...v0.3.0) (2026-08-20)
4
+
5
+
6
+ ### Features
7
+
8
+ * give every browser tab an intentional title ([e518396](https://github.com/tonygorb/scenri/commit/e5183961dae9f52ecd5801ae4c8cfce5fb397f3a))
9
+ * name the product Scenri across the interface, docs and CLI ([cce242b](https://github.com/tonygorb/scenri/commit/cce242be955520569fdffb170f14f91f752d1801))
10
+
11
+ ## [0.2.3](https://github.com/tonygorb/scenri/compare/v0.2.2...v0.2.3) (2026-08-20)
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * **deps:** clear the esbuild advisory and a flaky e2e teardown ([97c6985](https://github.com/tonygorb/scenri/commit/97c6985dc7f6e92e3cac1eebd9e039201b12fc16))
17
+ * **deps:** clear the esbuild advisory, and stop the update fixture racing its own teardown ([5cb7bff](https://github.com/tonygorb/scenri/commit/5cb7bff083a92c929bc570b1d88b50747c3a3605))
18
+
3
19
  ## [0.2.2](https://github.com/tonygorb/scenri/compare/v0.2.1...v0.2.2) (2026-08-20)
4
20
 
5
21
 
package/LICENSE CHANGED
@@ -629,7 +629,7 @@ to attach them to the start of each source file to most effectively
629
629
  state the exclusion of warranty; and each file should have at least
630
630
  the "copyright" line and a pointer to where the full notice is found.
631
631
 
632
- scenri - the open studio for brand-consistent AI visuals
632
+ Scenri - the open studio for brand-consistent AI visuals
633
633
  Copyright (C) 2026 Tony Gorb
634
634
 
635
635
  This program is free software: you can redistribute it and/or modify
package/NOTICE CHANGED
@@ -1,4 +1,4 @@
1
- scenri
1
+ Scenri
2
2
  Copyright 2026 Tony Gorb
3
3
 
4
4
  This product includes software and assets developed by third parties.
@@ -46,11 +46,11 @@ Phosphor Icons (`@phosphor-icons/react`), MIT, Copyright (c) 2023 Phosphor Icons
46
46
 
47
47
  ## Third-party marks
48
48
 
49
- scenri shows each generation provider's own mark beside its name, so that the
49
+ Scenri shows each generation provider's own mark beside its name, so that the
50
50
  row asking for your key names the service you recognise rather than a generic
51
51
  glyph. Every mark is reproduced unmodified, in a variant its owner publishes,
52
52
  and is used referentially to identify which service a key or a sign-in belongs
53
- to. scenri is not built, supported, certified, endorsed by, or affiliated with
53
+ to. Scenri is not built, supported, certified, endorsed by, or affiliated with
54
54
  any of them, and makes no such claim.
55
55
 
56
56
  | Mark | File | Artwork source | Artwork licence |
@@ -63,7 +63,7 @@ Each mark sits on the plate its owner puts it on, in the colours its owner
63
63
  publishes, sampled from that owner's own icon: OpenAI's 180px favicon (white
64
64
  plate, black mark), OpenRouter's app icon (white plate, violet mark) and
65
65
  Replicate's favicon (red plate, white mark). The pairs are recorded in `BRAND` in
66
- `apps/studio/src/engines/providers.tsx` and are identical in both of scenri's
66
+ `apps/studio/src/engines/providers.tsx` and are identical in both of Scenri's
67
67
  themes, because none of these owners publishes a dark variant and inventing one
68
68
  would be redrawing their logo. No geometry is redrawn, distorted, recomposed,
69
69
  recoloured or tinted, and no mark is ever separated from its own plate.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  <picture>
4
4
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/logo-on-dark.svg">
5
- <img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/logo-on-light.svg" alt="scenri" width="200">
5
+ <img src="https://raw.githubusercontent.com/tonygorb/scenri/main/docs/media/logo-on-light.svg" alt="Scenri" width="200">
6
6
  </picture>
7
7
  <br><br>
8
8
 
@@ -19,7 +19,7 @@ Define a client's brand once. Then generate, branch, and art-direct on-brand ima
19
19
  npx scenri
20
20
  ```
21
21
 
22
- <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 Selvedge Trucker is chosen, an at sign picks the presenter Maren, a slash picks the Editorial Walk 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">
22
+ <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 Selvedge Trucker is chosen, an at sign picks the presenter Maren, a slash picks the Editorial Walk 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">
23
23
 
24
24
  <sub>Pick a product, pick a presenter, pick a scene, then write the direction. That is the brief.</sub>
25
25
 
@@ -29,7 +29,7 @@ npx scenri
29
29
 
30
30
  ## What it is
31
31
 
32
- 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**.
32
+ 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**.
33
33
 
34
34
  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.
35
35
 
@@ -47,7 +47,7 @@ All 97 examples on the home wall work the same way. Open one and it loads back i
47
47
 
48
48
  ## What you get out of the box
49
49
 
50
- <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">
50
+ <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">
51
51
 
52
52
  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.
53
53
 
@@ -61,7 +61,7 @@ npx scenri
61
61
 
62
62
  That is the whole install. It opens `http://127.0.0.1:4747`.
63
63
 
64
- 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).
64
+ 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).
65
65
 
66
66
  Requires **Node 22 or newer**. Two dependencies (`better-sqlite3` and `sharp`) ship native binaries, so on recent npm you may be asked to approve their install scripts once.
67
67
 
@@ -80,7 +80,7 @@ pnpm dev # starts the server on 127.0.0.1:4747
80
80
 
81
81
  ## First five minutes
82
82
 
83
- 1. **Paste a website URL.** scenri reads the public page and drafts the kit: name, palette, logo, tone.
83
+ 1. **Paste a website URL.** Scenri reads the public page and drafts the kit: name, palette, logo, tone.
84
84
  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.
85
85
  3. **Generate on Codex CLI.** Runs on your own ChatGPT plan, so there is no key and no per-image charge from us.
86
86
  4. **Branch an edit** from any shot, then hit compare. The heatmap shows what moved.
@@ -91,7 +91,7 @@ pnpm dev # starts the server on 127.0.0.1:4747
91
91
  - **Iteration is the product.** A version tree, not a prompt box. Branch, compare, keep the winners.
92
92
  - **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.
93
93
  - **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.
94
- - **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 daily version-number check against npm so updates can announce themselves, 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](docs/updates.md)).
94
+ - **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 daily version-number check against npm so updates can announce themselves, 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](docs/updates.md)).
95
95
 
96
96
  ## Engines
97
97
 
@@ -104,13 +104,13 @@ pnpm dev # starts the server on 127.0.0.1:4747
104
104
 
105
105
  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.
106
106
 
107
- 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.
107
+ 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.
108
108
 
109
- **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.
109
+ **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.
110
110
 
111
111
  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.
112
112
 
113
- 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.
113
+ 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.
114
114
 
115
115
  ## Configuration
116
116
 
@@ -127,7 +127,7 @@ Your Codex session is yours: scenri runs the official `codex` commands on your m
127
127
 
128
128
  `OPENROUTER_API_KEY`, `REPLICATE_API_TOKEN` and `FAL_KEY` are read from the environment as an alternative to entering them in Settings.
129
129
 
130
- **Before you change `SCENRI_HOST`:** scenri has no accounts, so anyone who can reach the port can spend your API keys and delete your library. Setting `SCENRI_HOST=0.0.0.0` opens it to your local network so you can use it from a phone. That path prints a URL carrying a one-time access token, refuses every request without it, and rejects unfamiliar `Host` headers. Treat it as convenience on a network you trust, not as a security boundary.
130
+ **Before you change `SCENRI_HOST`:** Scenri has no accounts, so anyone who can reach the port can spend your API keys and delete your library. Setting `SCENRI_HOST=0.0.0.0` opens it to your local network so you can use it from a phone. That path prints a URL carrying a one-time access token, refuses every request without it, and rejects unfamiliar `Host` headers. Treat it as convenience on a network you trust, not as a security boundary.
131
131
 
132
132
  ## Layout
133
133
 
@@ -158,7 +158,7 @@ Early. The version is `0.x` and interfaces can still move. Everything documented
158
158
 
159
159
  The application is [AGPL-3.0-only](LICENSE). The `.brand` format, its schema, and its validator live in [packages/brand-spec](https://github.com/tonygorb/scenri/tree/main/packages/brand-spec) under Apache-2.0, so any tool may implement or reuse them without taking on copyleft; the npm package carries that license text as `LICENSE-APACHE-2.0-brand-spec`. Contributions require a [CLA](CLA.md).
160
160
 
161
- The bundled imagery and curated content are licensed separately under [ASSETS-LICENSE.md](docs/ASSETS-LICENSE.md): free to use within scenri, commercial work included; no redistribution or rebundling. The npm package's `license` field describes the code; the starter imagery inside it stays under those asset terms.
161
+ The bundled imagery and curated content are licensed separately under [ASSETS-LICENSE.md](docs/ASSETS-LICENSE.md): free to use within Scenri, commercial work included; no redistribution or rebundling. The npm package's `license` field describes the code; the starter imagery inside it stays under those asset terms.
162
162
 
163
163
  The name and the mark are not part of the code license: [TRADEMARKS.md](TRADEMARKS.md) says what a fork may and may not call itself. What the app does and does not send anywhere is written down in [PRIVACY.md](PRIVACY.md).
164
164
 
@@ -202,5 +202,5 @@ async function stageVersion(deps) {
202
202
  }
203
203
 
204
204
  export { classify, createUpdateChecker, fetchDistTagLatest, findNpm, stageVersion };
205
- //# sourceMappingURL=chunk-VE675PSG.js.map
206
- //# sourceMappingURL=chunk-VE675PSG.js.map
205
+ //# sourceMappingURL=chunk-GA7P7K2M.js.map
206
+ //# sourceMappingURL=chunk-GA7P7K2M.js.map
@@ -1,4 +1,4 @@
1
- import { stageVersion, fetchDistTagLatest, findNpm } from './chunk-VE675PSG.js';
1
+ import { stageVersion, fetchDistTagLatest, findNpm } from './chunk-GA7P7K2M.js';
2
2
  import { readMeta } from './chunk-Y3ZPBPLP.js';
3
3
  import { defaultHome, newestStaged, compareSemver } from './chunk-4MAFHYAD.js';
4
4
 
@@ -34,7 +34,7 @@ async function runUpdateCommand(opts) {
34
34
  const effective = staged && compareSemver(staged, meta.version) > 0 ? staged : meta.version;
35
35
  console.log(` running ${meta.version}${staged ? ` \xB7 staged ${staged}` : ""} \xB7 latest ${latest}`);
36
36
  if (compareSemver(latest, effective) <= 0) {
37
- console.log(staged ? " the newest version is already staged. Just start scenri." : " already the newest.");
37
+ console.log(staged ? " the newest version is already staged. Just start Scenri." : " already the newest.");
38
38
  return 0;
39
39
  }
40
40
  if (opts.check) {
@@ -62,5 +62,5 @@ async function runUpdateCommand(opts) {
62
62
  }
63
63
 
64
64
  export { runUpdateCommand };
65
- //# sourceMappingURL=cli-JVN6MY72.js.map
66
- //# sourceMappingURL=cli-JVN6MY72.js.map
65
+ //# sourceMappingURL=cli-GQLRYYGU.js.map
66
+ //# sourceMappingURL=cli-GQLRYYGU.js.map
package/dist/index.js CHANGED
@@ -23,10 +23,10 @@ function parseArgs(argv) {
23
23
  return { cmd: "error", message: `unknown command '${first}' (try --help)` };
24
24
  }
25
25
  function helpText() {
26
- return `scenri: local brand studio
26
+ return `Scenri: local brand studio
27
27
 
28
28
  Usage
29
- scenri start scenri (auto-restarts after updates)
29
+ scenri start Scenri (auto-restarts after updates)
30
30
  scenri serve start this exact build, no supervision
31
31
  scenri update download and stage the newest version
32
32
  --check only check, do not download
@@ -61,7 +61,7 @@ switch (command.cmd) {
61
61
  await (await import('./serve.js')).serve();
62
62
  break;
63
63
  case "update": {
64
- const { runUpdateCommand } = await import('./cli-JVN6MY72.js');
64
+ const { runUpdateCommand } = await import('./cli-GQLRYYGU.js');
65
65
  process.exit(await runUpdateCommand(command));
66
66
  break;
67
67
  }
@@ -71,7 +71,7 @@ switch (command.cmd) {
71
71
  await (await import('./serve.js')).serve();
72
72
  break;
73
73
  }
74
- const { runLauncher } = await import('./launcher-YT5KSSTN.js');
74
+ const { runLauncher } = await import('./launcher-RETYUGSR.js');
75
75
  const { readMeta } = await import('./meta-MPQPBPBK.js');
76
76
  const { defaultHome } = await import('./versionsDir-ILQ4CI7B.js');
77
77
  const meta = readMeta();
@@ -31,7 +31,7 @@ async function runLauncher(deps) {
31
31
  starts.push(startedAt);
32
32
  while (starts.length > 0 && startedAt - starts[0] > LOOP_WINDOW_MS) starts.shift();
33
33
  if (starts.length > LOOP_MAX_STARTS) {
34
- log(" scenri is restarting in a loop and has stopped trying.");
34
+ log(" Scenri is restarting in a loop and has stopped trying.");
35
35
  log(" Recover with: rm -rf ~/.scenri/app (app versions only, your library is untouched)");
36
36
  log(" then start fresh: npx scenri@latest");
37
37
  return 1;
@@ -51,7 +51,7 @@ async function runLauncher(deps) {
51
51
  if (signalled) return code ?? 0;
52
52
  if (code === RESTART_EXIT_CODE) continue;
53
53
  if (code !== 0 && useStaged && now() - startedAt < QUICK_DEATH_MS) {
54
- log(` scenri ${staged} failed to start. Falling back to ${deps.ownVersion}.`);
54
+ log(` Scenri ${staged} failed to start. Falling back to ${deps.ownVersion}.`);
55
55
  log(" If this keeps happening: rm -rf ~/.scenri/app");
56
56
  distrustStaged = true;
57
57
  continue;
@@ -61,5 +61,5 @@ async function runLauncher(deps) {
61
61
  }
62
62
 
63
63
  export { LAUNCHER_PROTOCOL, RESTART_EXIT_CODE, runLauncher };
64
- //# sourceMappingURL=launcher-YT5KSSTN.js.map
65
- //# sourceMappingURL=launcher-YT5KSSTN.js.map
64
+ //# sourceMappingURL=launcher-RETYUGSR.js.map
65
+ //# sourceMappingURL=launcher-RETYUGSR.js.map
package/dist/serve.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createUpdateChecker, classify, stageVersion, findNpm } from './chunk-VE675PSG.js';
1
+ import { createUpdateChecker, classify, stageVersion, findNpm } from './chunk-GA7P7K2M.js';
2
2
  import { readMeta, repoSlug } from './chunk-Y3ZPBPLP.js';
3
3
  import { newestStaged, compareSemver } from './chunk-4MAFHYAD.js';
4
4
  import { networkInterfaces, homedir, tmpdir } from 'os';
@@ -297,7 +297,7 @@ var SCHEMA_VERSION = 1;
297
297
  var SchemaTooNewError = class extends Error {
298
298
  constructor(found, supported) {
299
299
  super(
300
- `This library was written by a newer scenri (schema ${found}; this build understands ${supported}). Update and retry: npx scenri@latest`
300
+ `This library was written by a newer Scenri (schema ${found}; this build understands ${supported}). Update and retry: npx scenri@latest`
301
301
  );
302
302
  this.name = "SchemaTooNewError";
303
303
  }
@@ -2149,7 +2149,7 @@ function createCodexSetup(opts = {}) {
2149
2149
  ok: false,
2150
2150
  fallbackCommand: INSTALL_COMMAND,
2151
2151
  docsUrl: INSTALL_DOCS_URL,
2152
- detail: "Codex installed but is not on this app\u2019s PATH yet. Restart scenri, or install it yourself."
2152
+ detail: "Codex installed but is not on this app\u2019s PATH yet. Restart Scenri, or install it yourself."
2153
2153
  };
2154
2154
  }
2155
2155
  return { ok: true };
@@ -2308,7 +2308,7 @@ function createDemoEngine(saveImage) {
2308
2308
  <rect width="100%" height="100%" fill="url(#g)"/>
2309
2309
  <circle cx="${w * (0.25 + 0.5 * (seed * 37 % 100) / 100)}" cy="${h * 0.38}" r="${Math.min(w, h) * 0.18}" fill="#ffffff" opacity="0.25"/>
2310
2310
  <text x="24" y="${h - 48}" font-family="Helvetica, Arial" font-size="${Math.max(14, Math.round(w / 42))}" fill="#ffffff" opacity="0.92">${esc(label)}</text>
2311
- <text x="24" y="${h - 22}" font-family="Helvetica, Arial" font-size="12" fill="#ffffff" opacity="0.6">scenri demo engine</text>
2311
+ <text x="24" y="${h - 22}" font-family="Helvetica, Arial" font-size="12" fill="#ffffff" opacity="0.6">Scenri demo engine</text>
2312
2312
  </svg>`;
2313
2313
  return sharp5(Buffer.from(svg)).png().toBuffer();
2314
2314
  }
@@ -2946,7 +2946,7 @@ function createContentFetcher(deps) {
2946
2946
  const root = contentCacheRoot(env);
2947
2947
  const staging = `${root}.staging`;
2948
2948
  if (!deps.store.getSetting("content.disclosed")) {
2949
- log(" fetching the scenri library (~95 MB, once, cached; set SCENRI_NO_CONTENT_FETCH=1 to disable)");
2949
+ log(" fetching the Scenri library (~95 MB, once, cached; set SCENRI_NO_CONTENT_FETCH=1 to disable)");
2950
2950
  deps.store.setSetting("content.disclosed", "1");
2951
2951
  }
2952
2952
  try {
@@ -2982,7 +2982,7 @@ function createContentFetcher(deps) {
2982
2982
  deps.store.setSetting("content.version", String(meta.version ?? ""));
2983
2983
  } catch {
2984
2984
  }
2985
- log(" scenri library ready");
2985
+ log(" Scenri library ready");
2986
2986
  return { ok: true, updated: true, error: null };
2987
2987
  } catch (err) {
2988
2988
  rmSync(staging, { recursive: true, force: true });
@@ -5900,11 +5900,11 @@ function registerAssetBuildRoutes(app, deps) {
5900
5900
  engineId: engine?.capabilities().id ?? null,
5901
5901
  engineName: engine?.capabilities().displayName ?? null,
5902
5902
  /**
5903
- * True when scenri cannot price this per image, because it is not billed
5903
+ * True when Scenri cannot price this per image, because it is not billed
5904
5904
  * through a key we hold. NOT the same as costing the user nothing: the
5905
5905
  * local Codex engine spends the Codex allowance on their ChatGPT plan,
5906
5906
  * which only OpenAI can meter. Copy built on this flag must say "nothing
5907
- * billed through scenri", never "free".
5907
+ * billed through Scenri", never "free".
5908
5908
  */
5909
5909
  free: engine ? await engine.costEstimate(COST_PROBE).catch(() => 0) <= 0 : true
5910
5910
  };
@@ -6514,6 +6514,31 @@ function registerImageRoutes(app, deps) {
6514
6514
 
6515
6515
  // src/release/notes.data.ts
6516
6516
  var RELEASES = [
6517
+ {
6518
+ version: "0.3.0",
6519
+ date: "2026-08-20",
6520
+ title: "The name is Scenri.",
6521
+ sections: [
6522
+ {
6523
+ heading: "The name",
6524
+ body: "Written out, the product is Scenri. Every screen, every page of the documentation and every line the terminal prints now spells it that way. Lowercase stays where a machine reads it: the command you type, the folder your library lives in, and the settings that configure it are all unchanged."
6525
+ },
6526
+ {
6527
+ heading: "Browser tabs",
6528
+ body: "A tab now names what is open in it rather than the section it came from, so eight products in eight tabs read as eight product names. Settings and What's new say so, an open shot says Shot, and a tab whose page is still loading names its section instead of going blank."
6529
+ }
6530
+ ]
6531
+ },
6532
+ {
6533
+ version: "0.2.3",
6534
+ date: "2026-08-20",
6535
+ sections: [
6536
+ {
6537
+ heading: "Security",
6538
+ body: "The last advisory against a build dependency is closed. Nothing about how scenri runs changes, and nothing in the published package moves."
6539
+ }
6540
+ ]
6541
+ },
6517
6542
  {
6518
6543
  version: "0.2.2",
6519
6544
  date: "2026-08-20",
@@ -6690,7 +6715,7 @@ function registerUpdateRoutes(app, deps) {
6690
6715
  const apply = effectiveApply();
6691
6716
  if (apply.phase !== "ready") return reply.status(409).send({ error: "no staged update to restart into" });
6692
6717
  if (!runtime.supervised) {
6693
- return reply.status(409).send({ error: "not supervised; restart scenri yourself", blockReason: "unsupervised" });
6718
+ return reply.status(409).send({ error: "not supervised; restart Scenri yourself", blockReason: "unsupervised" });
6694
6719
  }
6695
6720
  reply.send({ ok: true });
6696
6721
  const exit = deps.exitImpl ?? ((code) => process.exit(code));
@@ -7495,7 +7520,7 @@ async function run() {
7495
7520
  if (info.name === readMeta().name) {
7496
7521
  const url = `http://127.0.0.1:${PORT}`;
7497
7522
  console.log(`
7498
- scenri is already running \u2192 ${url}
7523
+ Scenri is already running \u2192 ${url}
7499
7524
  `);
7500
7525
  if (process.env.SCENRI_NO_OPEN !== "1") {
7501
7526
  try {
@@ -7517,7 +7542,7 @@ async function run() {
7517
7542
  if (closing) return;
7518
7543
  closing = true;
7519
7544
  console.log(`
7520
- ${signal}: closing scenri\u2026`);
7545
+ ${signal}: closing Scenri\u2026`);
7521
7546
  setTimeout(() => process.exit(1), 5e3).unref();
7522
7547
  app.drain().then(() => process.exit(0)).catch(() => process.exit(1));
7523
7548
  };
@@ -7528,7 +7553,7 @@ async function run() {
7528
7553
  const query = token ? `/?t=${token}` : "";
7529
7554
  const localUrl = `http://127.0.0.1:${PORT}${query}`;
7530
7555
  console.log(`
7531
- scenri studio \u2192 ${localUrl}`);
7556
+ Scenri Studio \u2192 ${localUrl}`);
7532
7557
  for (const ip of reachableAt) console.log(` on your network \u2192 http://${ip}:${PORT}${query}`);
7533
7558
  console.log(` data dir \u2192 ${core.home}
7534
7559
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scenri",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "author": "Tony Gorb <hello@scenri.co>",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",