@suveren/gateway 0.6.0 → 0.6.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.
@@ -69,13 +69,22 @@ async function start(args) {
69
69
  if (await isPortListening(SUVEREN_PORT)) {
70
70
  console.error(`Port ${SUVEREN_PORT} is already in use.`);
71
71
  console.error(``);
72
- console.error(`Something is already serving there — most likely a gateway started by the`);
73
- console.error(`login service or from another terminal, which leaves no PID file for this`);
74
- console.error(`CLI to find.`);
75
- console.error(``);
76
- console.error(` Check what it is: suveren-gateway status`);
77
- console.error(` Stop a login service: suveren-gateway service uninstall`);
78
- console.error(` Or use another port: SUVEREN_CP_PORT=3410 suveren-gateway start`);
72
+ if (serviceRunning()) {
73
+ // Do not suggest uninstalling autostart: that removes a feature the user
74
+ // asked for in order to solve a problem that needs a restart. After an
75
+ // upgrade the files on disk are new and the running process is not.
76
+ console.error(`The login service is already running the gateway, so there is nothing to start.`);
77
+ console.error(``);
78
+ console.error(` Pick up an update: suveren-gateway restart`);
79
+ console.error(` Check what is running: suveren-gateway status`);
80
+ console.error(` Remove autostart: suveren-gateway service uninstall`);
81
+ } else {
82
+ console.error(`Something is already serving there — most likely a gateway started from`);
83
+ console.error(`another terminal, which leaves no PID file for this CLI to find.`);
84
+ console.error(``);
85
+ console.error(` Check what it is: suveren-gateway status`);
86
+ console.error(` Or use another port: SUVEREN_CP_PORT=3410 suveren-gateway start`);
87
+ }
79
88
  process.exit(1);
80
89
  }
81
90
 
@@ -134,6 +143,16 @@ async function start(args) {
134
143
 
135
144
  async function stop() {
136
145
  const pid = readPid();
146
+ // A service-managed gateway writes no PID file. Reporting "not running" while
147
+ // it serves requests is the most misleading thing this CLI can say, and it is
148
+ // what sent an upgrade into a port conflict it could not explain.
149
+ if (!pid && serviceRunning()) {
150
+ console.error('suveren-gateway is running under the login service, which this command does not manage.');
151
+ console.error('');
152
+ console.error(' To pick up an update: suveren-gateway restart');
153
+ console.error(' To stop it for good: suveren-gateway service uninstall');
154
+ process.exit(1);
155
+ }
137
156
  if (!pid) {
138
157
  console.error('suveren-gateway is not running (no PID file).');
139
158
  process.exit(1);
@@ -242,6 +261,14 @@ async function status() {
242
261
  }
243
262
 
244
263
  async function restart() {
264
+ // The service manager owns the process when autostart is installed, and it
265
+ // leaves no PID file — so this must be asked FIRST or the checks below all
266
+ // conclude, wrongly, that nothing is running.
267
+ if (serviceRestart()) {
268
+ console.log('suveren-gateway: restarted via the login service.');
269
+ console.log(` The vault is locked after a restart — unlock at http://localhost:${SUVEREN_PORT}`);
270
+ return;
271
+ }
245
272
  if (readPid() && isPidAlive(readPid())) {
246
273
  await stop();
247
274
  }
@@ -279,6 +306,52 @@ async function logs(args) {
279
306
 
280
307
  const LAUNCH_AGENT_LABEL = 'ai.suveren.gateway';
281
308
 
309
+ /**
310
+ * Is the login service currently running the gateway?
311
+ *
312
+ * `stop`, `start` and `restart` were all written before autostart existed and
313
+ * all reason from the PID file — which a service-managed gateway never writes.
314
+ * The result was that installing autostart silently broke the update path:
315
+ * `stop` reported "not running", and `start` then hit a port held by a process
316
+ * it could not see. Every command has to know about the service the same CLI
317
+ * installs.
318
+ */
319
+ function serviceRunning() {
320
+ const os = platform();
321
+ if (os === 'darwin') {
322
+ const p = runQuiet('launchctl', ['print', `gui/${process.getuid()}/${LAUNCH_AGENT_LABEL}`]);
323
+ return p.status === 0 && /state = running/.test(p.stdout || '');
324
+ }
325
+ if (os === 'win32') {
326
+ const p = runQuiet('schtasks', ['/Query', '/TN', WIN_TASK_NAME, '/FO', 'LIST']);
327
+ return p.status === 0 && /Status:\s+Running/i.test(p.stdout || '');
328
+ }
329
+ const p = runQuiet('systemctl', ['--user', 'is-active', SYSTEMD_UNIT]);
330
+ return (p.stdout || '').trim() === 'active';
331
+ }
332
+
333
+ /**
334
+ * Restart through whichever service manager owns the process.
335
+ *
336
+ * Returns false when no service is running, so callers fall back to the manual
337
+ * path. Restarting is what an upgrade needs: npm replaces the files on disk,
338
+ * but the running process loaded them once at start and keeps serving the old
339
+ * code until it is replaced.
340
+ */
341
+ function serviceRestart() {
342
+ if (!serviceRunning()) return false;
343
+ const os = platform();
344
+ if (os === 'darwin') {
345
+ runQuiet('launchctl', ['kickstart', '-k', `gui/${process.getuid()}/${LAUNCH_AGENT_LABEL}`]);
346
+ } else if (os === 'win32') {
347
+ runQuiet('schtasks', ['/End', '/TN', WIN_TASK_NAME]);
348
+ runQuiet('schtasks', ['/Run', '/TN', WIN_TASK_NAME]);
349
+ } else {
350
+ runQuiet('systemctl', ['--user', 'restart', SYSTEMD_UNIT]);
351
+ }
352
+ return true;
353
+ }
354
+
282
355
  async function service(args) {
283
356
  const sub = args[0] ?? 'help';
284
357
  const os = platform();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suveren/gateway",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Suveren gateway — local agent gateway built in compliance with the Human Agency Protocol (HAP). Runs the UI, control plane, and MCP server in one Node process.",
5
5
  "type": "module",
6
6
  "main": "server.js",