@holin-work/holin-cli 1.3.6 → 1.3.7

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.
Files changed (2) hide show
  1. package/bin/holin-cli.js +40 -36
  2. package/package.json +1 -1
package/bin/holin-cli.js CHANGED
@@ -234,6 +234,25 @@ function createProxyServer() {
234
234
  return;
235
235
  }
236
236
 
237
+ // Health check endpoint — must be before credential check so waitForPort probes always succeed
238
+ if (req.method === "GET" && req.url === "/health") {
239
+ const creds = readCredentials();
240
+ const body = JSON.stringify({
241
+ status: "ok",
242
+ authenticated: !!(creds?.api_key),
243
+ customer: creds?.customer_name || null,
244
+ plan: creds?.plan || null,
245
+ upstream: UPSTREAM_URL,
246
+ pid: process.pid,
247
+ });
248
+ res.writeHead(200, {
249
+ "Content-Type": "application/json",
250
+ "Content-Length": Buffer.byteLength(body),
251
+ });
252
+ res.end(body);
253
+ return;
254
+ }
255
+
237
256
  // Read credentials on every request (hot-reload support)
238
257
  const creds = readCredentials();
239
258
  if (!creds?.api_key) {
@@ -248,24 +267,6 @@ function createProxyServer() {
248
267
  return;
249
268
  }
250
269
 
251
- // Health check endpoint
252
- if (req.method === "GET" && req.url === "/health") {
253
- const body = JSON.stringify({
254
- status: "ok",
255
- authenticated: !!(creds && creds.api_key),
256
- customer: creds?.customer_name || null,
257
- plan: creds?.plan || null,
258
- upstream: UPSTREAM_URL,
259
- pid: process.pid,
260
- });
261
- res.writeHead(200, {
262
- "Content-Type": "application/json",
263
- "Content-Length": Buffer.byteLength(body),
264
- });
265
- res.end(body);
266
- return;
267
- }
268
-
269
270
  // Build upstream request
270
271
  // MCP streamable-http has a single endpoint; forward directly to upstream path.
271
272
  // Accio requests http://127.0.0.1:18787/mcp → upstream https://api.holin.work/icbu/mcp
@@ -602,26 +603,29 @@ function waitForPort(port, host, timeoutMs) {
602
603
 
603
604
  function startProxyDaemon() {
604
605
  return new Promise((resolve, reject) => {
605
- if (isProxyRunning()) {
606
- // Proxy process exists per pid file also verify port is actually listening
607
- waitForPort(PROXY_PORT, PROXY_HOST, 3000).then(resolve).catch(resolve); // best-effort
608
- return;
609
- }
610
-
611
- ensureConfigDir();
612
- const logFd = openSync(LOG_FILE, "a");
613
-
614
- const child = spawn(process.execPath, [process.argv[1], "proxy", "start", "--daemon"], {
615
- detached: true,
616
- stdio: ["ignore", logFd, logFd],
617
- });
606
+ // Always verify the port is actually reachable first, regardless of pid file.
607
+ // The old proxy may have exited (idle timeout) while the pid file is stale.
608
+ waitForPort(PROXY_PORT, PROXY_HOST, 500)
609
+ .then(() => {
610
+ // Port is already up — existing proxy is healthy, no need to spawn.
611
+ resolve();
612
+ })
613
+ .catch(() => {
614
+ // Port not reachable — spawn a new proxy daemon regardless of pid file state.
615
+ ensureConfigDir();
616
+ const logFd = openSync(LOG_FILE, "a");
617
+
618
+ const child = spawn(process.execPath, [process.argv[1], "proxy", "start", "--daemon"], {
619
+ detached: true,
620
+ stdio: ["ignore", logFd, logFd],
621
+ });
618
622
 
619
- child.unref();
623
+ child.unref();
620
624
 
621
- // Wait until the proxy port is actually accepting connections (up to 10s).
622
- // This replaces the old pid-file poll which only confirmed the process existed,
623
- // not that it was ready to serve — causing ECONNREFUSED races on slow machines.
624
- waitForPort(PROXY_PORT, PROXY_HOST, 10000).then(resolve).catch(reject);
625
+ // Wait until the proxy port is actually accepting connections (up to 10s).
626
+ // Only resolve after a real HTTP response from /health never resolve on catch.
627
+ waitForPort(PROXY_PORT, PROXY_HOST, 10000).then(resolve).catch(reject);
628
+ });
625
629
  });
626
630
  }
627
631
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holin-work/holin-cli",
3
- "version": "1.3.6",
3
+ "version": "1.3.7",
4
4
  "description": "Holin CLI — Accio plugin authorization tool for ICBU batch listing",
5
5
  "type": "module",
6
6
  "bin": {