scenri 0.10.2 → 0.10.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,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.10.3](https://github.com/tonygorb/Scenri/compare/v0.10.2...v0.10.3) (2026-09-18)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * look for npm without freezing the server ([db1bde3](https://github.com/tonygorb/Scenri/commit/db1bde3e1450353bd0f62c7ecf2a2796ea2af75d))
9
+ * look for npm without freezing the server ([cec4b03](https://github.com/tonygorb/Scenri/commit/cec4b0327f2dd4f1846e9c9192926876db851ada))
10
+
3
11
  ## [0.10.2](https://github.com/tonygorb/Scenri/compare/v0.10.1...v0.10.2) (2026-09-17)
4
12
 
5
13
 
@@ -1,5 +1,5 @@
1
1
  import { stagingDir, versionsDir, isValidVersionDir, pruneStaged, entryOf } from './chunk-4MAFHYAD.js';
2
- import { spawnSync, spawn } from 'child_process';
2
+ import { spawn, execFile } from 'child_process';
3
3
  import { existsSync, rmSync, mkdirSync, readFileSync, renameSync } from 'fs';
4
4
  import { join, dirname } from 'path';
5
5
 
@@ -122,37 +122,46 @@ function createUpdateChecker(deps) {
122
122
  }
123
123
  };
124
124
  }
125
- function findNpm(opts = {}) {
125
+ async function findNpm(opts = {}) {
126
126
  const env = opts.env ?? process.env;
127
127
  const platform = opts.platform ?? process.platform;
128
- const canRun = opts.canRun ?? ((argv) => {
129
- try {
130
- return spawnSync(argv[0], [...argv.slice(1), "--version"], { stdio: "ignore", timeout: 1e4 }).status === 0;
131
- } catch {
132
- return false;
133
- }
134
- });
135
- if (platform !== "win32" && canRun(["npm"])) return ["npm"];
128
+ const canRun = opts.canRun ?? answersVersion;
129
+ if (platform !== "win32" && await canRun(["npm"])) return ["npm"];
136
130
  const ep = env.npm_execpath?.replace(/npx-cli\.js$/, "npm-cli.js");
137
- if (ep && /npm-cli\.js$/.test(ep) && canRun([process.execPath, ep])) return [process.execPath, ep];
131
+ if (ep && /npm-cli\.js$/.test(ep) && await canRun([process.execPath, ep])) return [process.execPath, ep];
138
132
  if (platform === "win32") {
139
- const cli = windowsNpmCli();
140
- if (cli && canRun([process.execPath, cli])) return [process.execPath, cli];
133
+ const cli = await windowsNpmCli();
134
+ if (cli && await canRun([process.execPath, cli])) return [process.execPath, cli];
141
135
  }
142
136
  return null;
143
137
  }
138
+ function answersVersion(argv) {
139
+ return new Promise((resolve) => {
140
+ try {
141
+ const child = spawn(argv[0], [...argv.slice(1), "--version"], { stdio: "ignore", timeout: 1e4 });
142
+ child.on("error", () => resolve(false));
143
+ child.on("exit", (code) => resolve(code === 0));
144
+ } catch {
145
+ resolve(false);
146
+ }
147
+ });
148
+ }
144
149
  function windowsNpmCli() {
145
- try {
146
- const out = spawnSync("where", ["npm"], { encoding: "utf8", timeout: 1e4 });
147
- if (out.status !== 0 || !out.stdout) return null;
148
- for (const line of out.stdout.split(/\r?\n/)) {
149
- if (!/npm(\.cmd)?$/i.test(line.trim())) continue;
150
- const cli = join(dirname(line.trim()), "node_modules", "npm", "bin", "npm-cli.js");
151
- if (existsSync(cli)) return cli;
150
+ return new Promise((resolve) => {
151
+ try {
152
+ execFile("where", ["npm"], { encoding: "utf8", timeout: 1e4 }, (err, stdout) => {
153
+ if (err || !stdout) return resolve(null);
154
+ for (const line of stdout.split(/\r?\n/)) {
155
+ if (!/npm(\.cmd)?$/i.test(line.trim())) continue;
156
+ const cli = join(dirname(line.trim()), "node_modules", "npm", "bin", "npm-cli.js");
157
+ if (existsSync(cli)) return resolve(cli);
158
+ }
159
+ resolve(null);
160
+ });
161
+ } catch {
162
+ resolve(null);
152
163
  }
153
- } catch {
154
- }
155
- return null;
164
+ });
156
165
  }
157
166
  function run(argv) {
158
167
  return new Promise((resolve) => {
@@ -181,7 +190,7 @@ async function runVerify(entry) {
181
190
  }
182
191
  async function stageVersion(deps) {
183
192
  const env = deps.env ?? process.env;
184
- const npmArgv = deps.npmArgv !== void 0 ? deps.npmArgv : findNpm({ env });
193
+ const npmArgv = deps.npmArgv !== void 0 ? deps.npmArgv : await findNpm({ env });
185
194
  if (!npmArgv) {
186
195
  return { ok: false, reason: "no-npm", detail: "npm is not reachable from this process" };
187
196
  }
@@ -247,5 +256,5 @@ async function stageVersion(deps) {
247
256
  }
248
257
 
249
258
  export { classify, createUpdateChecker, fetchDistTagLatest, findNpm, isReleaseTriplet, stageVersion };
250
- //# sourceMappingURL=chunk-FYQ5BAFA.js.map
251
- //# sourceMappingURL=chunk-FYQ5BAFA.js.map
259
+ //# sourceMappingURL=chunk-YK37H6DM.js.map
260
+ //# sourceMappingURL=chunk-YK37H6DM.js.map
@@ -1,4 +1,4 @@
1
- import { stageVersion, fetchDistTagLatest, findNpm } from './chunk-FYQ5BAFA.js';
1
+ import { stageVersion, fetchDistTagLatest, findNpm } from './chunk-YK37H6DM.js';
2
2
  import { readMeta } from './chunk-Y3ZPBPLP.js';
3
3
  import { defaultHome, newestStaged, compareSemver } from './chunk-4MAFHYAD.js';
4
4
 
@@ -41,7 +41,7 @@ async function runUpdateCommand(opts) {
41
41
  console.log(` ${latest} is available. Stage it with: scenri update`);
42
42
  return 0;
43
43
  }
44
- if (!findNpm()) {
44
+ if (!await findNpm()) {
45
45
  console.error(" npm is not reachable from here, so nothing can be downloaded.");
46
46
  console.error(" Run the newest version directly with: npx scenri@latest");
47
47
  console.error(" (or install Node.js with npm and try again)");
@@ -63,5 +63,5 @@ async function runUpdateCommand(opts) {
63
63
  }
64
64
 
65
65
  export { runUpdateCommand };
66
- //# sourceMappingURL=cli-KSKENGIR.js.map
67
- //# sourceMappingURL=cli-KSKENGIR.js.map
66
+ //# sourceMappingURL=cli-VBJGIJI4.js.map
67
+ //# sourceMappingURL=cli-VBJGIJI4.js.map
package/dist/index.js CHANGED
@@ -81,7 +81,7 @@ try {
81
81
  await (await import('./serve.js')).serve();
82
82
  break;
83
83
  case "update": {
84
- const { runUpdateCommand } = await import('./cli-KSKENGIR.js');
84
+ const { runUpdateCommand } = await import('./cli-VBJGIJI4.js');
85
85
  process.exit(await runUpdateCommand(command));
86
86
  break;
87
87
  }
package/dist/serve.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createUpdateChecker, classify, isReleaseTriplet, findNpm, stageVersion } from './chunk-FYQ5BAFA.js';
1
+ import { createUpdateChecker, classify, findNpm, isReleaseTriplet, stageVersion } from './chunk-YK37H6DM.js';
2
2
  import { shouldAdoptRunning, anotherScenriLines, portBusyLines } from './chunk-CMLJGPYC.js';
3
3
  import { SchemaTooNewError, createCore, BUDGET_EXHAUSTED, SpendCapError, EDIT_REFERENCE_ROLE_DIRECTIVE, REFERENCE_ROLE_DIRECTIVE, ratioLabel, searchTerms, termMatches, budgetSize, SCHEMA_VERSION, ASPECT_TOLERANCE } from './chunk-UUYMUOZQ.js';
4
4
  import { detectInstallKind } from './chunk-PAIAXRAC.js';
@@ -11703,6 +11703,16 @@ function registerImageRoutes(app, deps) {
11703
11703
 
11704
11704
  // src/release/notes.data.ts
11705
11705
  var RELEASES = [
11706
+ {
11707
+ version: "0.10.3",
11708
+ date: "2026-09-18",
11709
+ sections: [
11710
+ {
11711
+ heading: "Fixes",
11712
+ body: "Scenri no longer stops answering for a moment right after the studio opens, while it checks whether one-click updates can run here. On Windows that pause could last several seconds, long enough for a second click on the desktop icon to start Scenri again instead of opening the one already running."
11713
+ }
11714
+ ]
11715
+ },
11706
11716
  {
11707
11717
  version: "0.10.2",
11708
11718
  date: "2026-09-17",
@@ -12530,14 +12540,22 @@ function registerUpdateRoutes(app, deps) {
12530
12540
  return applyState;
12531
12541
  };
12532
12542
  const REQUIRED_PROTOCOL = 1;
12533
- let npmProbe = null;
12543
+ let npmFound = false;
12544
+ let npmAsked = null;
12545
+ const npmKnown = () => {
12546
+ if (runtime.installKind === "dev" || !runtime.supervised || deps.stageImpl) return Promise.resolve();
12547
+ npmAsked ??= findNpm().then((argv) => {
12548
+ npmFound = argv !== null;
12549
+ }).catch(() => {
12550
+ });
12551
+ return npmAsked;
12552
+ };
12534
12553
  const blockReason = () => {
12535
12554
  if (runtime.installKind === "dev") return "dev";
12536
12555
  if (!runtime.supervised) return "unsupervised";
12537
12556
  if ((runtime.launcherProtocol ?? 1) < REQUIRED_PROTOCOL) return "launcher-too-old";
12538
12557
  if (deps.stageImpl) return null;
12539
- npmProbe ??= findNpm() !== null;
12540
- return npmProbe ? null : "no-npm";
12558
+ return npmFound ? null : "no-npm";
12541
12559
  };
12542
12560
  const startStaging = (target) => {
12543
12561
  applyState = { phase: "staging", version: target, error: null };
@@ -12565,12 +12583,13 @@ function registerUpdateRoutes(app, deps) {
12565
12583
  };
12566
12584
  updates.onResult(maybeAutoStage);
12567
12585
  const bootLook = setTimeout(() => {
12568
- updates.check().then(maybeAutoStage).catch(() => {
12586
+ npmKnown().then(() => updates.check()).then(maybeAutoStage).catch(() => {
12569
12587
  });
12570
12588
  }, deps.bootLookMs ?? 15e3);
12571
12589
  bootLook.unref();
12572
12590
  app.addHook("onClose", async () => clearTimeout(bootLook));
12573
12591
  const updateStatus = async (force = false) => {
12592
+ await npmKnown();
12574
12593
  const r = await updates.check(force);
12575
12594
  const kind = r.latest ? classify(meta.version, r.latest) : null;
12576
12595
  const apply = effectiveApply();
@@ -12601,6 +12620,7 @@ function registerUpdateRoutes(app, deps) {
12601
12620
  if (busy > 0) {
12602
12621
  return reply.status(409).send({ error: `work is still running (${busy} task${busy === 1 ? "" : "s"})` });
12603
12622
  }
12623
+ await npmKnown();
12604
12624
  const block = blockReason();
12605
12625
  if (block)
12606
12626
  return reply.status(409).send({ error: `one-click update cannot run here (${block})`, blockReason: block });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scenri",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
4
4
  "author": "Tony Gorb <hello@scenri.co>",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",