plum-e2e 2.6.1 → 2.6.2

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.
@@ -88,6 +88,17 @@ async function deleteRunner(id) {
88
88
  }
89
89
  }
90
90
 
91
+ /**
92
+ * Stops/restarts a runner over the network via the primary's control routes,
93
+ * which hit the runner's own /api/shutdown|restart endpoints — works for any
94
+ * reachable runner, not just ones whose process this manager owns by PID.
95
+ */
96
+ async function controlRunner(id, action) {
97
+ const res = await fetch(`${API_URL}/runners/${id}/${action}`, { method: 'POST' });
98
+ const body = await res.json().catch(() => ({}));
99
+ if (!res.ok || body.ok === false) throw new Error(body.error || `HTTP ${res.status}`);
100
+ }
101
+
91
102
  /**
92
103
  * Resolves the display + control state for every runner: reachability (ping),
93
104
  * whether we own a live process for it, and whether we can control it at all.
@@ -156,12 +167,8 @@ function prepareNodeEnv() {
156
167
  async function runAction(r) {
157
168
  const options = [];
158
169
 
159
- if (!r.local) {
160
- clack.log.info(
161
- pc.dim(`"${r.name}" runs on another machine — it can be pinged but not controlled here.`)
162
- );
163
- options.push({ value: 'ping', label: 'Ping' });
164
- } else if (r.managed) {
170
+ if (r.managed) {
171
+ // Local, and this manager owns its process — control it directly by PID.
165
172
  options.push(
166
173
  { value: 'stop', label: pc.red('Stop') },
167
174
  { value: 'restart', label: pc.yellow('Restart') },
@@ -169,14 +176,18 @@ async function runAction(r) {
169
176
  { value: 'ping', label: 'Ping' }
170
177
  );
171
178
  } else if (r.online) {
172
- clack.log.info(
173
- pc.dim(
174
- `"${r.name}" is up but was not started by this manager — stop it from its own terminal.`
175
- )
179
+ // Remote, or local but started outside this manager (no PID to own) —
180
+ // either way the runner's own /api/shutdown|restart endpoints are
181
+ // reachable over the network via the primary's control routes.
182
+ options.push(
183
+ { value: 'stop', label: pc.red('Stop') },
184
+ { value: 'restart', label: pc.yellow('Restart') },
185
+ { value: 'ping', label: 'Ping' }
176
186
  );
177
- options.push({ value: 'ping', label: 'Ping' });
178
- } else {
187
+ } else if (r.local) {
179
188
  options.push({ value: 'start', label: pc.green('Start') }, { value: 'ping', label: 'Ping' });
189
+ } else {
190
+ options.push({ value: 'ping', label: 'Ping' });
180
191
  }
181
192
 
182
193
  options.push(
@@ -194,15 +205,39 @@ async function runAction(r) {
194
205
  const entry = startNode({ id: r.id, port, token: r.token });
195
206
  clack.log.success(pc.green(`Started "${r.name}" on port ${port} (pid ${entry.pid})`));
196
207
  } else if (action === 'stop') {
197
- const ok = stopNode(r.id);
198
- clack.log.success(ok ? pc.green(`Stopped "${r.name}"`) : pc.dim(`"${r.name}" was not running`));
208
+ if (r.managed) {
209
+ const ok = stopNode(r.id);
210
+ clack.log.success(
211
+ ok ? pc.green(`Stopped "${r.name}"`) : pc.dim(`"${r.name}" was not running`)
212
+ );
213
+ } else {
214
+ const s = clack.spinner();
215
+ s.start(`Stopping "${r.name}"...`);
216
+ try {
217
+ await controlRunner(r.id, 'stop');
218
+ s.stop(pc.green(`Stopped "${r.name}"`));
219
+ } catch (e) {
220
+ s.stop(pc.red(`Could not stop "${r.name}": ${e.message}`));
221
+ }
222
+ }
199
223
  } else if (action === 'restart') {
200
- const s = clack.spinner();
201
- s.start(`Restarting "${r.name}"...`);
202
- stopNode(r.id);
203
- await new Promise((resolve) => setTimeout(resolve, 600));
204
- const entry = startNode({ id: r.id, port, token: r.token });
205
- s.stop(pc.green(`Restarted "${r.name}" (pid ${entry.pid})`));
224
+ if (r.managed) {
225
+ const s = clack.spinner();
226
+ s.start(`Restarting "${r.name}"...`);
227
+ stopNode(r.id);
228
+ await new Promise((resolve) => setTimeout(resolve, 600));
229
+ const entry = startNode({ id: r.id, port, token: r.token });
230
+ s.stop(pc.green(`Restarted "${r.name}" (pid ${entry.pid})`));
231
+ } else {
232
+ const s = clack.spinner();
233
+ s.start(`Restarting "${r.name}"...`);
234
+ try {
235
+ await controlRunner(r.id, 'restart');
236
+ s.stop(pc.green(`Restarted "${r.name}"`));
237
+ } catch (e) {
238
+ s.stop(pc.red(`Could not restart "${r.name}": ${e.message}`));
239
+ }
240
+ }
206
241
  } else if (action === 'log') {
207
242
  const entry = runnerProcess.loadRegistry()[r.id];
208
243
  clack.note(entry?.logFile ?? '(no log file)', 'Log file');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plum-e2e",
3
- "version": "2.6.1",
3
+ "version": "2.6.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/silverlunah/plum.git"