muuuuse 1.3.2 → 1.4.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/util.js CHANGED
@@ -2,25 +2,11 @@ const { createHash, randomBytes } = require("node:crypto");
2
2
  const fs = require("node:fs");
3
3
  const os = require("node:os");
4
4
  const path = require("node:path");
5
- const { spawnSync } = require("node:child_process");
6
5
 
7
6
  const BRAND = "🔌Muuuuse";
8
- const POLL_MS = 250;
9
- const SESSION_MATCH_WINDOW_MS = 5 * 60 * 1000;
7
+ const POLL_MS = 220;
10
8
  const MAX_RELAY_CHARS = 4000;
11
9
 
12
- const FLAG_ALIASES = new Map([
13
- ["--max-relays", "maxRelays"],
14
- ["--no-preset", "noPreset"],
15
- ["--session", "session"],
16
- ]);
17
-
18
- const BOOLEAN_FLAGS = new Set(["noPreset"]);
19
-
20
- function shellEscape(value) {
21
- return `'${String(value).replace(/'/g, `'\\''`)}'`;
22
- }
23
-
24
10
  function createId(length = 10) {
25
11
  return randomBytes(Math.ceil(length / 2)).toString("hex").slice(0, length);
26
12
  }
@@ -34,12 +20,6 @@ function ensureDir(dirPath) {
34
20
  return dirPath;
35
21
  }
36
22
 
37
- function resetDir(dirPath) {
38
- fs.rmSync(dirPath, { recursive: true, force: true });
39
- fs.mkdirSync(dirPath, { recursive: true });
40
- return dirPath;
41
- }
42
-
43
23
  function writeJson(filePath, value) {
44
24
  ensureDir(path.dirname(filePath));
45
25
  const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
@@ -50,7 +30,7 @@ function writeJson(filePath, value) {
50
30
  function readJson(filePath, fallback = null) {
51
31
  try {
52
32
  return JSON.parse(fs.readFileSync(filePath, "utf8"));
53
- } catch (error) {
33
+ } catch {
54
34
  return fallback;
55
35
  }
56
36
  }
@@ -65,10 +45,7 @@ function readAppendedText(filePath, previousOffset = 0) {
65
45
  const stats = fs.statSync(filePath);
66
46
  const startOffset = stats.size < previousOffset ? 0 : previousOffset;
67
47
  if (stats.size === startOffset) {
68
- return {
69
- nextOffset: startOffset,
70
- text: "",
71
- };
48
+ return { nextOffset: startOffset, text: "" };
72
49
  }
73
50
 
74
51
  const fd = fs.openSync(filePath, "r");
@@ -85,10 +62,7 @@ function readAppendedText(filePath, previousOffset = 0) {
85
62
  }
86
63
  } catch (error) {
87
64
  if (error && error.code === "ENOENT") {
88
- return {
89
- nextOffset: 0,
90
- text: "",
91
- };
65
+ return { nextOffset: 0, text: "" };
92
66
  }
93
67
  throw error;
94
68
  }
@@ -97,29 +71,11 @@ function readAppendedText(filePath, previousOffset = 0) {
97
71
  function getFileSize(filePath) {
98
72
  try {
99
73
  return fs.statSync(filePath).size;
100
- } catch (error) {
74
+ } catch {
101
75
  return 0;
102
76
  }
103
77
  }
104
78
 
105
- function commandExists(command) {
106
- const result = spawnSync("bash", ["-lc", `command -v ${shellEscape(command)} >/dev/null 2>&1`], {
107
- encoding: "utf8",
108
- });
109
- return result.status === 0;
110
- }
111
-
112
- function readCommandVersion(command, args = ["--version"]) {
113
- const result = spawnSync(command, args, {
114
- encoding: "utf8",
115
- timeout: 4000,
116
- });
117
- if (result.status !== 0) {
118
- return null;
119
- }
120
- return (result.stdout || result.stderr || "").trim().split("\n")[0] || null;
121
- }
122
-
123
79
  function stripAnsi(text) {
124
80
  return String(text || "").replace(
125
81
  // eslint-disable-next-line no-control-regex
@@ -132,6 +88,7 @@ function sanitizeRelayText(input, maxChars = MAX_RELAY_CHARS) {
132
88
  const normalized = stripAnsi(input)
133
89
  .replace(/\r/g, "")
134
90
  .replace(/\u0000/g, "")
91
+ .replace(/\u0007/g, "")
135
92
  .replace(/\n{3,}/g, "\n\n")
136
93
  .trim();
137
94
 
@@ -142,11 +99,6 @@ function sanitizeRelayText(input, maxChars = MAX_RELAY_CHARS) {
142
99
  return `${normalized.slice(0, maxChars - 3).trimEnd()}...`;
143
100
  }
144
101
 
145
- function toInt(value, fallback) {
146
- const parsed = Number.parseInt(String(value), 10);
147
- return Number.isFinite(parsed) ? parsed : fallback;
148
- }
149
-
150
102
  function isPidAlive(pid) {
151
103
  if (!Number.isInteger(pid) || pid <= 0) {
152
104
  return false;
@@ -155,7 +107,7 @@ function isPidAlive(pid) {
155
107
  try {
156
108
  process.kill(pid, 0);
157
109
  return true;
158
- } catch (error) {
110
+ } catch {
159
111
  return false;
160
112
  }
161
113
  }
@@ -179,7 +131,7 @@ function getDefaultSessionName(currentPath = process.cwd()) {
179
131
  const resolvedPath = (() => {
180
132
  try {
181
133
  return fs.realpathSync(currentPath);
182
- } catch (error) {
134
+ } catch {
183
135
  return path.resolve(currentPath);
184
136
  }
185
137
  })();
@@ -202,109 +154,58 @@ function getSeatPaths(sessionName, seatId) {
202
154
  dir,
203
155
  eventsPath: path.join(dir, "events.jsonl"),
204
156
  metaPath: path.join(dir, "meta.json"),
157
+ pipePath: path.join(dir, "pipe.log"),
205
158
  statusPath: path.join(dir, "status.json"),
206
159
  };
207
160
  }
208
161
 
209
- function parseFlags(argv) {
210
- const positionals = [];
211
- const flags = {
212
- maxRelays: Number.POSITIVE_INFINITY,
213
- noPreset: false,
214
- session: null,
215
- };
216
-
217
- for (let index = 0; index < argv.length; index += 1) {
218
- const token = argv[index];
219
- if (!token.startsWith("--")) {
220
- positionals.push(token);
221
- continue;
222
- }
223
-
224
- const [rawFlag, inlineValue] = token.split("=", 2);
225
- const key = FLAG_ALIASES.get(rawFlag);
226
- if (!key) {
227
- throw new Error(`Unknown flag: ${rawFlag}`);
228
- }
229
-
230
- if (BOOLEAN_FLAGS.has(key)) {
231
- flags[key] = true;
232
- continue;
233
- }
234
-
235
- const next = inlineValue !== undefined ? inlineValue : argv[index + 1];
236
- if (inlineValue === undefined) {
237
- index += 1;
238
- }
239
-
240
- if (next === undefined) {
241
- throw new Error(`Missing value for ${rawFlag}`);
242
- }
243
-
244
- flags[key] = next;
162
+ function listSessionNames() {
163
+ const sessionsRoot = path.join(getStateRoot(), "sessions");
164
+ try {
165
+ return fs.readdirSync(sessionsRoot, { withFileTypes: true })
166
+ .filter((entry) => entry.isDirectory())
167
+ .map((entry) => entry.name)
168
+ .sort();
169
+ } catch {
170
+ return [];
245
171
  }
246
-
247
- flags.maxRelays = flags.maxRelays === Number.POSITIVE_INFINITY
248
- ? Number.POSITIVE_INFINITY
249
- : toInt(flags.maxRelays, Number.POSITIVE_INFINITY);
250
-
251
- return {
252
- positionals,
253
- flags,
254
- };
255
172
  }
256
173
 
257
174
  function usage() {
258
175
  return [
259
- `${BRAND} wraps two local programs and bounces final blocks between them.`,
176
+ `${BRAND} arms two raw terminals and bounces BEL-marked final output between them.`,
260
177
  "",
261
178
  "Usage:",
262
- " muuuuse 1 <program...>",
263
- " muuuuse 2 <program...>",
264
- " muuuuse stop",
265
- " muuuuse status",
266
- " muuuuse doctor",
267
- "",
268
- "Examples:",
269
- " muuuuse 1 codex",
270
- " muuuuse 2 gemini",
179
+ " muuuuse 1",
180
+ " muuuuse 2",
271
181
  " muuuuse stop",
272
- " muuuuse 1 bash -lc 'while read line; do printf \"script one: %s\\n\\n\" \"$line\"; done'",
273
182
  "",
274
- "Notes:",
275
- " - Seats auto-pair by current working directory by default.",
276
- " - Use `--session <name>` on seats and stop/status if you want an explicit shared lane.",
277
- " - Known presets (`codex`, `claude`, `gemini`) expand to recommended launch flags.",
278
- " - Any other program runs as-is inside the current terminal under a PTY wrapper.",
183
+ "Flow:",
184
+ " 1. Run `muuuuse 1` in terminal one.",
185
+ " 2. Run `muuuuse 2` in terminal two.",
186
+ " 3. Use those armed shells normally.",
187
+ " 4. When a program rings the terminal bell, that final block relays to the other seat.",
188
+ " 5. Run `muuuuse stop` from any other shell to stop every armed seat.",
279
189
  ].join("\n");
280
190
  }
281
191
 
282
192
  module.exports = {
283
193
  BRAND,
284
- MAX_RELAY_CHARS,
285
194
  POLL_MS,
286
- SESSION_MATCH_WINDOW_MS,
287
195
  appendJsonl,
288
- commandExists,
289
196
  createId,
290
197
  ensureDir,
291
198
  getDefaultSessionName,
292
199
  getFileSize,
293
- getSeatDir,
294
200
  getSeatPaths,
295
- getSessionDir,
296
201
  getStateRoot,
297
202
  hashText,
298
203
  isPidAlive,
299
- parseFlags,
204
+ listSessionNames,
300
205
  readAppendedText,
301
- readCommandVersion,
302
206
  readJson,
303
- resetDir,
304
207
  sanitizeRelayText,
305
- shellEscape,
306
208
  sleep,
307
- toInt,
308
209
  usage,
309
210
  writeJson,
310
211
  };