moshcode 0.90.0 → 0.92.0

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/src/tui.mjs CHANGED
@@ -22,6 +22,7 @@ import { activeChildInput, createMirror, pressKey, setActiveSink, teeOutput } fr
22
22
  import { fetchMotdAd } from "./ads.mjs";
23
23
  import { runScript } from "./runtime.mjs";
24
24
  import { moshVocabulary } from "./commands.mjs";
25
+ import { loadNice, saveNice, describeNice, canCapMemory, parseMemory } from "./nice.mjs";
25
26
  import { mcpCommand, pluginCommand, skillCommand } from "./integrations.mjs";
26
27
  import { stocksCommand } from "./advisor.mjs";
27
28
  import { cryptoCommand } from "./crypto.mjs";
@@ -388,6 +389,63 @@ function isInstalledTool(key) {
388
389
  * line, not an argument list: re-joining tokens would drop the quoting that the
389
390
  * shell still has to read.
390
391
  */
392
+ /**
393
+ * `/nice` — run the CLIs the pit starts at a lower priority than your terminal.
394
+ *
395
+ * A toggle rather than a per-launch flag because the thing being tuned is the
396
+ * box, not the command: you decide once that this machine is shared, and every
397
+ * engine started afterwards honours it. Off by default — a throttle nobody
398
+ * asked for is a slow engine nobody can explain.
399
+ */
400
+ function niceCommand(rest) {
401
+ const [verb, ...args] = rest.filter((a) => a !== "--json");
402
+ const sub = String(verb ?? "").toLowerCase();
403
+ const json = rest.includes("--json");
404
+ const settings = loadNice();
405
+
406
+ if (json) { console.log(JSON.stringify(settings, null, 2)); return; }
407
+
408
+ if (!verb || sub === "status") {
409
+ console.log(info(describeNice(settings)));
410
+ if (!settings.on) console.log(ash(` ${acid("/nice on")} throttles CPU and I/O for every engine the pit starts`));
411
+ else if (!settings.memoryMax) console.log(ash(` ${acid("/nice mem 2G")} adds a memory ceiling — the part nice(1) can't do`));
412
+ return;
413
+ }
414
+
415
+ if (sub === "on" || sub === "off") {
416
+ const saved = saveNice({ ...settings, on: sub === "on" });
417
+ console.log(ok(describeNice(saved)));
418
+ if (saved.on) console.log(ash(" applies to engines started from here on; anything already running keeps its priority"));
419
+ return;
420
+ }
421
+
422
+ if (sub === "cpu" || sub === "io") {
423
+ const n = Number(args[0]);
424
+ if (!Number.isInteger(n)) {
425
+ console.log(err(`usage: /nice ${sub} <number>${sub === "cpu" ? " (-20..19, higher = yields more)" : " (0..7, higher = yields more)"}`));
426
+ return;
427
+ }
428
+ const saved = saveNice({ ...settings, [sub]: n });
429
+ console.log(ok(describeNice(saved)));
430
+ return;
431
+ }
432
+
433
+ if (sub === "mem" || sub === "memory") {
434
+ const parsed = parseMemory(args[0]);
435
+ if (!parsed.ok) { console.log(err(parsed.error)); return; }
436
+ const saved = saveNice({ ...settings, memoryMax: parsed.value });
437
+ console.log(ok(describeNice(saved)));
438
+ // Worth saying plainly: this is the one setting that can silently not apply.
439
+ if (parsed.value && !canCapMemory()) {
440
+ console.log(ash(" no systemd user session on this box, so the ceiling is recorded but not enforced"));
441
+ console.log(ash(` ${acid("loginctl enable-linger $USER")} gives this login one`));
442
+ }
443
+ return;
444
+ }
445
+
446
+ console.log(err(`/nice ${sub} isn't a thing — try on, off, cpu <n>, io <n>, or mem <size>`));
447
+ }
448
+
391
449
  function aliasCommand(rest, line) {
392
450
  const json = rest.includes("--json");
393
451
  // `--json` is the listing's flag wherever it appears, so `/alias --json` is a
@@ -936,6 +994,7 @@ export async function tui() {
936
994
  continue;
937
995
  }
938
996
  if (cmd === "alias" || cmd === "aliases") { aliasCommand(rest, line); continue; }
997
+ if (cmd === "nice" || cmd === "throttle") { niceCommand(rest); continue; }
939
998
  if (cmd === "pwd" || cmd === "where") { printPwd(); continue; }
940
999
  if (cmd === "login") {
941
1000
  const device = rest.includes("--device") || rest.includes("device") || rest.includes("-d");
@@ -1024,6 +1083,20 @@ export async function tui() {
1024
1083
  rl = mkrl();
1025
1084
  continue;
1026
1085
  }
1086
+ // SSH workspaces (PRD 0013). `/ssh dev`, `/ssh exec --tty` and `/ssh
1087
+ // shell` hand the terminal to ssh the way /attach does; every other verb
1088
+ // answers in place and the prompt stays.
1089
+ if (cmd === "ssh") {
1090
+ const { sshCommand, takesTerminal } = await import("./ssh.mjs");
1091
+ if (takesTerminal(rest)) {
1092
+ rl.close();
1093
+ await sshCommand(rest);
1094
+ rl = mkrl();
1095
+ } else {
1096
+ await sshCommand(rest);
1097
+ }
1098
+ continue;
1099
+ }
1027
1100
  if (cmd === "agents" || cmd === "agent" || cmd === "engines") {
1028
1101
  if (!rest[0] || (rest.length === 1 && rest[0] === "--json")) {
1029
1102
  printEngines(rest[0] === "--json");