scenri 0.9.0 → 0.9.2
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 +17 -0
- package/dist/{chunk-4NQKRVO2.js → chunk-2IGINCPU.js} +13 -7
- package/dist/{chunk-WGIZJXNE.js → chunk-CMLJGPYC.js} +25 -3
- package/dist/{chunk-QDODTMSN.js → chunk-ECS5LRHB.js} +12 -5
- package/dist/{chunk-HLMR33DT.js → chunk-SDVL5OJF.js} +19 -10
- package/dist/{cli-BVPVS4V5.js → cli-FYWM4HBN.js} +204 -54
- package/dist/index.js +3 -3
- package/dist/install-4W6AARK6.js +4 -0
- package/dist/paths-6ZXRZUHY.js +3 -0
- package/dist/{refresh-7HXPUHLQ.js → refresh-FKQABJOC.js} +26 -12
- package/dist/serve.js +64 -8
- package/launcher/launch.mjs +20 -2
- package/launcher/starting.html +6 -3
- package/package.json +1 -1
- package/studio-dist/assets/{index-DSSGRUMi.js → index-CqJgKSNM.js} +1 -1
- package/studio-dist/index.html +1 -1
- package/dist/install-V6DAA7TT.js +0 -4
- package/dist/paths-36Y73GGY.js +0 -3
|
@@ -1,37 +1,48 @@
|
|
|
1
|
-
import { adoptRunningInstall } from './chunk-
|
|
1
|
+
import { adoptRunningInstall } from './chunk-ECS5LRHB.js';
|
|
2
2
|
import { newestStaged, compareSemver } from './chunk-4MAFHYAD.js';
|
|
3
|
-
import {
|
|
4
|
-
import { readLauncherRecord, launcherDir, LAUNCHER_SCHEMA, writeLauncherRecord } from './chunk-
|
|
3
|
+
import { runningNodeMajor, stableExecPath, isOurMacBundle, MAC_SCRIPT, macPlist, writeSupportFiles, writeMacBundle, writeLnk } from './chunk-SDVL5OJF.js';
|
|
4
|
+
import { readLauncherRecord, launcherDir, LAUNCHER_SCHEMA, writeLauncherRecord } from './chunk-2IGINCPU.js';
|
|
5
5
|
import { existsSync, readFileSync } from 'fs';
|
|
6
6
|
import { join } from 'path';
|
|
7
7
|
|
|
8
8
|
var ASSETS = ["launch.mjs", "Scenri.icns", "scenri.ico"];
|
|
9
9
|
async function refreshLauncher(deps) {
|
|
10
10
|
if (deps.env.SCENRI_NO_DESKTOP === "1") return {};
|
|
11
|
+
if (deps.installKind === "dev") return {};
|
|
11
12
|
const record = readLauncherRecord(deps.homedir);
|
|
12
13
|
if (!record) return {};
|
|
13
14
|
const out = {};
|
|
15
|
+
const running = runningNodeMajor();
|
|
16
|
+
const ownNode = stableExecPath(deps.execPath, deps.platform);
|
|
17
|
+
const nodeGone = !existsSync(record.nodePath);
|
|
18
|
+
const majorChanged = (nodeGone || samePath(record.nodePath, ownNode, deps.platform) || samePath(record.nodePath, deps.execPath, deps.platform)) && record.nodeMajor !== void 0 && record.nodeMajor !== running;
|
|
19
|
+
let rebuilt = false;
|
|
14
20
|
if (deps.installKind === "npx" || deps.installKind === "global") {
|
|
15
21
|
const newest = newestStaged(deps.home, deps.pkg);
|
|
16
|
-
|
|
22
|
+
const newer = !newest || compareSemver(deps.version, newest) > 0;
|
|
23
|
+
const rebuild = majorChanged && newest !== null && compareSemver(deps.version, newest) >= 0;
|
|
24
|
+
if (newer || rebuild) {
|
|
17
25
|
const r = await adoptRunningInstall({
|
|
18
26
|
home: deps.home,
|
|
19
27
|
pkg: deps.pkg,
|
|
20
28
|
version: deps.version,
|
|
21
29
|
ownEntry: deps.ownEntry,
|
|
22
30
|
installKind: deps.installKind,
|
|
23
|
-
verifyImpl: deps.verifyImpl
|
|
31
|
+
verifyImpl: deps.verifyImpl,
|
|
32
|
+
force: rebuild
|
|
24
33
|
});
|
|
25
|
-
if (r.adopted)
|
|
34
|
+
if (r.adopted) {
|
|
35
|
+
out.adopted = true;
|
|
36
|
+
rebuilt = rebuild;
|
|
37
|
+
}
|
|
26
38
|
}
|
|
27
39
|
}
|
|
28
40
|
const support = launcherDir(deps.homedir);
|
|
29
41
|
const artifact = record.artifact.path;
|
|
30
42
|
const ours = existsSync(artifact) && (record.artifact.kind === "macos-app" ? isOurMacBundle(artifact) : true);
|
|
31
43
|
const schemaStale = record.schema !== LAUNCHER_SCHEMA;
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const nodeMajor = nodeGone ? runningNodeMajor() : record.nodeMajor ?? runningNodeMajor();
|
|
44
|
+
const nodePath = nodeGone ? ownNode : record.nodePath;
|
|
45
|
+
const nodeMajor = nodeGone || rebuilt ? running : record.nodeMajor ?? running;
|
|
35
46
|
const nodeFilesStale = deps.platform === "darwin" && (readText(join(support, "node-path")) !== `${nodePath}
|
|
36
47
|
` || readText(join(support, "node-major")) !== `${nodeMajor}
|
|
37
48
|
`);
|
|
@@ -59,7 +70,7 @@ async function refreshLauncher(deps) {
|
|
|
59
70
|
icon: `${join(support, "scenri.ico")},0`
|
|
60
71
|
});
|
|
61
72
|
}
|
|
62
|
-
if (schemaStale || supportStale || nodeGone || iconStale || bundleStale) {
|
|
73
|
+
if (schemaStale || supportStale || nodeGone || iconStale || bundleStale || rebuilt) {
|
|
63
74
|
writeLauncherRecord(deps.homedir, {
|
|
64
75
|
...record,
|
|
65
76
|
schema: LAUNCHER_SCHEMA,
|
|
@@ -71,6 +82,9 @@ async function refreshLauncher(deps) {
|
|
|
71
82
|
}
|
|
72
83
|
return out;
|
|
73
84
|
}
|
|
85
|
+
function samePath(a, b, platform) {
|
|
86
|
+
return platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
87
|
+
}
|
|
74
88
|
function readText(path) {
|
|
75
89
|
try {
|
|
76
90
|
return readFileSync(path, "utf8");
|
|
@@ -87,5 +101,5 @@ function sameFile(a, b) {
|
|
|
87
101
|
}
|
|
88
102
|
|
|
89
103
|
export { refreshLauncher };
|
|
90
|
-
//# sourceMappingURL=refresh-
|
|
91
|
-
//# sourceMappingURL=refresh-
|
|
104
|
+
//# sourceMappingURL=refresh-FKQABJOC.js.map
|
|
105
|
+
//# sourceMappingURL=refresh-FKQABJOC.js.map
|
package/dist/serve.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createUpdateChecker, classify, isReleaseTriplet, findNpm, stageVersion } from './chunk-FYQ5BAFA.js';
|
|
2
|
-
import { portBusyLines } from './chunk-
|
|
2
|
+
import { shouldAdoptRunning, anotherScenriLines, portBusyLines } from './chunk-CMLJGPYC.js';
|
|
3
3
|
import { SchemaTooNewError, createCore, BUDGET_EXHAUSTED, SpendCapError, budgetSize, EDIT_REFERENCE_ROLE_DIRECTIVE, REFERENCE_ROLE_DIRECTIVE, ratioLabel, searchTerms, termMatches, SCHEMA_VERSION, ASPECT_TOLERANCE } from './chunk-OJAG3FRX.js';
|
|
4
4
|
import { detectInstallKind } from './chunk-PAIAXRAC.js';
|
|
5
5
|
export { detectInstallKind } from './chunk-PAIAXRAC.js';
|
|
@@ -740,6 +740,10 @@ function codexFailureDetail(stderr, stdout) {
|
|
|
740
740
|
const marked = body.startsWith("ERROR:") ? body : errorAt >= 0 ? body.slice(errorAt + 1) : "";
|
|
741
741
|
return tailOf(marked) || tailOf(body) || tailOf(stdout.trim()) || tailOf(stderr.trim());
|
|
742
742
|
}
|
|
743
|
+
var MODEL_NEEDS_NEWER_CODEX = /The '([^']+)' model requires a newer version of Codex/;
|
|
744
|
+
function tooOldForModel(version, model) {
|
|
745
|
+
return `Codex CLI ${version ?? "on this computer"} is too old for the model it is set to, ${model}.`;
|
|
746
|
+
}
|
|
743
747
|
function killTree(child, platform, spawnImpl) {
|
|
744
748
|
if (platform === "win32" && child.pid) {
|
|
745
749
|
try {
|
|
@@ -783,6 +787,8 @@ function createRunner(opts = {}) {
|
|
|
783
787
|
resolved ??= await resolveCodex(platform, spawnImpl);
|
|
784
788
|
return resolved;
|
|
785
789
|
}
|
|
790
|
+
let knownVersion = null;
|
|
791
|
+
let tooOldFor = null;
|
|
786
792
|
const winArg = (a) => `"${a.replace(/[\r\n]+/g, " ").replace(/"/g, "'").replace(/%/g, " percent ")}"`;
|
|
787
793
|
const spawnCodex = (exe, args, stdinOpen) => {
|
|
788
794
|
const stdio = [stdinOpen ? "pipe" : "ignore", "pipe", "pipe"];
|
|
@@ -890,6 +896,17 @@ function createRunner(opts = {}) {
|
|
|
890
896
|
);
|
|
891
897
|
return;
|
|
892
898
|
}
|
|
899
|
+
const newer = MODEL_NEEDS_NEWER_CODEX.exec(stderr);
|
|
900
|
+
if (newer) {
|
|
901
|
+
const model = newer[1];
|
|
902
|
+
tooOldFor = { version: knownVersion, model };
|
|
903
|
+
invalidateProbe();
|
|
904
|
+
finish(
|
|
905
|
+
`exit-${code ?? "unknown"}`,
|
|
906
|
+
() => reject(new Error(`${tooOldForModel(knownVersion, model)} Update Codex CLI, then run this again.`))
|
|
907
|
+
);
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
893
910
|
const snippet2 = codexFailureDetail(stderr, stdout);
|
|
894
911
|
finish(
|
|
895
912
|
`exit-${code ?? "unknown"}`,
|
|
@@ -971,6 +988,7 @@ function createRunner(opts = {}) {
|
|
|
971
988
|
return verdict({ ok: false, reason: NOT_INSTALLED_REASON, code: "not-installed" }, exe, null);
|
|
972
989
|
}
|
|
973
990
|
const version = parseCodexVersion(ver.stdout);
|
|
991
|
+
knownVersion = version;
|
|
974
992
|
if (version && !versionAtLeast(version, MIN_CODEX_VERSION)) {
|
|
975
993
|
return verdict(
|
|
976
994
|
{
|
|
@@ -982,6 +1000,16 @@ function createRunner(opts = {}) {
|
|
|
982
1000
|
version
|
|
983
1001
|
);
|
|
984
1002
|
}
|
|
1003
|
+
if (tooOldFor) {
|
|
1004
|
+
if (version === tooOldFor.version) {
|
|
1005
|
+
return verdict(
|
|
1006
|
+
{ ok: false, reason: tooOldForModel(version, tooOldFor.model), code: "update-needed" },
|
|
1007
|
+
exe,
|
|
1008
|
+
version
|
|
1009
|
+
);
|
|
1010
|
+
}
|
|
1011
|
+
tooOldFor = null;
|
|
1012
|
+
}
|
|
985
1013
|
const login = await probeSpawn(exe, ["login", "status"]);
|
|
986
1014
|
if (login.outcome === "ok") {
|
|
987
1015
|
return verdict({ ok: true }, exe, version);
|
|
@@ -1495,7 +1523,7 @@ function createCodexEngine(opts) {
|
|
|
1495
1523
|
}
|
|
1496
1524
|
};
|
|
1497
1525
|
function isFatalSetupError(err) {
|
|
1498
|
-
return /failed to spawn|ENOENT|not logged in|login required|401|unauthorized/i.test(
|
|
1526
|
+
return /failed to spawn|ENOENT|not logged in|login required|401|unauthorized|is too old/i.test(
|
|
1499
1527
|
String(err?.message ?? err)
|
|
1500
1528
|
);
|
|
1501
1529
|
}
|
|
@@ -7789,6 +7817,26 @@ function registerImageRoutes(app, deps) {
|
|
|
7789
7817
|
|
|
7790
7818
|
// src/release/notes.data.ts
|
|
7791
7819
|
var RELEASES = [
|
|
7820
|
+
{
|
|
7821
|
+
version: "0.9.2",
|
|
7822
|
+
date: "2026-09-07",
|
|
7823
|
+
sections: [
|
|
7824
|
+
{
|
|
7825
|
+
heading: "Fixes",
|
|
7826
|
+
body: "If the Codex app has switched to a model your Codex CLI is too old for, a shot now stops with a plain sentence naming the version and the model, and the setup dialog opens on the update step rather than a green check. Update the CLI once and generation resumes."
|
|
7827
|
+
}
|
|
7828
|
+
]
|
|
7829
|
+
},
|
|
7830
|
+
{
|
|
7831
|
+
version: "0.9.1",
|
|
7832
|
+
date: "2026-09-07",
|
|
7833
|
+
sections: [
|
|
7834
|
+
{
|
|
7835
|
+
heading: "Desktop",
|
|
7836
|
+
body: "The desktop icon on Windows now ends in the studio or in one plain sentence, never in silence. On both systems the Starting page and the studio open in your default browser, a start that fails says why, and after a Node.js upgrade the next terminal start rebuilds the copy the icon runs."
|
|
7837
|
+
}
|
|
7838
|
+
]
|
|
7839
|
+
},
|
|
7792
7840
|
{
|
|
7793
7841
|
version: "0.9.0",
|
|
7794
7842
|
date: "2026-09-06",
|
|
@@ -8739,15 +8787,15 @@ function registerDesktopRoutes(app, deps) {
|
|
|
8739
8787
|
record: null
|
|
8740
8788
|
};
|
|
8741
8789
|
}
|
|
8742
|
-
const { installDeps } = await import('./cli-
|
|
8743
|
-
const { desktopStatus } = await import('./install-
|
|
8790
|
+
const { installDeps } = await import('./cli-FYWM4HBN.js');
|
|
8791
|
+
const { desktopStatus } = await import('./install-4W6AARK6.js');
|
|
8744
8792
|
return desktopStatus(installDeps(runtime.entry));
|
|
8745
8793
|
});
|
|
8746
8794
|
const install = deps.installImpl ?? (async () => {
|
|
8747
8795
|
if (!runtime.entry) {
|
|
8748
8796
|
return { ok: false, reason: "unsupported", message: "Desktop shortcuts are not available on this system yet." };
|
|
8749
8797
|
}
|
|
8750
|
-
const { addToDesktop } = await import('./cli-
|
|
8798
|
+
const { addToDesktop } = await import('./cli-FYWM4HBN.js');
|
|
8751
8799
|
return addToDesktop(runtime.entry);
|
|
8752
8800
|
});
|
|
8753
8801
|
app.get("/api/desktop", async () => {
|
|
@@ -10260,6 +10308,14 @@ async function run() {
|
|
|
10260
10308
|
});
|
|
10261
10309
|
const info = await res.json();
|
|
10262
10310
|
if (info.name === readMeta().name) {
|
|
10311
|
+
if (!shouldAdoptRunning({ installKind, ourHome: core.home, theirHome: info.home })) {
|
|
10312
|
+
console.error("");
|
|
10313
|
+
for (const line of anotherScenriLines(PORT, info.home ?? "another library", core.home)) {
|
|
10314
|
+
console.error(` ${line}`);
|
|
10315
|
+
}
|
|
10316
|
+
console.error("");
|
|
10317
|
+
process.exit(1);
|
|
10318
|
+
}
|
|
10263
10319
|
const url = `http://127.0.0.1:${PORT}`;
|
|
10264
10320
|
console.log(`
|
|
10265
10321
|
Scenri is already running \u2192 ${url}
|
|
@@ -10332,10 +10388,10 @@ async function run() {
|
|
|
10332
10388
|
}
|
|
10333
10389
|
}
|
|
10334
10390
|
const ownEntry = fileURLToPath(import.meta.url);
|
|
10335
|
-
const { addToDesktop, installDeps } = await import('./cli-
|
|
10336
|
-
const { refreshLauncher } = await import('./refresh-
|
|
10391
|
+
const { addToDesktop, installDeps } = await import('./cli-FYWM4HBN.js');
|
|
10392
|
+
const { refreshLauncher } = await import('./refresh-FKQABJOC.js');
|
|
10337
10393
|
const { askOnTerminal, offerDesktop, shouldOfferDesktop } = await import('./offer-IVTNHIDJ.js');
|
|
10338
|
-
const { launcherInstalled } = await import('./paths-
|
|
10394
|
+
const { launcherInstalled } = await import('./paths-6ZXRZUHY.js');
|
|
10339
10395
|
const meta = readMeta();
|
|
10340
10396
|
void refreshLauncher({ ...installDeps(ownEntry), ownEntry, installKind, pkg: meta.name }).then((r) => {
|
|
10341
10397
|
if (r.adopted)
|
package/launcher/launch.mjs
CHANGED
|
@@ -22,7 +22,9 @@ const record = readJson(join(here, 'launcher.json'));
|
|
|
22
22
|
const home = typeof record?.home === 'string' && record.home ? record.home : join(homedir(), '.scenri');
|
|
23
23
|
const logPath = join(home, 'logs', 'launcher.log');
|
|
24
24
|
|
|
25
|
-
log(
|
|
25
|
+
log(
|
|
26
|
+
`bootstrap v${SCHEMA}: invoked from ${here} by ${process.execPath} (node ${process.version}, ${process.platform}), home ${home}`,
|
|
27
|
+
);
|
|
26
28
|
const version = newestVersion(home);
|
|
27
29
|
if (!version) {
|
|
28
30
|
log(`bootstrap v${SCHEMA}: no valid version under ${join(home, 'app', 'versions')}`);
|
|
@@ -49,6 +51,13 @@ const child = spawn(process.execPath, [entry, 'open'], {
|
|
|
49
51
|
env,
|
|
50
52
|
});
|
|
51
53
|
if (fd !== null) closeSync(fd);
|
|
54
|
+
// A node that cannot be executed arrives as an event, not an exit, and unhandled
|
|
55
|
+
// it would end this process with nothing on screen.
|
|
56
|
+
child.on('error', (err) => {
|
|
57
|
+
log(`bootstrap v${SCHEMA}: could not start ${entry} (${err.message})`);
|
|
58
|
+
dialog(`Scenri could not start (${err.message}). Open a terminal and run: npx scenri`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
});
|
|
52
61
|
log(`bootstrap v${SCHEMA}: handing off to ${entry} (pid ${child.pid ?? 'unknown'})`);
|
|
53
62
|
if (wait) {
|
|
54
63
|
child.on('exit', (code) => process.exit(code ?? 1));
|
|
@@ -127,8 +136,10 @@ function dialog(message) {
|
|
|
127
136
|
{ stdio: 'ignore' },
|
|
128
137
|
);
|
|
129
138
|
} else if (process.platform === 'win32') {
|
|
139
|
+
// windowsHide keeps the console off screen and the box still shows;
|
|
140
|
+
// probed on a real Windows desktop, where a detached PowerShell shows nothing.
|
|
130
141
|
spawnSync(
|
|
131
|
-
|
|
142
|
+
powershell(),
|
|
132
143
|
[
|
|
133
144
|
'-NoProfile',
|
|
134
145
|
'-NonInteractive',
|
|
@@ -142,3 +153,10 @@ function dialog(message) {
|
|
|
142
153
|
/* the log line above is the record */
|
|
143
154
|
}
|
|
144
155
|
}
|
|
156
|
+
|
|
157
|
+
/** Windows PowerShell 5.1 by its absolute path: a Desktop click has Explorer's PATH, not the terminal's. */
|
|
158
|
+
function powershell() {
|
|
159
|
+
const root = process.env.SystemRoot || process.env.windir || 'C:\\Windows';
|
|
160
|
+
const absolute = join(root, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
|
161
|
+
return existsSync(absolute) ? absolute : 'powershell.exe';
|
|
162
|
+
}
|
package/launcher/starting.html
CHANGED
|
@@ -49,9 +49,12 @@
|
|
|
49
49
|
var bar = document.getElementById('bar');
|
|
50
50
|
var deadline = Date.now() + 90000;
|
|
51
51
|
var done = false;
|
|
52
|
+
var failed = false;
|
|
52
53
|
function go() { if (done) return; done = true; location.replace(target); }
|
|
54
|
+
// The fail text is a verdict on the wait, not on the server: a start that
|
|
55
|
+
// finishes late (a first Defender scan of a fresh install) still hands over.
|
|
53
56
|
function fail(message) {
|
|
54
|
-
if (done) return;
|
|
57
|
+
if (done || failed) return; failed = true;
|
|
55
58
|
title.textContent = 'Scenri did not start';
|
|
56
59
|
text.innerHTML = message;
|
|
57
60
|
bar.hidden = true;
|
|
@@ -59,7 +62,7 @@
|
|
|
59
62
|
if (!ok) { fail('This page needs to be opened by Scenri itself. Open a terminal and run: <code>npx scenri</code>'); return; }
|
|
60
63
|
function probe() {
|
|
61
64
|
if (done) return;
|
|
62
|
-
if (Date.now() > deadline)
|
|
65
|
+
if (Date.now() > deadline) fail('Open a terminal and run <code>npx scenri</code> to see why.');
|
|
63
66
|
// An image load needs no CORS and works from a file: URL in every browser;
|
|
64
67
|
// the opaque fetch is the second opinion for browsers that block images
|
|
65
68
|
// from a local page.
|
|
@@ -69,7 +72,7 @@
|
|
|
69
72
|
if (window.fetch) {
|
|
70
73
|
fetch(target + 'api/version', { mode: 'no-cors', cache: 'no-store' }).then(go, () => {});
|
|
71
74
|
}
|
|
72
|
-
setTimeout(probe, 500);
|
|
75
|
+
setTimeout(probe, failed ? 2000 : 500);
|
|
73
76
|
}
|
|
74
77
|
probe();
|
|
75
78
|
})();
|