@gachlab/devup 0.9.0 → 0.9.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 CHANGED
@@ -5,6 +5,14 @@ All notable changes to `@gachlab/devup` are documented here.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.9.1] — 2026-05-22
9
+
10
+ Hotfix for two issues reported moments after 0.9.0 hit:
11
+
12
+ ### Fixed
13
+ - **The y/N prompt no longer no-ops silently.** The conflict list would print, then the process would just continue without waiting for input on some terminals (IDE integrated terminals, multiplexers, custom shells where `process.stdin.isTTY` is misreported). Replaced `readline.question` with direct stdin handling. TTY detection now also accepts stderr / stdout being a TTY when stdin isn't — covers more real environments.
14
+ - **Daemon-already-running guard moved before the port scan.** Running `devup` (TUI) or `devup up -d` while a daemon was already up for the same project caused the scan to list the daemon's own services as conflicts, prompt the user to kill them, restart them (because the daemon's auto-restarter kicked in), then bail with "daemon already running". Now the daemon check runs first and short-circuits cleanly — no churn, single clear error.
15
+
8
16
  ## [0.9.0] — 2026-05-22
9
17
 
10
18
  Pre-boot port-conflict resolution. When devup detects another process already holding a port it needs, it now offers to take it over instead of silently marking the service as crashed.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChG,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAClE,YAAY,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChG,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAClE,YAAY,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC"}
package/dist/index.js CHANGED
@@ -2647,9 +2647,6 @@ async function killAll(conflicts, out) {
2647
2647
  return allOk;
2648
2648
  }
2649
2649
 
2650
- // src/index.ts
2651
- import { createInterface as createInterface6 } from "readline";
2652
-
2653
2650
  // src/platform/detect.ts
2654
2651
  async function detectPlatform() {
2655
2652
  switch (process.platform) {
@@ -4106,6 +4103,19 @@ ${formatValidationWarnings(warnings)}`);
4106
4103
  if (cliArgs.logFile) {
4107
4104
  logSink = new LogSink({ projectName: config.name, rootDir: cliArgs.logDir });
4108
4105
  }
4106
+ if (process.env.DEVUP_DAEMON_CHILD !== "1") {
4107
+ const daemonStatus = isDaemonRunning(config.name);
4108
+ if (daemonStatus.pid && !daemonStatus.stale) {
4109
+ console.error(`\u274C A devup daemon is already running for "${config.name}" (pid=${daemonStatus.pid}).`);
4110
+ console.error("");
4111
+ console.error("Stop it first with `devup down`, or interact via the control plane:");
4112
+ console.error(" devup ctl status");
4113
+ console.error(" devup ctl logs <svc> --follow");
4114
+ console.error(" devup ctl restart <svc>");
4115
+ await logSink?.close();
4116
+ process.exit(1);
4117
+ }
4118
+ }
4109
4119
  if (process.env.DEVUP_DAEMON_CHILD !== "1") {
4110
4120
  const conflicts = await scanPortConflicts(services);
4111
4121
  if (conflicts.length) {
@@ -4153,16 +4163,6 @@ ${formatValidationWarnings(warnings)}`);
4153
4163
  proxyOpts
4154
4164
  }));
4155
4165
  }
4156
- const daemonStatus = isDaemonRunning(config.name);
4157
- if (daemonStatus.pid && !daemonStatus.stale) {
4158
- console.error(`\u274C A devup daemon is already running for "${config.name}" (pid=${daemonStatus.pid}).`);
4159
- console.error("");
4160
- console.error("Stop it first with `devup down`, or interact via the control plane:");
4161
- console.error(" devup ctl status");
4162
- console.error(" devup ctl logs <svc> --follow");
4163
- console.error(" devup ctl restart <svc>");
4164
- process.exit(1);
4165
- }
4166
4166
  const isInteractive = process.stdin.isTTY ?? false;
4167
4167
  const { waitUntilExit } = render(
4168
4168
  React7.createElement(App, {
@@ -4182,15 +4182,29 @@ ${formatValidationWarnings(warnings)}`);
4182
4182
  }
4183
4183
  function askYesNo(question) {
4184
4184
  return new Promise((resolve4) => {
4185
- if (!process.stdin.isTTY) {
4185
+ const isTTY = Boolean(process.stdin.isTTY || process.stderr.isTTY || process.stdout.isTTY);
4186
+ if (!isTTY) {
4186
4187
  resolve4(false);
4187
4188
  return;
4188
4189
  }
4189
- const rl = createInterface6({ input: process.stdin, output: process.stderr });
4190
- rl.question(question, (answer) => {
4191
- rl.close();
4192
- resolve4(/^y(es)?$/i.test(answer.trim()));
4193
- });
4190
+ process.stderr.write(question);
4191
+ process.stdin.resume();
4192
+ process.stdin.setEncoding("utf8");
4193
+ const cleanup = () => {
4194
+ process.stdin.removeListener("data", onData);
4195
+ process.stdin.removeListener("end", onEnd);
4196
+ process.stdin.pause();
4197
+ };
4198
+ const onData = (data) => {
4199
+ cleanup();
4200
+ resolve4(/^y(es)?$/i.test(String(data).trim()));
4201
+ };
4202
+ const onEnd = () => {
4203
+ cleanup();
4204
+ resolve4(false);
4205
+ };
4206
+ process.stdin.once("data", onData);
4207
+ process.stdin.once("end", onEnd);
4194
4208
  });
4195
4209
  }
4196
4210
  main().catch((e) => {