rechrome 1.31.5 → 1.31.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rechrome",
3
- "version": "1.31.5",
3
+ "version": "1.31.6",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/snomiao/rechrome.git"
package/rechrome.js CHANGED
@@ -8,7 +8,7 @@ import { createHash, randomBytes } from "crypto";
8
8
  import { mkdirSync, appendFileSync, existsSync, realpathSync, accessSync, cpSync, unlinkSync, readFileSync, readdirSync, renameSync, rmdirSync, constants as fsConstants } from "fs";
9
9
  import { hostname, homedir, networkInterfaces } from "os";
10
10
  import { isIPv4 } from "net";
11
- import { join, basename, dirname } from "path";
11
+ import { join, basename, dirname, resolve } from "path";
12
12
  import { pathToFileURL } from "url";
13
13
  import { createRequire } from "node:module";
14
14
  import { spawn as cpSpawn } from "child_process";
@@ -2053,9 +2053,48 @@ function oxmgrVersion(bin: string): string | null {
2053
2053
  // RECH_DAEMON_MANAGER override — lives in ./daemon-manager.js (pure + tested);
2054
2054
  // here we just supply the runtime inputs (what's on PATH, the oxmgr version).
2055
2055
  let _daemonMgr: DaemonManager | undefined;
2056
+ /**
2057
+ * The first oxmgr that actually runs. PATH order alone isn't enough: a stale global shim
2058
+ * (e.g. bun's, printing "oxmgr binary is missing") can shadow a working build, and npm's
2059
+ * `oxmgr.cmd` re-parses argv through cmd.exe, so real executables come first. RECH_OXMGR
2060
+ * names one explicitly (a path, or a command looked up on PATH). The successful probe's
2061
+ * output seeds the version cache, so `--version` runs once.
2062
+ */
2063
+ function findWorkingOxmgr(): string | null {
2064
+ const ext = IS_WINDOWS ? ".exe" : "";
2065
+ // Windows PATH entries may be quoted ("C:\Program Files\x"); quotes break the joined path.
2066
+ const dirs = (process.env.PATH ?? "").split(IS_WINDOWS ? ";" : ":").map(d => d.trim().replace(/^"(.*)"$/, "$1")).filter(Boolean);
2067
+ const override = process.env.RECH_OXMGR;
2068
+ const candidates = [
2069
+ override && (/[\\/]/.test(override) ? resolve(override) : Bun.which(override)),
2070
+ ...dirs.map(d => join(d, `oxmgr${ext}`)),
2071
+ // The vendored binary inside a global npm / bun install of the `oxmgr` package
2072
+ // (npm: <dir>/node_modules on Windows, <prefix>/lib/node_modules beside <prefix>/bin elsewhere).
2073
+ ...dirs.map(d => join(d, "node_modules", "oxmgr", "vendor", `oxmgr${ext}`)),
2074
+ ...(IS_WINDOWS ? [] : dirs.map(d => join(d, "..", "lib", "node_modules", "oxmgr", "vendor", "oxmgr"))),
2075
+ join(HOME, ".bun", "install", "global", "node_modules", "oxmgr", "vendor", `oxmgr${ext}`),
2076
+ ].filter((p): p is string => !!p && existsSync(p));
2077
+ const works = (p: string) => {
2078
+ try {
2079
+ const r = Bun.spawnSync([p, "--version"], { stdout: "pipe", stderr: "ignore", windowsHide: true });
2080
+ if (r.exitCode !== 0) return false;
2081
+ const m = /(\d+\.\d+\.\d+[^\s]*)/.exec(r.stdout?.toString() ?? "");
2082
+ _oxmgrVersion = m ? m[1]! : null;
2083
+ return true;
2084
+ } catch { return false; }
2085
+ };
2086
+ const exe = [...new Set(candidates)].find(works);
2087
+ if (exe) return exe;
2088
+ // Last resort (Windows): a working shim such as npm's oxmgr.cmd, but never over a working
2089
+ // pm2 — cmd.exe re-parses a shim's argv (see runPm).
2090
+ if (!IS_WINDOWS || Bun.which("pm2")) return null;
2091
+ const shim = Bun.which("oxmgr");
2092
+ return shim && works(shim) ? shim : null;
2093
+ }
2094
+
2056
2095
  function daemonManager(): DaemonManager {
2057
2096
  if (_daemonMgr) return _daemonMgr;
2058
- const oxmgrBin = Bun.which("oxmgr");
2097
+ const oxmgrBin = findWorkingOxmgr();
2059
2098
  const pm2Bin = Bun.which("pm2");
2060
2099
  _daemonMgr = pickDaemonManager({
2061
2100
  oxmgrBin,
@@ -2074,6 +2113,10 @@ function daemonManager(): DaemonManager {
2074
2113
  // the managed process (it has no per-var flag like oxmgr's --env), so install
2075
2114
  // passes daemon env this way.
2076
2115
  async function runPm(mgr: DaemonManager, args: string[], env?: Record<string, string>): Promise<number> {
2116
+ // A .cmd/.bat shim runs through cmd.exe, which treats & | < > ^ in an argument as syntax
2117
+ // (e.g. the &s in a RECHROME_URL passed with --env): fail clearly instead of mangling it.
2118
+ if (/\.(cmd|bat)$/i.test(mgr.bin) && args.some(a => /[&|<>^]/.test(a)))
2119
+ throw new Error(`${mgr.bin} is a cmd.exe shim, which would mangle an argument containing & | < > ^. Install the ${mgr.id} executable (or set RECH_OXMGR to it) and try again.`);
2077
2120
  const proc = Bun.spawn([mgr.bin, ...args], {
2078
2121
  stdout: "inherit",
2079
2122
  stderr: "inherit",
@@ -2176,7 +2219,7 @@ async function ensureDaemonManager(ask: (q: string, def?: string) => Promise<str
2176
2219
  });
2177
2220
  const code = await proc.exited;
2178
2221
  if (code !== 0) throw new Error(`\`${command.join(" ")}\` failed (exit code ${code}). Resolve the installation error, then rerun setup.`);
2179
- if (!Bun.which("oxmgr")) {
2222
+ if (!findWorkingOxmgr()) {
2180
2223
  throw new Error("oxmgr was installed but is not on PATH. Add the package manager's global bin directory to PATH, then rerun setup.");
2181
2224
  }
2182
2225
  _daemonMgr = undefined;
@@ -2295,7 +2338,7 @@ async function daemonUninstall(): Promise<void> {
2295
2338
  const mgr = daemonManager();
2296
2339
  for (const name of [PM_PROCESS_NAME, ...LEGACY_PROCESS_NAMES]) await runPm(mgr, ["delete", name]);
2297
2340
  if (mgr.id === "pm2") await runPm(mgr, ["save", "--force"]); // an emptied list must still overwrite the dump
2298
- else await runPm(mgr, ["service", "uninstall"]);
2341
+ // oxmgr's boot/login service is left installed: every other oxmgr-managed process depends on it.
2299
2342
  console.log(`Removed ${mgr.id} process: ${PM_PROCESS_NAME}`);
2300
2343
  }
2301
2344
 
package/rechrome.ts CHANGED
@@ -8,7 +8,7 @@ import { createHash, randomBytes } from "crypto";
8
8
  import { mkdirSync, appendFileSync, existsSync, realpathSync, accessSync, cpSync, unlinkSync, readFileSync, readdirSync, renameSync, rmdirSync, constants as fsConstants } from "fs";
9
9
  import { hostname, homedir, networkInterfaces } from "os";
10
10
  import { isIPv4 } from "net";
11
- import { join, basename, dirname } from "path";
11
+ import { join, basename, dirname, resolve } from "path";
12
12
  import { pathToFileURL } from "url";
13
13
  import { createRequire } from "node:module";
14
14
  import { spawn as cpSpawn } from "child_process";
@@ -2053,9 +2053,48 @@ function oxmgrVersion(bin: string): string | null {
2053
2053
  // RECH_DAEMON_MANAGER override — lives in ./daemon-manager.ts (pure + tested);
2054
2054
  // here we just supply the runtime inputs (what's on PATH, the oxmgr version).
2055
2055
  let _daemonMgr: DaemonManager | undefined;
2056
+ /**
2057
+ * The first oxmgr that actually runs. PATH order alone isn't enough: a stale global shim
2058
+ * (e.g. bun's, printing "oxmgr binary is missing") can shadow a working build, and npm's
2059
+ * `oxmgr.cmd` re-parses argv through cmd.exe, so real executables come first. RECH_OXMGR
2060
+ * names one explicitly (a path, or a command looked up on PATH). The successful probe's
2061
+ * output seeds the version cache, so `--version` runs once.
2062
+ */
2063
+ function findWorkingOxmgr(): string | null {
2064
+ const ext = IS_WINDOWS ? ".exe" : "";
2065
+ // Windows PATH entries may be quoted ("C:\Program Files\x"); quotes break the joined path.
2066
+ const dirs = (process.env.PATH ?? "").split(IS_WINDOWS ? ";" : ":").map(d => d.trim().replace(/^"(.*)"$/, "$1")).filter(Boolean);
2067
+ const override = process.env.RECH_OXMGR;
2068
+ const candidates = [
2069
+ override && (/[\\/]/.test(override) ? resolve(override) : Bun.which(override)),
2070
+ ...dirs.map(d => join(d, `oxmgr${ext}`)),
2071
+ // The vendored binary inside a global npm / bun install of the `oxmgr` package
2072
+ // (npm: <dir>/node_modules on Windows, <prefix>/lib/node_modules beside <prefix>/bin elsewhere).
2073
+ ...dirs.map(d => join(d, "node_modules", "oxmgr", "vendor", `oxmgr${ext}`)),
2074
+ ...(IS_WINDOWS ? [] : dirs.map(d => join(d, "..", "lib", "node_modules", "oxmgr", "vendor", "oxmgr"))),
2075
+ join(HOME, ".bun", "install", "global", "node_modules", "oxmgr", "vendor", `oxmgr${ext}`),
2076
+ ].filter((p): p is string => !!p && existsSync(p));
2077
+ const works = (p: string) => {
2078
+ try {
2079
+ const r = Bun.spawnSync([p, "--version"], { stdout: "pipe", stderr: "ignore", windowsHide: true });
2080
+ if (r.exitCode !== 0) return false;
2081
+ const m = /(\d+\.\d+\.\d+[^\s]*)/.exec(r.stdout?.toString() ?? "");
2082
+ _oxmgrVersion = m ? m[1]! : null;
2083
+ return true;
2084
+ } catch { return false; }
2085
+ };
2086
+ const exe = [...new Set(candidates)].find(works);
2087
+ if (exe) return exe;
2088
+ // Last resort (Windows): a working shim such as npm's oxmgr.cmd, but never over a working
2089
+ // pm2 — cmd.exe re-parses a shim's argv (see runPm).
2090
+ if (!IS_WINDOWS || Bun.which("pm2")) return null;
2091
+ const shim = Bun.which("oxmgr");
2092
+ return shim && works(shim) ? shim : null;
2093
+ }
2094
+
2056
2095
  function daemonManager(): DaemonManager {
2057
2096
  if (_daemonMgr) return _daemonMgr;
2058
- const oxmgrBin = Bun.which("oxmgr");
2097
+ const oxmgrBin = findWorkingOxmgr();
2059
2098
  const pm2Bin = Bun.which("pm2");
2060
2099
  _daemonMgr = pickDaemonManager({
2061
2100
  oxmgrBin,
@@ -2074,6 +2113,10 @@ function daemonManager(): DaemonManager {
2074
2113
  // the managed process (it has no per-var flag like oxmgr's --env), so install
2075
2114
  // passes daemon env this way.
2076
2115
  async function runPm(mgr: DaemonManager, args: string[], env?: Record<string, string>): Promise<number> {
2116
+ // A .cmd/.bat shim runs through cmd.exe, which treats & | < > ^ in an argument as syntax
2117
+ // (e.g. the &s in a RECHROME_URL passed with --env): fail clearly instead of mangling it.
2118
+ if (/\.(cmd|bat)$/i.test(mgr.bin) && args.some(a => /[&|<>^]/.test(a)))
2119
+ throw new Error(`${mgr.bin} is a cmd.exe shim, which would mangle an argument containing & | < > ^. Install the ${mgr.id} executable (or set RECH_OXMGR to it) and try again.`);
2077
2120
  const proc = Bun.spawn([mgr.bin, ...args], {
2078
2121
  stdout: "inherit",
2079
2122
  stderr: "inherit",
@@ -2176,7 +2219,7 @@ async function ensureDaemonManager(ask: (q: string, def?: string) => Promise<str
2176
2219
  });
2177
2220
  const code = await proc.exited;
2178
2221
  if (code !== 0) throw new Error(`\`${command.join(" ")}\` failed (exit code ${code}). Resolve the installation error, then rerun setup.`);
2179
- if (!Bun.which("oxmgr")) {
2222
+ if (!findWorkingOxmgr()) {
2180
2223
  throw new Error("oxmgr was installed but is not on PATH. Add the package manager's global bin directory to PATH, then rerun setup.");
2181
2224
  }
2182
2225
  _daemonMgr = undefined;
@@ -2295,7 +2338,7 @@ async function daemonUninstall(): Promise<void> {
2295
2338
  const mgr = daemonManager();
2296
2339
  for (const name of [PM_PROCESS_NAME, ...LEGACY_PROCESS_NAMES]) await runPm(mgr, ["delete", name]);
2297
2340
  if (mgr.id === "pm2") await runPm(mgr, ["save", "--force"]); // an emptied list must still overwrite the dump
2298
- else await runPm(mgr, ["service", "uninstall"]);
2341
+ // oxmgr's boot/login service is left installed: every other oxmgr-managed process depends on it.
2299
2342
  console.log(`Removed ${mgr.id} process: ${PM_PROCESS_NAME}`);
2300
2343
  }
2301
2344
 
package/serve.js CHANGED
@@ -496,7 +496,7 @@ export async function serve() {
496
496
  if (certPath && keyPath) {
497
497
  const renewed = await renewCertIfNeeded(certPath, keyPath);
498
498
  if (renewed) { log("Restarting to load renewed TLS cert..."); process.exit(0); }
499
- // Check daily; pm2 restarts cleanly after exit(0)
499
+ // Check daily; the process manager (oxmgr --restart always, or pm2) restarts cleanly after exit(0)
500
500
  setInterval(async () => {
501
501
  if (await renewCertIfNeeded(certPath, keyPath)) { log("Restarting to load renewed TLS cert..."); process.exit(0); }
502
502
  }, 86_400_000);
package/serve.ts CHANGED
@@ -496,7 +496,7 @@ export async function serve() {
496
496
  if (certPath && keyPath) {
497
497
  const renewed = await renewCertIfNeeded(certPath, keyPath);
498
498
  if (renewed) { log("Restarting to load renewed TLS cert..."); process.exit(0); }
499
- // Check daily; pm2 restarts cleanly after exit(0)
499
+ // Check daily; the process manager (oxmgr --restart always, or pm2) restarts cleanly after exit(0)
500
500
  setInterval(async () => {
501
501
  if (await renewCertIfNeeded(certPath, keyPath)) { log("Restarting to load renewed TLS cert..."); process.exit(0); }
502
502
  }, 86_400_000);