muuuuse 2.3.4 → 2.3.6

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/README.md CHANGED
@@ -15,16 +15,13 @@ It does one job:
15
15
  The whole surface is:
16
16
 
17
17
  ```bash
18
- muuuuse
19
18
  muuuuse 1
20
19
  muuuuse 1 flow on
21
20
  muuuuse 1 flow off
22
21
  muuuuse 1 flow off continue 5
23
- muuuuse 1 flow on continue 3 5 6
24
22
  muuuuse 2
25
23
  muuuuse 2 flow off
26
24
  muuuuse 2 flow on continue 3
27
- muuuuse 2 flow on continue 3 5 6
28
25
  muuuuse 3
29
26
  muuuuse 3 flow on
30
27
  muuuuse 4
@@ -51,7 +48,7 @@ Now both shells are armed. `muuuuse 1` generates the session key, `muuuuse 2` si
51
48
 
52
49
  `flow on` means that seat relays commentary and final answers. `flow off` means that seat relays and accepts final answers only. Mixed calibration is allowed per seat.
53
50
 
54
- `continue <seat> [<seat> ...]` forwards that seat's relayed output into one or more additional armed seats without changing the signed odd/even pair law. This lets you build local loops like `1 -> 2 -> 3 -> 4 -> 1`, split chains like `2 -> 3` and `2 -> 5`, or wider local meshes while every adjacent pair still keeps its own session keypair.
51
+ `continue <seat>` forwards that seat's relayed output into another armed seat without changing the signed odd/even pair law. This lets you build local loops like `1 -> 2 -> 3 -> 4 -> 1` while every adjacent pair still keeps its own session keypair.
55
52
 
56
53
  If you want Codex in one and Gemini in the other, start them inside the armed shells:
57
54
 
@@ -81,7 +78,7 @@ muuuuse stop
81
78
 
82
79
  - state lives under `~/.muuuuse`
83
80
  - only the signed armed pair can exchange relay events
84
- - `continue <seat> [<seat> ...]` is a separate local forwarding lane and can target any armed seat numbers
81
+ - `continue <seat>` is a separate local forwarding lane and can target any armed seat number
85
82
  - supported relay detection is built for Codex, Claude, and Gemini
86
83
 
87
84
  ## Install
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muuuuse",
3
- "version": "2.3.4",
3
+ "version": "2.3.6",
4
4
  "description": "🔌Muuuuse arms regular terminals in isolated pairs and can continue relay output into any other armed seat.",
5
5
  "type": "commonjs",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -56,10 +56,9 @@ async function main(argv = process.argv.slice(2)) {
56
56
 
57
57
  const seatId = normalizeSeatId(command);
58
58
  if (seatId) {
59
- const { flowMode, continueSeatId, continueSeatIds } = parseSeatOptions(command, argv.slice(1));
59
+ const { flowMode, continueSeatId } = parseSeatOptions(command, argv.slice(1));
60
60
  const seat = new ArmedSeat({
61
61
  cwd: process.cwd(),
62
- continueSeatIds,
63
62
  continueSeatId,
64
63
  flowMode,
65
64
  seatId,
@@ -84,11 +83,8 @@ function renderSeatStatus(seat) {
84
83
  if (seat.partnerLive) {
85
84
  bits.push("peer live");
86
85
  }
87
- const continueSeatIds = Array.isArray(seat.continueSeatIds)
88
- ? seat.continueSeatIds
89
- : (seat.continueSeatId ? [seat.continueSeatId] : []);
90
- if (continueSeatIds.length > 0) {
91
- bits.push(`continue ${continueSeatIds.join(",")}`);
86
+ if (seat.continueSeatId) {
87
+ bits.push(`continue ${seat.continueSeatId}`);
92
88
  }
93
89
  if (seat.trust) {
94
90
  bits.push(`trust ${seat.trust}`);
@@ -109,14 +105,12 @@ function renderSeatStatus(seat) {
109
105
 
110
106
  function parseSeatOptions(command, args) {
111
107
  let flowMode = "off";
112
- const continueSeatIds = [];
113
- let sawContinue = false;
114
- let index = 0;
108
+ let continueSeatId = null;
115
109
 
116
- while (index < args.length) {
110
+ for (let index = 0; index < args.length;) {
117
111
  const token = String(args[index] || "").trim().toLowerCase();
118
112
 
119
- if (token === "flow" && !sawContinue) {
113
+ if (token === "flow") {
120
114
  const flowToken = String(args[index + 1] || "").trim().toLowerCase();
121
115
  if (flowToken === "on" || flowToken === "off") {
122
116
  flowMode = flowToken;
@@ -127,22 +121,10 @@ function parseSeatOptions(command, args) {
127
121
  }
128
122
 
129
123
  if (token === "continue") {
130
- sawContinue = true;
131
- let consumedTargets = 0;
132
- index += 1;
133
-
134
- while (index < args.length) {
135
- const targetSeatId = normalizeSeatId(args[index]);
136
- if (!targetSeatId) {
137
- break;
138
- }
139
-
140
- continueSeatIds.push(targetSeatId);
141
- consumedTargets += 1;
142
- index += 1;
143
- }
144
-
145
- if (consumedTargets > 0) {
124
+ const targetSeatId = normalizeSeatId(args[index + 1]);
125
+ if (targetSeatId) {
126
+ continueSeatId = targetSeatId;
127
+ index += 2;
146
128
  continue;
147
129
  }
148
130
  break;
@@ -151,19 +133,27 @@ function parseSeatOptions(command, args) {
151
133
  break;
152
134
  }
153
135
 
154
- if (index === args.length) {
155
- return {
156
- flowMode,
157
- continueSeatId: continueSeatIds[0] || null,
158
- continueSeatIds,
159
- };
136
+ if (args.length === 0 || (flowMode || continueSeatId !== null) && consumedAllArgs(args, flowMode, continueSeatId)) {
137
+ return { flowMode, continueSeatId };
160
138
  }
161
139
 
162
140
  throw new Error(
163
- `\`muuuuse ${command}\` accepts no extra arguments, \`flow on\` / \`flow off\`, optional \`continue <seat> [<seat> ...]\`, or both in sequence. Run it directly in the terminal you want to arm.`
141
+ `\`muuuuse ${command}\` accepts no extra arguments, \`flow on\` / \`flow off\`, optional \`continue <seat>\`, or both in sequence. Run it directly in the terminal you want to arm.`
164
142
  );
165
143
  }
166
144
 
145
+ function consumedAllArgs(args, flowMode, continueSeatId) {
146
+ const expected = [];
147
+ if (flowMode !== "off" || args.includes("flow")) {
148
+ expected.push("flow", flowMode);
149
+ }
150
+ if (continueSeatId !== null || args.includes("continue")) {
151
+ expected.push("continue", String(continueSeatId));
152
+ }
153
+ return expected.length === args.length &&
154
+ expected.every((value, index) => String(args[index]).trim().toLowerCase() === String(value).trim().toLowerCase());
155
+ }
156
+
167
157
  module.exports = {
168
158
  main,
169
159
  };
package/src/runtime.js CHANGED
@@ -91,36 +91,6 @@ function normalizeContinueSeatId(value) {
91
91
  return seatId || null;
92
92
  }
93
93
 
94
- function normalizeContinueSeatIds(value) {
95
- const values = Array.isArray(value) ? value : [value];
96
- const seen = new Set();
97
- const seatIds = [];
98
-
99
- for (const entry of values) {
100
- const seatId = normalizeContinueSeatId(entry);
101
- if (!seatId || seen.has(seatId)) {
102
- continue;
103
- }
104
-
105
- seen.add(seatId);
106
- seatIds.push(seatId);
107
- }
108
-
109
- return seatIds;
110
- }
111
-
112
- function resolveContinueSeatConfig(continueSeatIds, continueSeatId) {
113
- if (Array.isArray(continueSeatIds) && continueSeatIds.length > 0) {
114
- return continueSeatIds;
115
- }
116
-
117
- if (continueSeatIds !== undefined && continueSeatIds !== null && !Array.isArray(continueSeatIds)) {
118
- return continueSeatIds;
119
- }
120
-
121
- return continueSeatId;
122
- }
123
-
124
94
  function resolveShell() {
125
95
  const shell = String(process.env.SHELL || "").trim();
126
96
  return shell || "/bin/bash";
@@ -629,7 +599,7 @@ async function sendTextAndEnter(child, text, shouldAbort = () => false) {
629
599
  }
630
600
 
631
601
  try {
632
- child.write("\r");
602
+ child.write("\n");
633
603
  } catch {
634
604
  return false;
635
605
  }
@@ -643,12 +613,9 @@ class ArmedSeat {
643
613
  this.partnerSeatId = getPartnerSeatId(options.seatId);
644
614
  this.anchorSeatId = isAnchorSeat(options.seatId) ? options.seatId : this.partnerSeatId;
645
615
  this.flowMode = normalizeFlowMode(options.flowMode);
646
- this.continueSeatIds = normalizeContinueSeatIds(
647
- resolveContinueSeatConfig(options.continueSeatIds, options.continueSeatId)
648
- );
649
- this.continueSeatId = this.continueSeatIds[0] || null;
616
+ this.continueSeatId = normalizeContinueSeatId(options.continueSeatId);
650
617
  this.cwd = normalizeWorkingPath(options.cwd);
651
- if (this.continueSeatIds.includes(this.seatId)) {
618
+ if (this.continueSeatId === this.seatId) {
652
619
  throw new Error(`\`muuuuse ${this.seatId}\` cannot continue to itself.`);
653
620
  }
654
621
  this.sessionName = resolveSessionName(this.cwd, this.seatId);
@@ -724,7 +691,6 @@ class ArmedSeat {
724
691
  partnerSeatId: this.partnerSeatId,
725
692
  sessionName: this.sessionName,
726
693
  flowMode: this.flowMode,
727
- continueSeatIds: this.continueSeatIds,
728
694
  continueSeatId: this.continueSeatId,
729
695
  cwd: this.cwd,
730
696
  pid: process.pid,
@@ -741,7 +707,6 @@ class ArmedSeat {
741
707
  partnerSeatId: this.partnerSeatId,
742
708
  sessionName: this.sessionName,
743
709
  flowMode: this.flowMode,
744
- continueSeatIds: this.continueSeatIds,
745
710
  continueSeatId: this.continueSeatId,
746
711
  cwd: this.cwd,
747
712
  pid: process.pid,
@@ -1088,19 +1053,18 @@ class ArmedSeat {
1088
1053
  return Number.isFinite(requestedAtMs) && requestedAtMs > this.startedAtMs;
1089
1054
  }
1090
1055
 
1091
- findContinuationTarget(targetSeatId) {
1092
- const normalizedTargetSeatId = normalizeContinueSeatId(targetSeatId);
1093
- if (!normalizedTargetSeatId) {
1056
+ findContinuationTarget() {
1057
+ if (!this.continueSeatId) {
1094
1058
  return null;
1095
1059
  }
1096
1060
 
1097
1061
  const candidates = listSessionNames()
1098
1062
  .map((sessionName) => {
1099
- if (!getSeatDirIfExists(sessionName, normalizedTargetSeatId)) {
1063
+ if (!getSeatDirIfExists(sessionName, this.continueSeatId)) {
1100
1064
  return null;
1101
1065
  }
1102
1066
 
1103
- const seat = buildSeatReport(sessionName, normalizedTargetSeatId);
1067
+ const seat = buildSeatReport(sessionName, this.continueSeatId);
1104
1068
  if (!seat || !matchesWorkingPath(seat.cwd, this.cwd)) {
1105
1069
  return null;
1106
1070
  }
@@ -1520,21 +1484,19 @@ class ArmedSeat {
1520
1484
  }
1521
1485
 
1522
1486
  forwardContinuation(signedEntry) {
1523
- if (this.continueSeatIds.length === 0) {
1487
+ if (!this.continueSeatId) {
1524
1488
  return;
1525
1489
  }
1526
1490
 
1527
- for (const continueSeatId of this.continueSeatIds) {
1528
- const target = this.findContinuationTarget(continueSeatId);
1529
- if (!target) {
1530
- this.log(`[${this.seatId}] continue ${continueSeatId} unavailable`);
1531
- continue;
1532
- }
1533
-
1534
- const continuationEntry = buildContinuationEntry(this.sessionName, target.seatId, signedEntry);
1535
- appendJsonl(target.paths.continuePath, continuationEntry);
1536
- this.log(`[${this.seatId} => ${target.seatId}] ${previewText(continuationEntry.text)}`);
1491
+ const target = this.findContinuationTarget();
1492
+ if (!target) {
1493
+ this.log(`[${this.seatId}] continue ${this.continueSeatId} unavailable`);
1494
+ return;
1537
1495
  }
1496
+
1497
+ const continuationEntry = buildContinuationEntry(this.sessionName, target.seatId, signedEntry);
1498
+ appendJsonl(target.paths.continuePath, continuationEntry);
1499
+ this.log(`[${this.seatId} => ${target.seatId}] ${previewText(continuationEntry.text)}`);
1538
1500
  }
1539
1501
 
1540
1502
  async tick() {
@@ -1583,9 +1545,8 @@ class ArmedSeat {
1583
1545
  this.log(`${BRAND} seat ${this.seatId} armed for ${this.sessionName}.`);
1584
1546
  this.log("Use this shell normally. Codex, Claude, and Gemini relay automatically from their local session logs.");
1585
1547
  this.log(`Seat ${this.seatId} relay mode is flow ${this.flowMode}.`);
1586
- if (this.continueSeatIds.length > 0) {
1587
- const targetLabel = this.continueSeatIds.length === 1 ? "seat" : "seats";
1588
- this.log(`Seat ${this.seatId} continues to ${targetLabel} ${this.continueSeatIds.join(", ")}.`);
1548
+ if (this.continueSeatId) {
1549
+ this.log(`Seat ${this.seatId} continues to seat ${this.continueSeatId}.`);
1589
1550
  }
1590
1551
  if (isAnchorSeat(this.seatId)) {
1591
1552
  this.log(`Seat ${this.seatId} generated the session key and is waiting for seat ${this.partnerSeatId} to sign it.`);
@@ -1684,19 +1645,11 @@ function buildSeatReport(sessionName, seatId) {
1684
1645
  return null;
1685
1646
  }
1686
1647
 
1687
- const continueSeatIds = normalizeContinueSeatIds(
1688
- resolveContinueSeatConfig(
1689
- status?.continueSeatIds ?? meta?.continueSeatIds,
1690
- status?.continueSeatId ?? meta?.continueSeatId
1691
- )
1692
- );
1693
-
1694
1648
  return {
1695
1649
  seatId,
1696
1650
  state: wrapperLive ? status?.state || "running" : "orphaned_child",
1697
1651
  flowMode: status?.flowMode || meta?.flowMode || "off",
1698
- continueSeatId: continueSeatIds[0] || null,
1699
- continueSeatIds,
1652
+ continueSeatId: status?.continueSeatId || meta?.continueSeatId || null,
1700
1653
  wrapperPid,
1701
1654
  childPid,
1702
1655
  wrapperLive,
package/src/util.js CHANGED
@@ -284,27 +284,20 @@ function usage() {
284
284
  return [
285
285
  `${BRAND} arms regular terminals in isolated odd/even pairs and relays assistant output between each pair.`,
286
286
  "",
287
- "Command List:",
288
- " muuuuse",
289
- " Show this command list.",
290
- "",
291
- " muuuuse <seat>",
292
- " Arm a seat and keep its normal odd/even partner relay behavior.",
293
- "",
294
- " muuuuse <seat> flow on",
295
- " Relay commentary and final answers into that armed shell.",
296
- "",
297
- " muuuuse <seat> flow off",
298
- " Relay final answers only into that armed shell.",
299
- "",
300
- " muuuuse <seat> flow on continue <seat> [<seat> ...]",
301
- " Fan that seat's relayed output into one or more additional armed seats.",
302
- "",
303
- " muuuuse status",
304
- " Show all armed seats, flow mode, pair trust, and continuation targets.",
305
- "",
287
+ "Usage:",
288
+ " muuuuse 1",
289
+ " muuuuse 1 flow on",
290
+ " muuuuse 1 flow off",
291
+ " muuuuse 1 flow on continue 3",
292
+ " muuuuse 2",
293
+ " muuuuse 2 flow on",
294
+ " muuuuse 2 flow off",
295
+ " muuuuse 2 flow on continue 3",
296
+ " muuuuse 3",
297
+ " muuuuse 4",
298
+ " muuuuse 4 flow on continue 1",
306
299
  " muuuuse stop",
307
- " Stop every armed seat in the current cwd.",
300
+ " muuuuse status",
308
301
  "",
309
302
  "Flow:",
310
303
  " 1. Run `muuuuse 1` in terminal one.",
@@ -312,7 +305,7 @@ function usage() {
312
305
  " 3. The odd seat generates the session key and the matching even seat signs it automatically.",
313
306
  " 4. Additional pairs work the same way: `3/4`, `5/6`, `7/8`...",
314
307
  " 5. Optional: arm each seat with `flow on` or `flow off`.",
315
- " 6. Optional: add `continue <seat> [<seat> ...]` to forward that seat's relayed output into more armed seats.",
308
+ " 6. Optional: add `continue <seat>` to forward that seat's relayed output into another armed seat.",
316
309
  " 7. Use those armed shells normally.",
317
310
  " 8. `flow off` sends final answers only. `flow on` keeps assistant commentary bouncing.",
318
311
  " 9. Run `muuuuse status` or `muuuuse stop` from any shell.",