svamp-cli 0.2.342 → 0.2.356

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 (67) hide show
  1. package/bin/skills/artifact/SKILL.md +19 -5
  2. package/bin/skills/crew/SKILL.md +62 -13
  3. package/dist/DefaultTransport-vfp78fug.mjs +47 -0
  4. package/dist/GeminiTransport-3OkhtFRO.mjs +130 -0
  5. package/dist/KimiTransport-D8n009Tr.mjs +141 -0
  6. package/dist/acpAgentConfig-CObOPlA8.mjs +48 -0
  7. package/dist/acpBackend-UaBD4zL3.mjs +925 -0
  8. package/dist/{adminCommands-CRTBDZan.mjs → adminCommands-BvsYnRDA.mjs} +2 -18
  9. package/dist/{agentCommands-BuO988tJ.mjs → agentCommands-D91RcjqQ.mjs} +6 -23
  10. package/dist/api-BHjyC0Ph.mjs +157 -0
  11. package/dist/{auth--WS0wezs.mjs → auth-CRz8TsEl.mjs} +1 -22
  12. package/dist/bootStatus-DqtNgJch.mjs +143 -0
  13. package/dist/claudeAuth-C0wc1GTM.mjs +291 -0
  14. package/dist/cli.mjs +2948 -5
  15. package/dist/codexAppServerBackend-VGsYtbqg.mjs +786 -0
  16. package/dist/codexProvider-BtxYc5Rj.mjs +68 -0
  17. package/dist/{commands-DDuGBSsz.mjs → commands-B-yczgz_.mjs} +115 -49
  18. package/dist/{commands-DU4B-4Gh.mjs → commands-BMeDrpSv.mjs} +55 -25
  19. package/dist/{commands-DXLOKJOj.mjs → commands-BbmypSgx.mjs} +62 -27
  20. package/dist/{commands-C0HQiu-S.mjs → commands-BkdbelFo.mjs} +78 -38
  21. package/dist/{commands-DXM17M6n.mjs → commands-C13-yE2R.mjs} +42 -19
  22. package/dist/{commands-BKb1g8Gq.mjs → commands-CoOc4qiw.mjs} +20 -30
  23. package/dist/{commands-BsKgZ5sQ.mjs → commands-DW5RnHZw.mjs} +2 -19
  24. package/dist/{commands-Cwxy-C5N.mjs → commands-sPvRcG3J.mjs} +10 -32
  25. package/dist/cron-hHGdb5kU.mjs +132 -0
  26. package/dist/detectIsolation-CrV8IFDv.mjs +339 -0
  27. package/dist/envFlag-COOuJ3AH.mjs +11 -0
  28. package/dist/fileLock-BMPwCrB6.mjs +80 -0
  29. package/dist/{fleet-BHzlAvnu.mjs → fleet-Ca-UjJNL.mjs} +122 -36
  30. package/dist/flushExit-C-i6SGjj.mjs +37 -0
  31. package/dist/friendlyName-1UZcyDhu.mjs +586 -0
  32. package/dist/{headlessCli-C2gvOpOt.mjs → headlessCli-C8HzQvis.mjs} +3 -21
  33. package/dist/hookSettings-FFc1pi8j.mjs +10546 -0
  34. package/dist/{httpServer-Be1Hcddr.mjs → httpServer-BzEDbOGn.mjs} +14 -5
  35. package/dist/hyphaClient-ybivOjZo.mjs +49 -0
  36. package/dist/index.mjs +40 -8
  37. package/dist/instance-CeHz5C8v.mjs +203 -0
  38. package/dist/kimiProvider-DbxODebR.mjs +69 -0
  39. package/dist/kimiSetup-NGsoYB5h.mjs +48 -0
  40. package/dist/loopVerify-DALtTk2V.mjs +444 -0
  41. package/dist/machineTools-DygM1jXW.mjs +580 -0
  42. package/dist/model-CUHojegn.mjs +66 -0
  43. package/dist/{notifyCommands-0aZUwKQK.mjs → notifyCommands-CCUE18Q-.mjs} +3 -18
  44. package/dist/package-BrsiNutS.mjs +64 -0
  45. package/dist/pinnedCodex-XW4qvOAe.mjs +11 -0
  46. package/dist/pinnedNono-5ZHb6328.mjs +15 -0
  47. package/dist/{rpc-rAQQ1q8t.mjs → rpc-CBkSkzsI.mjs} +38 -6
  48. package/dist/{rpc-B2Lc2BAB.mjs → rpc-ccY7xvlo.mjs} +33 -22
  49. package/dist/{run-C7QVfyw4.mjs → run-DCq3aEiF.mjs} +17 -9
  50. package/dist/run-DDmeV9K3.mjs +9826 -0
  51. package/dist/sandboxDetect-Cdzsj5TC.mjs +19 -0
  52. package/dist/{scheduler-CzF4lOe_.mjs → scheduler-BkVJ27Ki.mjs} +38 -6
  53. package/dist/securityContext-Cm5UTCuD.mjs +218 -0
  54. package/dist/serveAuth-4M0vwMyj.mjs +460 -0
  55. package/dist/{serveCommands-EIEqdaVu.mjs → serveCommands-CLhkccJM.mjs} +13 -34
  56. package/dist/sharingNotificationSync-qGIQ-3yf.mjs +359 -0
  57. package/dist/{sideband-BoutiRJi.mjs → sideband-68HhGT0i.mjs} +1 -21
  58. package/dist/store-B4E6NzG6.mjs +356 -0
  59. package/dist/store-fi0f-ff1.mjs +184 -0
  60. package/dist/types-57J3Re0n.mjs +17 -0
  61. package/dist/wantsHelp-6bMOeNUz.mjs +5 -0
  62. package/dist/wrapCommand-CdNawSaa.mjs +117 -0
  63. package/package.json +3 -3
  64. package/dist/cli-B24CmIli.mjs +0 -2780
  65. package/dist/package-CTbGYaJG.mjs +0 -64
  66. package/dist/run-CF4kAVPW.mjs +0 -26508
  67. package/dist/sandboxDetect-DNTcbgWD.mjs +0 -12
@@ -1,26 +1,12 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import os from 'node:os';
4
- import { ah as PINNED_CODEX_VERSION, c as connectToHypha } from './run-CF4kAVPW.mjs';
4
+ import { c as connectToHypha } from './hyphaClient-ybivOjZo.mjs';
5
5
  import { PINNED_CLAUDE_CODE_VERSION } from './pinnedClaudeCode-LCVY4Otm.mjs';
6
- import 'os';
7
- import 'fs/promises';
8
- import 'fs';
9
- import 'path';
10
- import 'url';
11
- import 'child_process';
12
- import 'crypto';
13
- import 'node:crypto';
6
+ import { P as PINNED_CODEX_VERSION } from './pinnedCodex-XW4qvOAe.mjs';
7
+ import { P as PINNED_NONO_VERSION } from './pinnedNono-5ZHb6328.mjs';
14
8
  import 'node:child_process';
15
- import 'util';
16
- import 'http';
17
- import 'net';
18
- import 'node:events';
19
- import '@agentclientprotocol/sdk';
20
9
  import 'node:util';
21
- import 'node:readline';
22
- import 'node:fs/promises';
23
- import 'yaml';
24
10
 
25
11
  const KIMI_NPM_PACKAGE = "@moonshot-ai/kimi-code";
26
12
  const PINNED_KIMI_VERSION = "0.38.0";
@@ -51,6 +37,26 @@ function readDaemonState() {
51
37
  function getSelfMachineId() {
52
38
  return readDaemonState()?.machineId || null;
53
39
  }
40
+ function rowDetail(result) {
41
+ const one = (t) => (t || "").trim().replace(/\s+/g, " ");
42
+ const err = one(result.stderr).slice(-200);
43
+ if (result.exitCode === 124 || /timed out and was killed/i.test(err)) {
44
+ return `TIMED OUT (may have partially completed) \u2014 ${err || "no stderr"}`;
45
+ }
46
+ const last = one((result.stdout || "").trim().split("\n").slice(-1)[0]);
47
+ return last || err || "";
48
+ }
49
+ const INSTALL_TIMEOUT_MS = 10 * 60 * 1e3;
50
+ const SAFE_SKILL_NAME = /^[a-z0-9][a-z0-9._-]*$/i;
51
+ const SAFE_VERSION = /^[0-9][0-9a-zA-Z.+-]*$/;
52
+ function assertSafeFleetArg(value, kind, pattern) {
53
+ if (typeof value !== "string" || !pattern.test(value)) {
54
+ throw new Error(
55
+ `refusing to fan out: ${kind} ${JSON.stringify(value)} is not a plain ${kind} (expected ${pattern.source}). This string would be interpolated into a shell command run on every machine.`
56
+ );
57
+ }
58
+ return value;
59
+ }
54
60
  function partitionSelf(machines, selfId) {
55
61
  if (!selfId) return { targets: machines, skipped: [] };
56
62
  return {
@@ -155,28 +161,44 @@ async function fleetStatus() {
155
161
  return;
156
162
  }
157
163
  try {
158
- printHeader(["MACHINE ID", "LABEL", "CLAUDE", "SVAMP", "STATUS"], [20, 24, 12, 10, 12]);
164
+ printHeader(["MACHINE ID", "LABEL", "CLAUDE", "SVAMP", "NONO", "STATUS"], [20, 24, 12, 10, 14, 12]);
159
165
  const rows = await fanOut(machines, async (m) => {
160
- const [claudeRes, svampRes] = await Promise.all([
166
+ const [claudeRes, svampRes, nonoRes] = await Promise.all([
161
167
  m.rpc.bash("claude --version 2>/dev/null || echo unknown").catch(() => null),
162
- m.rpc.bash("svamp --version 2>/dev/null || echo unknown").catch(() => null)
168
+ m.rpc.bash("svamp --version 2>/dev/null || echo unknown").catch(() => null),
169
+ // Check PATH and the svamp-managed location — installNono writes to the latter,
170
+ // which is not necessarily on the daemon's PATH (see #0-series PATH notes).
171
+ m.rpc.bash("nono --version 2>/dev/null || ~/.svamp/tools/bin/nono --version 2>/dev/null || echo none").catch(() => null)
163
172
  ]);
164
173
  const claude = parseVersionOutput(claudeRes?.stdout);
165
174
  const svamp = parseVersionOutput(svampRes?.stdout);
175
+ const nono = parseVersionOutput(nonoRes?.stdout);
166
176
  const info = await m.rpc.getMachineInfo();
167
- return { claude, svamp, status: info.daemonState?.status || "unknown" };
177
+ return { claude, svamp, nono, status: info.daemonState?.status || "unknown" };
168
178
  });
179
+ let driftingNono = 0;
169
180
  for (const r of rows) {
170
181
  const id = truncate(r.machine.machineId, 18).padEnd(20);
171
182
  const label = truncate(r.machine.label, 22).padEnd(24);
172
183
  const claude = (r.ok ? r.result.claude : "-").padEnd(12);
173
184
  const svamp = (r.ok ? r.result.svamp : "-").padEnd(10);
185
+ const nonoRaw = r.ok ? r.result.nono : "-";
186
+ const nonoDrift = r.ok && nonoRaw !== PINNED_NONO_VERSION;
187
+ const nono = (nonoDrift ? `${nonoRaw} !` : nonoRaw).padEnd(14);
174
188
  const status = r.ok ? r.result.status : r.error || "fail";
175
189
  const color = r.ok && r.result.status === "running" ? "\x1B[32m" : "\x1B[31m";
176
- console.log(`${id} ${label} ${claude} ${svamp} ${color}${status}\x1B[0m`);
190
+ console.log(`${id} ${label} ${claude} ${svamp} ${nono}${color}${status}\x1B[0m`);
191
+ if (nonoDrift) driftingNono += 1;
177
192
  }
178
193
  console.log(`
179
194
  ${machines.length} machine(s).`);
195
+ if (driftingNono > 0) {
196
+ console.log(
197
+ `
198
+ \x1B[33m! ${driftingNono} machine(s) are not on the pinned nono ${PINNED_NONO_VERSION}\x1B[0m \u2014 nono IS the sandbox boundary, and installNono() only runs when the binary is ABSENT, so an
199
+ existing machine never picks up the pin. Reconcile deliberately with: svamp fleet upgrade-nono`
200
+ );
201
+ }
180
202
  } finally {
181
203
  await server.disconnect();
182
204
  }
@@ -232,8 +254,9 @@ async function fleetUpgradeClaude(opts) {
232
254
  `);
233
255
  try {
234
256
  printHeader(["MACHINE ID", "LABEL", "STATE", "DETAIL"], [20, 24, 6, 60]);
257
+ assertSafeFleetArg(version, "version", SAFE_VERSION);
235
258
  const cmd = `claude install ${version} 2>&1 || claude update 2>&1`;
236
- const rows = await fanOut(machines, async (m) => await m.rpc.bash(cmd, void 0));
259
+ const rows = await fanOut(machines, async (m) => await m.rpc.bash(cmd, void 0, INSTALL_TIMEOUT_MS));
237
260
  let failures = 0;
238
261
  for (const r of rows) {
239
262
  if (!r.ok) {
@@ -242,7 +265,7 @@ async function fleetUpgradeClaude(opts) {
242
265
  continue;
243
266
  }
244
267
  const ok = r.result.exitCode === 0;
245
- const detail = (r.result.stdout?.trim().split("\n").slice(-1)[0] || r.result.stderr?.trim().slice(-200) || "").replace(/\s+/g, " ");
268
+ const detail = rowDetail(r.result);
246
269
  printResultRow(r.machine, ok ? "OK" : "FAIL", detail);
247
270
  if (!ok) failures += 1;
248
271
  }
@@ -269,8 +292,9 @@ async function fleetUpgradeCodex(opts) {
269
292
  `);
270
293
  try {
271
294
  printHeader(["MACHINE ID", "LABEL", "STATE", "DETAIL"], [20, 24, 6, 60]);
295
+ assertSafeFleetArg(version, "version", SAFE_VERSION);
272
296
  const cmd = `bash -lc '${NPM_PREFIX_RESOLVER}; npm install -g $PREFIX_ARG @openai/codex@${version} 2>&1 | tail -5'`;
273
- const rows = await fanOut(machines, async (m) => await m.rpc.bash(cmd, void 0));
297
+ const rows = await fanOut(machines, async (m) => await m.rpc.bash(cmd, void 0, INSTALL_TIMEOUT_MS));
274
298
  let failures = 0;
275
299
  for (const r of rows) {
276
300
  if (!r.ok) {
@@ -279,7 +303,7 @@ async function fleetUpgradeCodex(opts) {
279
303
  continue;
280
304
  }
281
305
  const ok = r.result.exitCode === 0;
282
- const detail = (r.result.stdout?.trim().split("\n").slice(-1)[0] || r.result.stderr?.trim().slice(-200) || "").replace(/\s+/g, " ");
306
+ const detail = rowDetail(r.result);
283
307
  printResultRow(r.machine, ok ? "OK" : "FAIL", detail);
284
308
  if (!ok) failures += 1;
285
309
  }
@@ -290,6 +314,53 @@ ${rows.length - failures}/${rows.length} machines on codex ${version}.`);
290
314
  await server.disconnect();
291
315
  }
292
316
  }
317
+ async function fleetUpgradeNono(opts) {
318
+ if (opts?.version && opts.version !== PINNED_NONO_VERSION) {
319
+ console.error(`Refusing to install nono ${opts.version}: this command reconciles the PIN, and the pin is ${PINNED_NONO_VERSION}.`);
320
+ console.error("");
321
+ console.error("nono is the sandbox boundary, so the version that reaches the fleet must be the one");
322
+ console.error("reviewed in a commit \u2014 not one typed at the prompt. To roll a different nono:");
323
+ console.error(` 1. bump PINNED_NONO_VERSION to ${opts.version} in packages/svamp-cli/src/daemon/pinnedNono.ts`);
324
+ console.error(" (this also updates the checksum the installer verifies against)");
325
+ console.error(" 2. land + publish it, then re-run `svamp fleet upgrade-nono` with no -v");
326
+ process.exitCode = 1;
327
+ return;
328
+ }
329
+ const version = PINNED_NONO_VERSION;
330
+ const { server, machines } = await discoverMachines();
331
+ if (machines.length === 0) {
332
+ console.log("No machines found.");
333
+ await server.disconnect();
334
+ return;
335
+ }
336
+ console.log(`Reconciling nono to ${version} on ${machines.length} machine(s)...
337
+ `);
338
+ console.log("nono IS the sandbox boundary \u2014 roll it deliberately, and canary one machine first.");
339
+ console.log("No daemon restart needed: the new binary applies to sessions started after this.\n");
340
+ try {
341
+ printHeader(["MACHINE ID", "LABEL", "STATE", "DETAIL"], [20, 24, 6, 60]);
342
+ const cmd = `svamp daemon install-nono 2>&1 | tail -5`;
343
+ const rows = await fanOut(machines, async (m) => await m.rpc.bash(cmd, void 0, INSTALL_TIMEOUT_MS));
344
+ let failures = 0;
345
+ for (const r of rows) {
346
+ if (!r.ok) {
347
+ printResultRow(r.machine, "FAIL", r.error || "");
348
+ failures += 1;
349
+ continue;
350
+ }
351
+ const ok = r.result.exitCode === 0;
352
+ const detail = rowDetail(r.result);
353
+ printResultRow(r.machine, ok ? "OK" : "FAIL", detail);
354
+ if (!ok) failures += 1;
355
+ }
356
+ console.log(`
357
+ ${rows.length - failures}/${rows.length} machines reconciled to nono ${version}.`);
358
+ console.log("Confirm with: svamp fleet status (the NONO column flags anything off the pin)");
359
+ if (failures > 0) process.exitCode = 1;
360
+ } finally {
361
+ await server.disconnect();
362
+ }
363
+ }
293
364
  async function fleetUpgradeKimi(opts) {
294
365
  const version = opts?.version || PINNED_KIMI_VERSION;
295
366
  const { server, machines } = await discoverMachines();
@@ -298,6 +369,7 @@ async function fleetUpgradeKimi(opts) {
298
369
  await server.disconnect();
299
370
  return;
300
371
  }
372
+ assertSafeFleetArg(version, "version", SAFE_VERSION);
301
373
  console.log(`Reconciling Kimi to ${version} on ${machines.length} machine(s)...
302
374
  `);
303
375
  console.log(`Note: Kimi is a global npm package (${KIMI_NPM_PACKAGE}); machines that never use it can ignore this.`);
@@ -306,7 +378,7 @@ async function fleetUpgradeKimi(opts) {
306
378
  try {
307
379
  printHeader(["MACHINE ID", "LABEL", "STATE", "DETAIL"], [20, 24, 6, 60]);
308
380
  const cmd = `bash -lc '${NPM_PREFIX_RESOLVER}; npm install -g $PREFIX_ARG ${KIMI_NPM_PACKAGE}@${version} 2>&1 | tail -5'`;
309
- const rows = await fanOut(machines, async (m) => await m.rpc.bash(cmd, void 0));
381
+ const rows = await fanOut(machines, async (m) => await m.rpc.bash(cmd, void 0, INSTALL_TIMEOUT_MS));
310
382
  let failures = 0;
311
383
  for (const r of rows) {
312
384
  if (!r.ok) {
@@ -315,7 +387,7 @@ async function fleetUpgradeKimi(opts) {
315
387
  continue;
316
388
  }
317
389
  const ok = r.result.exitCode === 0;
318
- const detail = (r.result.stdout?.trim().split("\n").slice(-1)[0] || r.result.stderr?.trim().slice(-200) || "").replace(/\s+/g, " ");
390
+ const detail = rowDetail(r.result);
319
391
  printResultRow(r.machine, ok ? "OK" : "FAIL", detail);
320
392
  if (!ok) failures += 1;
321
393
  }
@@ -346,8 +418,9 @@ async function fleetUpgradeSvamp(opts) {
346
418
  try {
347
419
  printHeader(["MACHINE ID", "LABEL", "STATE", "DETAIL"], [20, 24, 6, 60]);
348
420
  for (const m of skipped) printResultRow(m, "SKIP", 'self \u2014 restart separately with "svamp daemon restart"');
421
+ assertSafeFleetArg(version, "version", SAFE_VERSION);
349
422
  const cmd = `bash -lc '${NPM_PREFIX_RESOLVER}; npm install -g $PREFIX_ARG svamp-cli@${version} 2>&1 | tail -5 && ${restartCmd} 2>&1 | tail -5'`;
350
- const rows = await fanOut(targets, async (m) => await m.rpc.bash(cmd, void 0));
423
+ const rows = await fanOut(targets, async (m) => await m.rpc.bash(cmd, void 0, INSTALL_TIMEOUT_MS));
351
424
  let failures = 0;
352
425
  for (const r of rows) {
353
426
  if (!r.ok) {
@@ -356,7 +429,7 @@ async function fleetUpgradeSvamp(opts) {
356
429
  continue;
357
430
  }
358
431
  const ok = r.result.exitCode === 0;
359
- const detail = (r.result.stdout?.trim().split("\n").slice(-1)[0] || r.result.stderr?.trim().slice(-200) || "").replace(/\s+/g, " ");
432
+ const detail = rowDetail(r.result);
360
433
  printResultRow(r.machine, ok ? "OK" : "FAIL", detail);
361
434
  if (!ok) failures += 1;
362
435
  }
@@ -374,6 +447,15 @@ ${rows.length - failures}/${rows.length} machines updated (new binary installed)
374
447
  await server.disconnect();
375
448
  }
376
449
  }
450
+ function fleetDaemonRestartCommand(opts) {
451
+ if (opts?.graceful === false) {
452
+ return `svamp daemon stop --cleanup 2>&1 && svamp daemon start 2>&1`;
453
+ }
454
+ if (opts?.force) {
455
+ return `svamp daemon restart 2>&1`;
456
+ }
457
+ return `svamp daemon restart --when-idle 2>&1`;
458
+ }
377
459
  async function fleetDaemonRestart(opts) {
378
460
  const { server, machines } = await discoverMachines();
379
461
  if (machines.length === 0) {
@@ -382,9 +464,12 @@ async function fleetDaemonRestart(opts) {
382
464
  return;
383
465
  }
384
466
  try {
467
+ const cmd = fleetDaemonRestartCommand(opts);
468
+ const mode = opts?.graceful === false ? "cleanup (hard stop+start)" : opts?.force ? "force (immediate)" : "graceful (restart when idle)";
469
+ console.log(`Restarting daemons on ${machines.length} machine(s) \u2014 ${mode}...
470
+ `);
385
471
  printHeader(["MACHINE ID", "LABEL", "STATE", "DETAIL"], [20, 24, 6, 60]);
386
- const cmd = opts?.graceful === false ? `svamp daemon stop --cleanup 2>&1 && svamp daemon start 2>&1` : `svamp daemon restart 2>&1`;
387
- const rows = await fanOut(machines, async (m) => await m.rpc.bash(cmd, void 0));
472
+ const rows = await fanOut(machines, async (m) => await m.rpc.bash(cmd, void 0, INSTALL_TIMEOUT_MS));
388
473
  let failures = 0;
389
474
  for (const r of rows) {
390
475
  if (!r.ok) {
@@ -393,7 +478,7 @@ async function fleetDaemonRestart(opts) {
393
478
  continue;
394
479
  }
395
480
  const ok = r.result.exitCode === 0;
396
- const detail = (r.result.stdout?.trim().split("\n").slice(-1)[0] || r.result.stderr?.trim().slice(-200) || "").replace(/\s+/g, " ");
481
+ const detail = rowDetail(r.result);
397
482
  printResultRow(r.machine, ok ? "OK" : "FAIL", detail);
398
483
  if (!ok) failures += 1;
399
484
  }
@@ -419,8 +504,9 @@ async function fleetPushSkill(name) {
419
504
  `);
420
505
  try {
421
506
  printHeader(["MACHINE ID", "LABEL", "STATE", "DETAIL"], [20, 24, 6, 60]);
507
+ assertSafeFleetArg(name, "skill name", SAFE_SKILL_NAME);
422
508
  const cmd = `svamp skills install ${name} --force 2>&1`;
423
- const rows = await fanOut(machines, async (m) => await m.rpc.bash(cmd, void 0));
509
+ const rows = await fanOut(machines, async (m) => await m.rpc.bash(cmd, void 0, INSTALL_TIMEOUT_MS));
424
510
  let failures = 0;
425
511
  for (const r of rows) {
426
512
  if (!r.ok) {
@@ -429,7 +515,7 @@ async function fleetPushSkill(name) {
429
515
  continue;
430
516
  }
431
517
  const ok = r.result.exitCode === 0;
432
- const detail = (r.result.stdout?.trim().split("\n").slice(-1)[0] || r.result.stderr?.trim().slice(-200) || "").replace(/\s+/g, " ");
518
+ const detail = rowDetail(r.result);
433
519
  printResultRow(r.machine, ok ? "OK" : "FAIL", detail);
434
520
  if (!ok) failures += 1;
435
521
  }
@@ -441,4 +527,4 @@ ${rows.length - failures}/${rows.length} updated.`);
441
527
  }
442
528
  }
443
529
 
444
- export { fleetDaemonRestart, fleetExec, fleetPushSkill, fleetStatus, fleetUpgradeClaude, fleetUpgradeCodex, fleetUpgradeKimi, fleetUpgradeSvamp, parseVersionOutput, partitionSelf };
530
+ export { assertSafeFleetArg, fleetDaemonRestart, fleetDaemonRestartCommand, fleetExec, fleetPushSkill, fleetStatus, fleetUpgradeClaude, fleetUpgradeCodex, fleetUpgradeKimi, fleetUpgradeNono, fleetUpgradeSvamp, parseVersionOutput, partitionSelf, rowDetail };
@@ -0,0 +1,37 @@
1
+ function flushAndExit(code) {
2
+ return new Promise((resolve) => {
3
+ const streams = [process.stdout, process.stderr];
4
+ let remaining = streams.length;
5
+ const tryExit = () => {
6
+ if (--remaining <= 0) {
7
+ process.exit(code);
8
+ resolve(void 0);
9
+ }
10
+ };
11
+ for (const s of streams) {
12
+ if (s.writableLength) {
13
+ s.write("", () => tryExit());
14
+ } else {
15
+ tryExit();
16
+ }
17
+ }
18
+ });
19
+ }
20
+ function flushStdout() {
21
+ return new Promise((resolve) => {
22
+ const streams = [process.stdout, process.stderr];
23
+ let remaining = streams.length;
24
+ const done = () => {
25
+ if (--remaining <= 0) resolve();
26
+ };
27
+ for (const s of streams) {
28
+ if (s.writableLength) {
29
+ s.write("", () => done());
30
+ } else {
31
+ done();
32
+ }
33
+ }
34
+ });
35
+ }
36
+
37
+ export { flushAndExit as a, flushStdout as f };