@threadbase-sh/streamer 1.31.2 → 1.31.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/dist/cli.cjs CHANGED
@@ -147757,7 +147757,81 @@ async function stopService(opts = {}) {
147757
147757
  const { stdout: stdout2, stderr } = await execFileP("schtasks.exe", ["/End", "/TN", label]).catch(
147758
147758
  (err) => ({ stdout: "", stderr: String(err) })
147759
147759
  );
147760
- return { method: "schtasks-end", stdout: stdout2, stderr };
147760
+ const listenerResult = await stopWindowsListener(opts.port ?? 8766);
147761
+ return {
147762
+ method: "schtasks-end",
147763
+ stdout: `${stdout2}
147764
+ ${listenerResult.stdout}`,
147765
+ stderr: `${stderr}
147766
+ ${listenerResult.stderr}`
147767
+ };
147768
+ }
147769
+ async function stopWindowsListener(port) {
147770
+ let netstat;
147771
+ try {
147772
+ netstat = await execFileP("netstat.exe", ["-ano", "-p", "tcp"]);
147773
+ } catch (err) {
147774
+ return { stdout: "", stderr: `Could not inspect port ${port}: ${String(err)}` };
147775
+ }
147776
+ const pids = /* @__PURE__ */ new Set();
147777
+ for (const line of netstat.stdout.split(/\r?\n/)) {
147778
+ const columns = line.trim().split(/\s+/);
147779
+ if (columns.length < 5 || columns[0]?.toUpperCase() !== "TCP") continue;
147780
+ const [localAddress, state, pid] = [columns[1], columns[3], columns[4]];
147781
+ if (state?.toUpperCase() !== "LISTENING" || !pid || pid === "0") continue;
147782
+ if (localAddress?.endsWith(`:${port}`)) pids.add(pid);
147783
+ }
147784
+ const output = [];
147785
+ const errors = [];
147786
+ for (const pid of pids) {
147787
+ try {
147788
+ const result = await execFileP("taskkill.exe", ["/PID", pid, "/T", "/F"]);
147789
+ output.push(result.stdout);
147790
+ if (result.stderr) errors.push(result.stderr);
147791
+ } catch (err) {
147792
+ errors.push(`Could not stop PID ${pid} on port ${port}: ${String(err)}`);
147793
+ }
147794
+ }
147795
+ return { stdout: output.join("\n"), stderr: errors.join("\n") };
147796
+ }
147797
+
147798
+ // src/updater/restart-health.ts
147799
+ function isExpectedVersion(actual, expected) {
147800
+ return actual === expected || actual.startsWith(`${expected}+`);
147801
+ }
147802
+ async function waitForRestartHealth(opts) {
147803
+ const timeoutMs = opts.timeoutMs ?? 15e3;
147804
+ const pollIntervalMs = opts.pollIntervalMs ?? 250;
147805
+ const deadline = Date.now() + timeoutMs;
147806
+ let lastFailure = "server did not respond";
147807
+ while (Date.now() <= deadline) {
147808
+ const controller = new AbortController();
147809
+ const requestTimer = setTimeout(() => controller.abort(), Math.min(2e3, timeoutMs));
147810
+ try {
147811
+ const response = await fetch(`http://127.0.0.1:${opts.port}/healthz`, {
147812
+ signal: controller.signal
147813
+ });
147814
+ if (response.ok) {
147815
+ const body = await response.json();
147816
+ if (body.ok === true && typeof body.version === "string") {
147817
+ if (isExpectedVersion(body.version, opts.expectedVersion)) return;
147818
+ lastFailure = `running version is ${body.version}, expected ${opts.expectedVersion}`;
147819
+ } else {
147820
+ lastFailure = "health response is missing ok/version";
147821
+ }
147822
+ } else {
147823
+ lastFailure = `health endpoint returned ${response.status}`;
147824
+ }
147825
+ } catch (err) {
147826
+ lastFailure = err instanceof Error ? err.message : String(err);
147827
+ } finally {
147828
+ clearTimeout(requestTimer);
147829
+ }
147830
+ const remaining = deadline - Date.now();
147831
+ if (remaining <= 0) break;
147832
+ await new Promise((resolve6) => setTimeout(resolve6, Math.min(pollIntervalMs, remaining)));
147833
+ }
147834
+ throw new Error(`restart verification failed: ${lastFailure}`);
147761
147835
  }
147762
147836
 
147763
147837
  // src/updater/stamp-version.ts
@@ -150899,7 +150973,7 @@ async function runInstall(opts) {
150899
150973
  await unpackTarball({ tarballPath, destDir });
150900
150974
  stampVersionTxt(destDir, targetVersion);
150901
150975
  if (process.platform === "win32") {
150902
- await stopService().catch(() => {
150976
+ await stopService({ port: opts.runningServer?.port }).catch(() => {
150903
150977
  });
150904
150978
  }
150905
150979
  swapCurrent(targetVersion);
@@ -150908,6 +150982,12 @@ async function runInstall(opts) {
150908
150982
  try {
150909
150983
  const r = await restartService();
150910
150984
  restartMethod = r.method;
150985
+ if (opts.runningServer && r.method !== "none") {
150986
+ await waitForRestartHealth({
150987
+ port: opts.runningServer.port,
150988
+ expectedVersion: targetVersion
150989
+ });
150990
+ }
150911
150991
  } catch (err) {
150912
150992
  restartMethod = `failed: ${err instanceof Error ? err.message : String(err)}`;
150913
150993
  }
@@ -150918,8 +150998,9 @@ async function runInstall(opts) {
150918
150998
  pruned,
150919
150999
  restart: { method: restartMethod }
150920
151000
  };
151001
+ const outcome = restartMethod.startsWith("failed:") ? "failed" : "installed";
150921
151002
  appendUpdateLog(
150922
- `[installed] ${installed.previous} \u2192 ${installed.installed} restart=${installed.restart.method}${pruned.length > 0 ? ` pruned=${pruned.length}` : ""}`
151003
+ `[${outcome}] ${installed.previous} \u2192 ${installed.installed} restart=${installed.restart.method}${pruned.length > 0 ? ` pruned=${pruned.length}` : ""}`
150923
151004
  );
150924
151005
  return installed;
150925
151006
  }
@@ -151378,11 +151459,20 @@ program2.command("update").description("Check for streamer updates from GitHub R
151378
151459
  );
151379
151460
  break;
151380
151461
  case "installed":
151381
- log7.info(
151382
- `Installed ${result.installed} (was ${result.previous}). Restart: ${result.restart.method}.`,
151383
- void 0,
151384
- "console"
151385
- );
151462
+ if (result.restart.method.startsWith("failed:")) {
151463
+ log7.error(
151464
+ `Installed ${result.installed} on disk, but the running service was not updated. Restart: ${result.restart.method}.`,
151465
+ void 0,
151466
+ "console"
151467
+ );
151468
+ process.exitCode = 1;
151469
+ } else {
151470
+ log7.info(
151471
+ `Installed ${result.installed} (was ${result.previous}). Restart: ${result.restart.method}.`,
151472
+ void 0,
151473
+ "console"
151474
+ );
151475
+ }
151386
151476
  if (result.pruned.length > 0) {
151387
151477
  log7.info(`Pruned old releases: ${result.pruned.join(", ")}`, void 0, "console");
151388
151478
  }