muuuuse 2.3.4 → 3.1.1
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 +2 -5
- package/package.json +1 -1
- package/src/cli.js +104 -49
- package/src/runtime.js +69 -51
- package/src/util.js +15 -24
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
|
|
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
|
|
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
package/src/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { BRAND, normalizeSeatId, usage } = require("./util");
|
|
1
|
+
const { BRAND, getPartnerSeatId, normalizeSeatId, usage } = require("./util");
|
|
2
2
|
const { ArmedSeat, getStatusReport, stopAllSessions } = require("./runtime");
|
|
3
3
|
|
|
4
4
|
async function main(argv = process.argv.slice(2)) {
|
|
@@ -56,10 +56,10 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
56
56
|
|
|
57
57
|
const seatId = normalizeSeatId(command);
|
|
58
58
|
if (seatId) {
|
|
59
|
-
const { flowMode, continueSeatId,
|
|
59
|
+
const { flowMode, continueSeatId, continueTargets } = parseSeatOptions(command, argv.slice(1));
|
|
60
60
|
const seat = new ArmedSeat({
|
|
61
61
|
cwd: process.cwd(),
|
|
62
|
-
|
|
62
|
+
continueTargets,
|
|
63
63
|
continueSeatId,
|
|
64
64
|
flowMode,
|
|
65
65
|
seatId,
|
|
@@ -84,11 +84,9 @@ function renderSeatStatus(seat) {
|
|
|
84
84
|
if (seat.partnerLive) {
|
|
85
85
|
bits.push("peer live");
|
|
86
86
|
}
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (continueSeatIds.length > 0) {
|
|
91
|
-
bits.push(`continue ${continueSeatIds.join(",")}`);
|
|
87
|
+
const renderedLinks = renderLinkTargets(seat);
|
|
88
|
+
if (renderedLinks) {
|
|
89
|
+
bits.push(`link ${renderedLinks}`);
|
|
92
90
|
}
|
|
93
91
|
if (seat.trust) {
|
|
94
92
|
bits.push(`trust ${seat.trust}`);
|
|
@@ -107,63 +105,120 @@ function renderSeatStatus(seat) {
|
|
|
107
105
|
return output;
|
|
108
106
|
}
|
|
109
107
|
|
|
108
|
+
function renderLinkTargets(seat) {
|
|
109
|
+
const targets = [];
|
|
110
|
+
if (seat.partnerSeatId) {
|
|
111
|
+
targets.push({
|
|
112
|
+
targetSeatId: seat.partnerSeatId,
|
|
113
|
+
flowMode: seat.flowMode || "off",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
for (const target of Array.isArray(seat.continueTargets) ? seat.continueTargets : []) {
|
|
117
|
+
targets.push(target);
|
|
118
|
+
}
|
|
119
|
+
if (targets.length === 0) {
|
|
120
|
+
return "";
|
|
121
|
+
}
|
|
122
|
+
return targets.map((target) => `${target.targetSeatId}:${target.flowMode}`).join(", ");
|
|
123
|
+
}
|
|
124
|
+
|
|
110
125
|
function parseSeatOptions(command, args) {
|
|
126
|
+
const seatId = normalizeSeatId(command);
|
|
111
127
|
let flowMode = "off";
|
|
112
|
-
|
|
113
|
-
let
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
flowMode
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
|
|
128
|
+
let continueSeatId = null;
|
|
129
|
+
let continueTargets = [];
|
|
130
|
+
if (args.length === 0) {
|
|
131
|
+
return { flowMode, continueSeatId, continueTargets };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (String(args[0] || "").trim().toLowerCase() === "link") {
|
|
135
|
+
const parsedLinks = parseLinkTargets(args.slice(1), seatId, flowMode);
|
|
136
|
+
if (parsedLinks.consumed === args.length - 1 && parsedLinks.consumed > 0) {
|
|
137
|
+
return {
|
|
138
|
+
flowMode: parsedLinks.flowMode,
|
|
139
|
+
continueSeatId: parsedLinks.continueTargets[0]?.targetSeatId || null,
|
|
140
|
+
continueTargets: parsedLinks.continueTargets,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
let index = 0;
|
|
145
|
+
|
|
146
|
+
const flowToken = String(args[index] || "").trim().toLowerCase();
|
|
147
|
+
const flowModeToken = String(args[index + 1] || "").trim().toLowerCase();
|
|
148
|
+
if (flowToken === "flow" && (flowModeToken === "on" || flowModeToken === "off")) {
|
|
149
|
+
flowMode = flowModeToken;
|
|
150
|
+
index += 2;
|
|
127
151
|
}
|
|
128
152
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
153
|
+
const continueToken = String(args[index] || "").trim().toLowerCase();
|
|
154
|
+
const targetSeatId = normalizeSeatId(args[index + 1]);
|
|
155
|
+
if (continueToken === "continue" && targetSeatId) {
|
|
156
|
+
continueSeatId = targetSeatId;
|
|
157
|
+
continueTargets = [{ targetSeatId, flowMode }];
|
|
158
|
+
index += 2;
|
|
159
|
+
}
|
|
133
160
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
161
|
+
if (index === args.length) {
|
|
162
|
+
return { flowMode, continueSeatId, continueTargets };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
139
165
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
166
|
+
throw new Error(
|
|
167
|
+
`\`muuuuse ${command}\` accepts no extra arguments or \`link <seat> flow on [<seat> flow off ...]\`. Run it directly in the terminal you want to arm.`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
144
170
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
171
|
+
function parseLinkTargets(args, seatId, defaultFlowMode) {
|
|
172
|
+
const partnerSeatId = seatId ? getPartnerSeatId(seatId) : null;
|
|
173
|
+
const continueTargets = [];
|
|
174
|
+
let flowMode = defaultFlowMode;
|
|
175
|
+
let consumed = 0;
|
|
176
|
+
|
|
177
|
+
while (consumed < args.length) {
|
|
178
|
+
const targetSeatId = normalizeSeatId(args[consumed]);
|
|
179
|
+
if (!targetSeatId) {
|
|
148
180
|
break;
|
|
149
181
|
}
|
|
150
182
|
|
|
151
|
-
|
|
183
|
+
const targetFlowMode = parseFlowModeToken(args[consumed + 1], args[consumed + 2]);
|
|
184
|
+
if (!targetFlowMode) {
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (targetSeatId === partnerSeatId) {
|
|
189
|
+
flowMode = targetFlowMode;
|
|
190
|
+
} else {
|
|
191
|
+
upsertTarget(continueTargets, {
|
|
192
|
+
targetSeatId,
|
|
193
|
+
flowMode: targetFlowMode,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
consumed += 3;
|
|
152
198
|
}
|
|
153
199
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
200
|
+
return { consumed, continueTargets, flowMode };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function parseFlowModeToken(flowToken, modeToken) {
|
|
204
|
+
const normalizedFlowToken = String(flowToken || "").trim().toLowerCase();
|
|
205
|
+
const normalizedModeToken = String(modeToken || "").trim().toLowerCase();
|
|
206
|
+
if (normalizedFlowToken === "flow" && (normalizedModeToken === "on" || normalizedModeToken === "off")) {
|
|
207
|
+
return normalizedModeToken;
|
|
160
208
|
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
161
211
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
)
|
|
212
|
+
function upsertTarget(targets, nextTarget) {
|
|
213
|
+
const existingIndex = targets.findIndex((entry) => entry.targetSeatId === nextTarget.targetSeatId);
|
|
214
|
+
if (existingIndex >= 0) {
|
|
215
|
+
targets[existingIndex] = nextTarget;
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
targets.push(nextTarget);
|
|
165
219
|
}
|
|
166
220
|
|
|
167
221
|
module.exports = {
|
|
168
222
|
main,
|
|
223
|
+
parseSeatOptions,
|
|
169
224
|
};
|
package/src/runtime.js
CHANGED
|
@@ -42,6 +42,7 @@ const {
|
|
|
42
42
|
|
|
43
43
|
const TYPE_CHUNK_DELAY_MS = 18;
|
|
44
44
|
const TYPE_CHUNK_SIZE = 24;
|
|
45
|
+
const TYPE_SUBMIT_DELAY_MS = 60;
|
|
45
46
|
const MIRROR_SUPPRESSION_WINDOW_MS = 30 * 1000;
|
|
46
47
|
const PENDING_RELAY_CONTEXT_TTL_MS = 2 * 60 * 1000;
|
|
47
48
|
const EMITTED_ANSWER_TTL_MS = 5 * 60 * 1000;
|
|
@@ -91,34 +92,24 @@ function normalizeContinueSeatId(value) {
|
|
|
91
92
|
return seatId || null;
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
function
|
|
95
|
-
const
|
|
95
|
+
function normalizeContinueTargets(targets, defaultFlowMode = "off") {
|
|
96
|
+
const normalized = [];
|
|
96
97
|
const seen = new Set();
|
|
97
|
-
const seatIds = [];
|
|
98
98
|
|
|
99
|
-
for (const entry of
|
|
100
|
-
const
|
|
101
|
-
if (!
|
|
99
|
+
for (const entry of Array.isArray(targets) ? targets : []) {
|
|
100
|
+
const targetSeatId = normalizeSeatId(entry?.targetSeatId ?? entry?.seatId ?? entry);
|
|
101
|
+
if (!targetSeatId || seen.has(targetSeatId)) {
|
|
102
102
|
continue;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
seen.add(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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;
|
|
105
|
+
seen.add(targetSeatId);
|
|
106
|
+
normalized.push({
|
|
107
|
+
targetSeatId,
|
|
108
|
+
flowMode: normalizeFlowMode(entry?.flowMode ?? entry?.flow ?? defaultFlowMode),
|
|
109
|
+
});
|
|
119
110
|
}
|
|
120
111
|
|
|
121
|
-
return
|
|
112
|
+
return normalized;
|
|
122
113
|
}
|
|
123
114
|
|
|
124
115
|
function resolveShell() {
|
|
@@ -501,6 +492,7 @@ function buildAnswerSignaturePayload(sessionName, challenge, entry) {
|
|
|
501
492
|
seatId: entry.seatId,
|
|
502
493
|
origin: entry.origin,
|
|
503
494
|
phase: entry.phase || "final_answer",
|
|
495
|
+
flowMode: entry.flowMode || "off",
|
|
504
496
|
createdAt: entry.createdAt,
|
|
505
497
|
text: entry.text,
|
|
506
498
|
});
|
|
@@ -520,6 +512,7 @@ function buildContinuationEntry(sourceSessionName, targetSeatId, entry) {
|
|
|
520
512
|
chainId: entry.chainId,
|
|
521
513
|
hop: entry.hop,
|
|
522
514
|
sourceAnswerId: entry.id,
|
|
515
|
+
flowMode: entry.flowMode || null,
|
|
523
516
|
publicKey: entry.publicKey || null,
|
|
524
517
|
signature: entry.signature || null,
|
|
525
518
|
};
|
|
@@ -628,6 +621,14 @@ async function sendTextAndEnter(child, text, shouldAbort = () => false) {
|
|
|
628
621
|
return false;
|
|
629
622
|
}
|
|
630
623
|
|
|
624
|
+
// Some TUIs treat fast, chunked relay typing as paste input and suppress an
|
|
625
|
+
// immediate Enter. A short settle delay keeps submit behavior reliable.
|
|
626
|
+
await sleep(TYPE_SUBMIT_DELAY_MS);
|
|
627
|
+
|
|
628
|
+
if (shouldAbort() || !child) {
|
|
629
|
+
return false;
|
|
630
|
+
}
|
|
631
|
+
|
|
631
632
|
try {
|
|
632
633
|
child.write("\r");
|
|
633
634
|
} catch {
|
|
@@ -643,12 +644,15 @@ class ArmedSeat {
|
|
|
643
644
|
this.partnerSeatId = getPartnerSeatId(options.seatId);
|
|
644
645
|
this.anchorSeatId = isAnchorSeat(options.seatId) ? options.seatId : this.partnerSeatId;
|
|
645
646
|
this.flowMode = normalizeFlowMode(options.flowMode);
|
|
646
|
-
this.
|
|
647
|
-
|
|
647
|
+
this.continueTargets = normalizeContinueTargets(
|
|
648
|
+
options.continueTargets || (
|
|
649
|
+
options.continueSeatId ? [{ targetSeatId: options.continueSeatId, flowMode: options.flowMode }] : []
|
|
650
|
+
),
|
|
651
|
+
this.flowMode
|
|
648
652
|
);
|
|
649
|
-
this.continueSeatId = this.
|
|
653
|
+
this.continueSeatId = this.continueTargets[0]?.targetSeatId || normalizeContinueSeatId(options.continueSeatId);
|
|
650
654
|
this.cwd = normalizeWorkingPath(options.cwd);
|
|
651
|
-
if (this.
|
|
655
|
+
if (this.continueTargets.some((target) => target.targetSeatId === this.seatId)) {
|
|
652
656
|
throw new Error(`\`muuuuse ${this.seatId}\` cannot continue to itself.`);
|
|
653
657
|
}
|
|
654
658
|
this.sessionName = resolveSessionName(this.cwd, this.seatId);
|
|
@@ -724,8 +728,8 @@ class ArmedSeat {
|
|
|
724
728
|
partnerSeatId: this.partnerSeatId,
|
|
725
729
|
sessionName: this.sessionName,
|
|
726
730
|
flowMode: this.flowMode,
|
|
727
|
-
continueSeatIds: this.continueSeatIds,
|
|
728
731
|
continueSeatId: this.continueSeatId,
|
|
732
|
+
continueTargets: this.continueTargets,
|
|
729
733
|
cwd: this.cwd,
|
|
730
734
|
pid: process.pid,
|
|
731
735
|
childPid: this.childPid,
|
|
@@ -741,8 +745,8 @@ class ArmedSeat {
|
|
|
741
745
|
partnerSeatId: this.partnerSeatId,
|
|
742
746
|
sessionName: this.sessionName,
|
|
743
747
|
flowMode: this.flowMode,
|
|
744
|
-
continueSeatIds: this.continueSeatIds,
|
|
745
748
|
continueSeatId: this.continueSeatId,
|
|
749
|
+
continueTargets: this.continueTargets,
|
|
746
750
|
cwd: this.cwd,
|
|
747
751
|
pid: process.pid,
|
|
748
752
|
childPid: this.childPid,
|
|
@@ -1088,8 +1092,12 @@ class ArmedSeat {
|
|
|
1088
1092
|
return Number.isFinite(requestedAtMs) && requestedAtMs > this.startedAtMs;
|
|
1089
1093
|
}
|
|
1090
1094
|
|
|
1095
|
+
shouldCaptureCommentary() {
|
|
1096
|
+
return this.flowMode === "on" || this.continueTargets.some((target) => target.flowMode === "on");
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1091
1099
|
findContinuationTarget(targetSeatId) {
|
|
1092
|
-
const normalizedTargetSeatId =
|
|
1100
|
+
const normalizedTargetSeatId = normalizeSeatId(targetSeatId);
|
|
1093
1101
|
if (!normalizedTargetSeatId) {
|
|
1094
1102
|
return null;
|
|
1095
1103
|
}
|
|
@@ -1141,7 +1149,8 @@ class ArmedSeat {
|
|
|
1141
1149
|
return;
|
|
1142
1150
|
}
|
|
1143
1151
|
|
|
1144
|
-
|
|
1152
|
+
const inboundFlowMode = normalizeFlowMode(entry.flowMode || this.flowMode);
|
|
1153
|
+
if (!shouldAcceptInboundEntry(inboundFlowMode, entry)) {
|
|
1145
1154
|
continue;
|
|
1146
1155
|
}
|
|
1147
1156
|
|
|
@@ -1153,6 +1162,7 @@ class ArmedSeat {
|
|
|
1153
1162
|
seatId: entry.seatId,
|
|
1154
1163
|
origin: entry.origin || "unknown",
|
|
1155
1164
|
phase: getRelayPhase(entry),
|
|
1165
|
+
flowMode: inboundFlowMode,
|
|
1156
1166
|
createdAt: entry.createdAt,
|
|
1157
1167
|
text: payload,
|
|
1158
1168
|
});
|
|
@@ -1208,7 +1218,8 @@ class ArmedSeat {
|
|
|
1208
1218
|
return;
|
|
1209
1219
|
}
|
|
1210
1220
|
|
|
1211
|
-
|
|
1221
|
+
const continueFlowMode = normalizeFlowMode(entry.flowMode || this.flowMode);
|
|
1222
|
+
if (!shouldAcceptInboundEntry(continueFlowMode, entry)) {
|
|
1212
1223
|
continue;
|
|
1213
1224
|
}
|
|
1214
1225
|
|
|
@@ -1420,12 +1431,13 @@ class ArmedSeat {
|
|
|
1420
1431
|
}
|
|
1421
1432
|
|
|
1422
1433
|
const answers = [];
|
|
1434
|
+
const captureCommentary = this.shouldCaptureCommentary();
|
|
1423
1435
|
if (detectedAgent.type === "codex") {
|
|
1424
1436
|
const result = readCodexAnswers(
|
|
1425
1437
|
this.liveState.sessionFile,
|
|
1426
1438
|
this.liveState.offset,
|
|
1427
1439
|
this.liveState.captureSinceMs,
|
|
1428
|
-
{ flowMode:
|
|
1440
|
+
{ flowMode: captureCommentary }
|
|
1429
1441
|
);
|
|
1430
1442
|
this.liveState.offset = result.nextOffset;
|
|
1431
1443
|
answers.push(...result.answers);
|
|
@@ -1434,7 +1446,7 @@ class ArmedSeat {
|
|
|
1434
1446
|
this.liveState.sessionFile,
|
|
1435
1447
|
this.liveState.offset,
|
|
1436
1448
|
this.liveState.captureSinceMs,
|
|
1437
|
-
{ flowMode:
|
|
1449
|
+
{ flowMode: captureCommentary }
|
|
1438
1450
|
);
|
|
1439
1451
|
this.liveState.offset = result.nextOffset;
|
|
1440
1452
|
answers.push(...result.answers);
|
|
@@ -1443,7 +1455,7 @@ class ArmedSeat {
|
|
|
1443
1455
|
this.liveState.sessionFile,
|
|
1444
1456
|
this.liveState.lastMessageId,
|
|
1445
1457
|
this.liveState.captureSinceMs,
|
|
1446
|
-
{ flowMode:
|
|
1458
|
+
{ flowMode: captureCommentary }
|
|
1447
1459
|
);
|
|
1448
1460
|
this.liveState.lastMessageId = result.lastMessageId;
|
|
1449
1461
|
this.liveState.offset = result.fileSize;
|
|
@@ -1501,6 +1513,7 @@ class ArmedSeat {
|
|
|
1501
1513
|
seatId: this.seatId,
|
|
1502
1514
|
origin: entry.origin || "unknown",
|
|
1503
1515
|
phase: entry.phase || "final_answer",
|
|
1516
|
+
flowMode: this.flowMode,
|
|
1504
1517
|
text: payload,
|
|
1505
1518
|
createdAt: entry.createdAt || new Date().toISOString(),
|
|
1506
1519
|
chainId: pendingInboundContext?.chainId || entry.chainId || entryId,
|
|
@@ -1520,20 +1533,23 @@ class ArmedSeat {
|
|
|
1520
1533
|
}
|
|
1521
1534
|
|
|
1522
1535
|
forwardContinuation(signedEntry) {
|
|
1523
|
-
if (this.
|
|
1536
|
+
if (this.continueTargets.length === 0) {
|
|
1524
1537
|
return;
|
|
1525
1538
|
}
|
|
1526
1539
|
|
|
1527
|
-
for (const
|
|
1528
|
-
const target = this.findContinuationTarget(
|
|
1540
|
+
for (const targetEntry of this.continueTargets) {
|
|
1541
|
+
const target = this.findContinuationTarget(targetEntry.targetSeatId);
|
|
1529
1542
|
if (!target) {
|
|
1530
|
-
this.log(`[${this.seatId}] continue ${
|
|
1543
|
+
this.log(`[${this.seatId}] continue ${targetEntry.targetSeatId} unavailable`);
|
|
1531
1544
|
continue;
|
|
1532
1545
|
}
|
|
1533
1546
|
|
|
1534
|
-
const continuationEntry = buildContinuationEntry(this.sessionName, target.seatId,
|
|
1547
|
+
const continuationEntry = buildContinuationEntry(this.sessionName, target.seatId, {
|
|
1548
|
+
...signedEntry,
|
|
1549
|
+
flowMode: targetEntry.flowMode,
|
|
1550
|
+
});
|
|
1535
1551
|
appendJsonl(target.paths.continuePath, continuationEntry);
|
|
1536
|
-
this.log(`[${this.seatId} => ${target.seatId}] ${previewText(continuationEntry.text)}`);
|
|
1552
|
+
this.log(`[${this.seatId} => ${target.seatId} ${targetEntry.flowMode}] ${previewText(continuationEntry.text)}`);
|
|
1537
1553
|
}
|
|
1538
1554
|
}
|
|
1539
1555
|
|
|
@@ -1583,9 +1599,10 @@ class ArmedSeat {
|
|
|
1583
1599
|
this.log(`${BRAND} seat ${this.seatId} armed for ${this.sessionName}.`);
|
|
1584
1600
|
this.log("Use this shell normally. Codex, Claude, and Gemini relay automatically from their local session logs.");
|
|
1585
1601
|
this.log(`Seat ${this.seatId} relay mode is flow ${this.flowMode}.`);
|
|
1586
|
-
if (this.
|
|
1587
|
-
|
|
1588
|
-
|
|
1602
|
+
if (this.continueTargets.length > 0) {
|
|
1603
|
+
this.log(
|
|
1604
|
+
`Seat ${this.seatId} continues to ${this.continueTargets.map((target) => `seat ${target.targetSeatId} (${target.flowMode})`).join(", ")}.`
|
|
1605
|
+
);
|
|
1589
1606
|
}
|
|
1590
1607
|
if (isAnchorSeat(this.seatId)) {
|
|
1591
1608
|
this.log(`Seat ${this.seatId} generated the session key and is waiting for seat ${this.partnerSeatId} to sign it.`);
|
|
@@ -1684,19 +1701,20 @@ function buildSeatReport(sessionName, seatId) {
|
|
|
1684
1701
|
return null;
|
|
1685
1702
|
}
|
|
1686
1703
|
|
|
1687
|
-
const continueSeatIds = normalizeContinueSeatIds(
|
|
1688
|
-
resolveContinueSeatConfig(
|
|
1689
|
-
status?.continueSeatIds ?? meta?.continueSeatIds,
|
|
1690
|
-
status?.continueSeatId ?? meta?.continueSeatId
|
|
1691
|
-
)
|
|
1692
|
-
);
|
|
1693
|
-
|
|
1694
1704
|
return {
|
|
1695
1705
|
seatId,
|
|
1706
|
+
partnerSeatId: status?.partnerSeatId || meta?.partnerSeatId || getPartnerSeatId(seatId),
|
|
1696
1707
|
state: wrapperLive ? status?.state || "running" : "orphaned_child",
|
|
1697
1708
|
flowMode: status?.flowMode || meta?.flowMode || "off",
|
|
1698
|
-
continueSeatId:
|
|
1699
|
-
|
|
1709
|
+
continueSeatId: status?.continueSeatId || meta?.continueSeatId || null,
|
|
1710
|
+
continueTargets: normalizeContinueTargets(
|
|
1711
|
+
status?.continueTargets || meta?.continueTargets || (
|
|
1712
|
+
(status?.continueSeatId || meta?.continueSeatId)
|
|
1713
|
+
? [{ targetSeatId: status?.continueSeatId || meta?.continueSeatId, flowMode: status?.flowMode || meta?.flowMode || "off" }]
|
|
1714
|
+
: []
|
|
1715
|
+
),
|
|
1716
|
+
status?.flowMode || meta?.flowMode || "off"
|
|
1717
|
+
),
|
|
1700
1718
|
wrapperPid,
|
|
1701
1719
|
childPid,
|
|
1702
1720
|
wrapperLive,
|
package/src/util.js
CHANGED
|
@@ -284,36 +284,27 @@ 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
|
-
"
|
|
287
|
+
"Usage:",
|
|
288
288
|
" muuuuse",
|
|
289
|
-
"
|
|
290
|
-
"",
|
|
291
|
-
" muuuuse
|
|
292
|
-
"
|
|
293
|
-
"",
|
|
294
|
-
" muuuuse
|
|
295
|
-
"
|
|
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
|
-
"",
|
|
289
|
+
" muuuuse 1",
|
|
290
|
+
" muuuuse 1 link 2 flow off",
|
|
291
|
+
" muuuuse 1 link 2 flow on",
|
|
292
|
+
" muuuuse 1 link 2 flow on 3 flow off",
|
|
293
|
+
" muuuuse 2",
|
|
294
|
+
" muuuuse 3",
|
|
295
|
+
" muuuuse 4",
|
|
296
|
+
" muuuuse 4 link 3 flow off 1 flow on",
|
|
306
297
|
" muuuuse stop",
|
|
307
|
-
"
|
|
298
|
+
" muuuuse status",
|
|
308
299
|
"",
|
|
309
300
|
"Flow:",
|
|
310
|
-
" 1. Run `muuuuse 1` in terminal one.",
|
|
311
|
-
" 2.
|
|
301
|
+
" 1. Run `muuuuse 1` in terminal one, then `muuuuse 2` in terminal two.",
|
|
302
|
+
" 2. Bare seats default to final-only pair relay.",
|
|
312
303
|
" 3. The odd seat generates the session key and the matching even seat signs it automatically.",
|
|
313
304
|
" 4. Additional pairs work the same way: `3/4`, `5/6`, `7/8`...",
|
|
314
|
-
" 5.
|
|
315
|
-
" 6.
|
|
316
|
-
" 7.
|
|
305
|
+
" 5. Use `link <seat> flow on [<seat> flow off ...]` to set each outbound route.",
|
|
306
|
+
" 6. Include the odd/even partner in `link` to set normal pair flow.",
|
|
307
|
+
" 7. Any extra linked seats receive routed copies with their own flow mode.",
|
|
317
308
|
" 8. `flow off` sends final answers only. `flow on` keeps assistant commentary bouncing.",
|
|
318
309
|
" 9. Run `muuuuse status` or `muuuuse stop` from any shell.",
|
|
319
310
|
"",
|