scenri 0.3.0 → 0.3.1
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 +8 -0
- package/README.md +6 -2
- package/dist/chunk-WGIZJXNE.js +36 -0
- package/dist/index.js +43 -33
- package/dist/serve.js +33 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.1](https://github.com/tonygorb/scenri/compare/v0.3.0...v0.3.1) (2026-08-20)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* calm boot failures and stop the silent hang on a busy port ([d04946f](https://github.com/tonygorb/scenri/commit/d04946f011b204c230473f5338f0a32f8870a1d0))
|
|
9
|
+
* friendly first-run errors and the install guide ([b1782ad](https://github.com/tonygorb/scenri/commit/b1782adb4fc48807e384a208ee8894022d62d615))
|
|
10
|
+
|
|
3
11
|
## [0.3.0](https://github.com/tonygorb/scenri/compare/v0.2.3...v0.3.0) (2026-08-20)
|
|
4
12
|
|
|
5
13
|
|
package/README.md
CHANGED
|
@@ -19,6 +19,8 @@ Define a client's brand once. Then generate, branch, and art-direct on-brand ima
|
|
|
19
19
|
npx scenri
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
<sub>Needs [Node.js](https://nodejs.org) 22 or newer. New to any of this? The [install guide](docs/INSTALL.md) walks through every step.</sub>
|
|
23
|
+
|
|
22
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 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
25
|
|
|
24
26
|
<sub>Pick a product, pick a presenter, pick a scene, then write the direction. That is the brief.</sub>
|
|
@@ -55,15 +57,17 @@ The brand kit sits one gesture away in Settings and is the part that keeps outpu
|
|
|
55
57
|
|
|
56
58
|
## Run it
|
|
57
59
|
|
|
60
|
+
Scenri runs on [Node.js](https://nodejs.org), version 22 or newer. If you are not sure you have it, or terminals are not part of your day, the **[install guide](docs/INSTALL.md)** covers every step for macOS, Windows and Linux. With Node in place:
|
|
61
|
+
|
|
58
62
|
```bash
|
|
59
63
|
npx scenri
|
|
60
64
|
```
|
|
61
65
|
|
|
62
|
-
That is the whole install.
|
|
66
|
+
That is the whole install. npm asks once whether to proceed, downloads the current release, and opens `http://127.0.0.1:4747`. Keep the terminal window open while you work; closing it stops Scenri. Tomorrow the same command opens it again, with everything where you left it.
|
|
63
67
|
|
|
64
68
|
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
69
|
|
|
66
|
-
|
|
70
|
+
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](docs/INSTALL.md#troubleshooting).
|
|
67
71
|
|
|
68
72
|
<details>
|
|
69
73
|
<summary>Run from source</summary>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// src/bootError.ts
|
|
2
|
+
var INSTALL_GUIDE = "https://github.com/tonygorb/scenri/blob/main/docs/INSTALL.md";
|
|
3
|
+
var NATIVE_MARKERS = ["NODE_MODULE_VERSION", "Could not locate the bindings file", "ERR_DLOPEN_FAILED"];
|
|
4
|
+
function portBusyLines(port) {
|
|
5
|
+
const next = port + 1;
|
|
6
|
+
return [
|
|
7
|
+
`Port ${port} is in use by another app.`,
|
|
8
|
+
"Start Scenri on a different port:",
|
|
9
|
+
` macOS or Linux: SCENRI_PORT=${next} npx scenri`,
|
|
10
|
+
` Windows PowerShell: $env:SCENRI_PORT=${next}; npx scenri`
|
|
11
|
+
];
|
|
12
|
+
}
|
|
13
|
+
function bootErrorLines(err) {
|
|
14
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
15
|
+
const first = raw.split("\n")[0];
|
|
16
|
+
const code = err?.code;
|
|
17
|
+
const haystack = `${typeof code === "string" ? code : ""} ${raw}`;
|
|
18
|
+
if (NATIVE_MARKERS.some((marker) => haystack.includes(marker))) {
|
|
19
|
+
return [
|
|
20
|
+
"Scenri could not start: a native component failed to load.",
|
|
21
|
+
`(${first})`,
|
|
22
|
+
"This usually means Node changed since this copy of Scenri was installed.",
|
|
23
|
+
"Fix: install the current Node LTS from https://nodejs.org, then run npx scenri@latest."
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
return [
|
|
27
|
+
`Scenri could not start: ${first}`,
|
|
28
|
+
"If this keeps happening, run npx scenri@latest, or see the install guide:",
|
|
29
|
+
INSTALL_GUIDE,
|
|
30
|
+
"Set SCENRI_DEBUG=1 to see the full error."
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { bootErrorLines, portBusyLines };
|
|
35
|
+
//# sourceMappingURL=chunk-WGIZJXNE.js.map
|
|
36
|
+
//# sourceMappingURL=chunk-WGIZJXNE.js.map
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { bootErrorLines } from './chunk-WGIZJXNE.js';
|
|
3
|
+
|
|
2
4
|
// src/args.ts
|
|
3
5
|
function parseArgs(argv) {
|
|
4
6
|
const [first, ...rest] = argv;
|
|
@@ -41,43 +43,51 @@ SCENRI_CONTENT_URL.
|
|
|
41
43
|
|
|
42
44
|
// src/index.ts
|
|
43
45
|
var command = parseArgs(process.argv.slice(2));
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
break;
|
|
63
|
-
case "update": {
|
|
64
|
-
const { runUpdateCommand } = await import('./cli-GQLRYYGU.js');
|
|
65
|
-
process.exit(await runUpdateCommand(command));
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
case "launch": {
|
|
69
|
-
const ownEntry = process.argv[1] ?? "";
|
|
70
|
-
if (!/[\\/]dist[\\/]/.test(ownEntry)) {
|
|
46
|
+
try {
|
|
47
|
+
switch (command.cmd) {
|
|
48
|
+
case "version": {
|
|
49
|
+
const { readMeta } = await import('./meta-MPQPBPBK.js');
|
|
50
|
+
console.log(readMeta().version);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
case "help":
|
|
54
|
+
console.log(helpText());
|
|
55
|
+
break;
|
|
56
|
+
case "error":
|
|
57
|
+
console.error(command.message);
|
|
58
|
+
process.exit(2);
|
|
59
|
+
break;
|
|
60
|
+
case "verify":
|
|
61
|
+
await (await import('./serve.js')).verify();
|
|
62
|
+
break;
|
|
63
|
+
case "serve":
|
|
71
64
|
await (await import('./serve.js')).serve();
|
|
72
65
|
break;
|
|
66
|
+
case "update": {
|
|
67
|
+
const { runUpdateCommand } = await import('./cli-GQLRYYGU.js');
|
|
68
|
+
process.exit(await runUpdateCommand(command));
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
case "launch": {
|
|
72
|
+
const ownEntry = process.argv[1] ?? "";
|
|
73
|
+
if (!/[\\/]dist[\\/]/.test(ownEntry)) {
|
|
74
|
+
await (await import('./serve.js')).serve();
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
const { runLauncher } = await import('./launcher-RETYUGSR.js');
|
|
78
|
+
const { readMeta } = await import('./meta-MPQPBPBK.js');
|
|
79
|
+
const { defaultHome } = await import('./versionsDir-ILQ4CI7B.js');
|
|
80
|
+
const meta = readMeta();
|
|
81
|
+
process.exit(await runLauncher({ home: defaultHome(), pkg: meta.name, ownVersion: meta.version, ownEntry }));
|
|
82
|
+
break;
|
|
73
83
|
}
|
|
74
|
-
const { runLauncher } = await import('./launcher-RETYUGSR.js');
|
|
75
|
-
const { readMeta } = await import('./meta-MPQPBPBK.js');
|
|
76
|
-
const { defaultHome } = await import('./versionsDir-ILQ4CI7B.js');
|
|
77
|
-
const meta = readMeta();
|
|
78
|
-
process.exit(await runLauncher({ home: defaultHome(), pkg: meta.name, ownVersion: meta.version, ownEntry }));
|
|
79
|
-
break;
|
|
80
84
|
}
|
|
85
|
+
} catch (err) {
|
|
86
|
+
if (process.env.SCENRI_DEBUG === "1") throw err;
|
|
87
|
+
console.error("");
|
|
88
|
+
for (const line of bootErrorLines(err)) console.error(` ${line}`);
|
|
89
|
+
console.error("");
|
|
90
|
+
process.exit(1);
|
|
81
91
|
}
|
|
82
92
|
//# sourceMappingURL=index.js.map
|
|
83
93
|
//# sourceMappingURL=index.js.map
|
package/dist/serve.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { portBusyLines } from './chunk-WGIZJXNE.js';
|
|
1
2
|
import { createUpdateChecker, classify, stageVersion, findNpm } from './chunk-GA7P7K2M.js';
|
|
2
3
|
import { readMeta, repoSlug } from './chunk-Y3ZPBPLP.js';
|
|
3
4
|
import { newestStaged, compareSemver } from './chunk-4MAFHYAD.js';
|
|
@@ -6514,6 +6515,16 @@ function registerImageRoutes(app, deps) {
|
|
|
6514
6515
|
|
|
6515
6516
|
// src/release/notes.data.ts
|
|
6516
6517
|
var RELEASES = [
|
|
6518
|
+
{
|
|
6519
|
+
version: "0.3.1",
|
|
6520
|
+
date: "2026-08-20",
|
|
6521
|
+
sections: [
|
|
6522
|
+
{
|
|
6523
|
+
heading: "Fixes",
|
|
6524
|
+
body: "Starting Scenri is calmer when something is wrong. A busy port or a failed start now explains itself in plain words instead of a stack trace, and the terminal says to keep its window open while Scenri runs."
|
|
6525
|
+
}
|
|
6526
|
+
]
|
|
6527
|
+
},
|
|
6517
6528
|
{
|
|
6518
6529
|
version: "0.3.0",
|
|
6519
6530
|
date: "2026-08-20",
|
|
@@ -7515,7 +7526,9 @@ async function run() {
|
|
|
7515
7526
|
continue;
|
|
7516
7527
|
}
|
|
7517
7528
|
try {
|
|
7518
|
-
const res = await fetch(`http://127.0.0.1:${PORT}/api/version
|
|
7529
|
+
const res = await fetch(`http://127.0.0.1:${PORT}/api/version`, {
|
|
7530
|
+
signal: AbortSignal.timeout(2e3)
|
|
7531
|
+
});
|
|
7519
7532
|
const info = await res.json();
|
|
7520
7533
|
if (info.name === readMeta().name) {
|
|
7521
7534
|
const url = `http://127.0.0.1:${PORT}`;
|
|
@@ -7533,6 +7546,10 @@ async function run() {
|
|
|
7533
7546
|
}
|
|
7534
7547
|
} catch {
|
|
7535
7548
|
}
|
|
7549
|
+
console.error("");
|
|
7550
|
+
for (const line of portBusyLines(PORT)) console.error(` ${line}`);
|
|
7551
|
+
console.error("");
|
|
7552
|
+
process.exit(1);
|
|
7536
7553
|
}
|
|
7537
7554
|
throw err;
|
|
7538
7555
|
}
|
|
@@ -7555,8 +7572,8 @@ async function run() {
|
|
|
7555
7572
|
console.log(`
|
|
7556
7573
|
Scenri Studio \u2192 ${localUrl}`);
|
|
7557
7574
|
for (const ip of reachableAt) console.log(` on your network \u2192 http://${ip}:${PORT}${query}`);
|
|
7558
|
-
console.log(` data dir \u2192 ${core.home}
|
|
7559
|
-
|
|
7575
|
+
console.log(` data dir \u2192 ${core.home}`);
|
|
7576
|
+
console.log(" Keep this window open while Scenri is running.\n");
|
|
7560
7577
|
if (!studioDist) console.log(" (studio UI not built, API only. Run: pnpm build)\n");
|
|
7561
7578
|
if (token) {
|
|
7562
7579
|
console.log(" Warning: the studio is reachable from your network.");
|
|
@@ -7566,10 +7583,22 @@ async function run() {
|
|
|
7566
7583
|
console.log(" For this machine only, unset SCENRI_HOST.\n");
|
|
7567
7584
|
}
|
|
7568
7585
|
if (process.env.SCENRI_NO_OPEN !== "1") {
|
|
7586
|
+
let told = false;
|
|
7587
|
+
const tellUrl = () => {
|
|
7588
|
+
if (told) return;
|
|
7589
|
+
told = true;
|
|
7590
|
+
console.log(` Open ${localUrl} in your browser.
|
|
7591
|
+
`);
|
|
7592
|
+
};
|
|
7569
7593
|
try {
|
|
7570
7594
|
const { default: open } = await import('open');
|
|
7571
|
-
await open(localUrl);
|
|
7595
|
+
const child = await open(localUrl);
|
|
7596
|
+
child.once("error", tellUrl);
|
|
7597
|
+
child.once("exit", (openExit) => {
|
|
7598
|
+
if (openExit !== null && openExit !== 0) tellUrl();
|
|
7599
|
+
});
|
|
7572
7600
|
} catch {
|
|
7601
|
+
tellUrl();
|
|
7573
7602
|
}
|
|
7574
7603
|
}
|
|
7575
7604
|
}
|