muuuuse 2.3.4 → 2.3.5
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 +128 -31
- package/src/runtime.js +60 -51
- package/src/util.js +13 -22
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,16 +105,34 @@ 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
|
+
|
|
120
|
+
return targets
|
|
121
|
+
.map((target) => `${target.targetSeatId}:${target.flowMode}`)
|
|
122
|
+
.join(", ");
|
|
123
|
+
}
|
|
124
|
+
|
|
110
125
|
function parseSeatOptions(command, args) {
|
|
126
|
+
const seatId = normalizeSeatId(command);
|
|
111
127
|
let flowMode = "off";
|
|
112
|
-
|
|
113
|
-
let
|
|
128
|
+
let continueSeatId = null;
|
|
129
|
+
let continueTargets = [];
|
|
114
130
|
let index = 0;
|
|
115
131
|
|
|
116
132
|
while (index < args.length) {
|
|
117
133
|
const token = String(args[index] || "").trim().toLowerCase();
|
|
118
134
|
|
|
119
|
-
if (token === "flow"
|
|
135
|
+
if (token === "flow") {
|
|
120
136
|
const flowToken = String(args[index + 1] || "").trim().toLowerCase();
|
|
121
137
|
if (flowToken === "on" || flowToken === "off") {
|
|
122
138
|
flowMode = flowToken;
|
|
@@ -127,22 +143,23 @@ function parseSeatOptions(command, args) {
|
|
|
127
143
|
}
|
|
128
144
|
|
|
129
145
|
if (token === "continue") {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (!targetSeatId) {
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
continueSeatIds.push(targetSeatId);
|
|
141
|
-
consumedTargets += 1;
|
|
142
|
-
index += 1;
|
|
146
|
+
const parsedTargets = parseContinueTargets(args.slice(index + 1), flowMode);
|
|
147
|
+
if (parsedTargets.targets.length > 0) {
|
|
148
|
+
continueTargets = mergeTargets(continueTargets, parsedTargets.targets);
|
|
149
|
+
continueSeatId = continueTargets[0].targetSeatId;
|
|
150
|
+
index += 1 + parsedTargets.consumed;
|
|
151
|
+
continue;
|
|
143
152
|
}
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
144
155
|
|
|
145
|
-
|
|
156
|
+
if (token === "link") {
|
|
157
|
+
const parsedLinks = parseLinkTargets(args.slice(index + 1), seatId, flowMode);
|
|
158
|
+
if (parsedLinks.consumed > 0) {
|
|
159
|
+
flowMode = parsedLinks.flowMode;
|
|
160
|
+
continueTargets = mergeTargets(continueTargets, parsedLinks.continueTargets);
|
|
161
|
+
continueSeatId = continueTargets[0]?.targetSeatId || null;
|
|
162
|
+
index += 1 + parsedLinks.consumed;
|
|
146
163
|
continue;
|
|
147
164
|
}
|
|
148
165
|
break;
|
|
@@ -152,18 +169,98 @@ function parseSeatOptions(command, args) {
|
|
|
152
169
|
}
|
|
153
170
|
|
|
154
171
|
if (index === args.length) {
|
|
155
|
-
return {
|
|
156
|
-
flowMode,
|
|
157
|
-
continueSeatId: continueSeatIds[0] || null,
|
|
158
|
-
continueSeatIds,
|
|
159
|
-
};
|
|
172
|
+
return { flowMode, continueSeatId, continueTargets };
|
|
160
173
|
}
|
|
161
174
|
|
|
162
175
|
throw new Error(
|
|
163
|
-
`\`muuuuse ${command}\` accepts no extra arguments, \`flow on\` / \`flow off\`, optional \`continue <seat> [<seat> ...]
|
|
176
|
+
`\`muuuuse ${command}\` accepts no extra arguments, \`flow on\` / \`flow off\`, optional \`continue <seat>\`, or \`link <seat> flow on [<seat> flow off ...]\`. Run it directly in the terminal you want to arm.`
|
|
164
177
|
);
|
|
165
178
|
}
|
|
166
179
|
|
|
180
|
+
function mergeTargets(existingTargets, nextTargets) {
|
|
181
|
+
const merged = [];
|
|
182
|
+
for (const target of Array.isArray(existingTargets) ? existingTargets : []) {
|
|
183
|
+
upsertTarget(merged, target);
|
|
184
|
+
}
|
|
185
|
+
for (const target of Array.isArray(nextTargets) ? nextTargets : []) {
|
|
186
|
+
upsertTarget(merged, target);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return merged;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function parseContinueTargets(args, defaultFlowMode) {
|
|
193
|
+
const targets = [];
|
|
194
|
+
let consumed = 0;
|
|
195
|
+
|
|
196
|
+
while (consumed < args.length) {
|
|
197
|
+
const targetSeatId = normalizeSeatId(args[consumed]);
|
|
198
|
+
if (!targetSeatId) {
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const nextFlowMode = parseFlowModeToken(args[consumed + 1], args[consumed + 2]);
|
|
203
|
+
const target = {
|
|
204
|
+
targetSeatId,
|
|
205
|
+
flowMode: nextFlowMode || defaultFlowMode,
|
|
206
|
+
};
|
|
207
|
+
upsertTarget(targets, target);
|
|
208
|
+
consumed += nextFlowMode ? 3 : 1;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return { consumed, targets };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function parseLinkTargets(args, seatId, defaultFlowMode) {
|
|
215
|
+
const partnerSeatId = seatId ? getPartnerSeatId(seatId) : null;
|
|
216
|
+
const continueTargets = [];
|
|
217
|
+
let flowMode = defaultFlowMode;
|
|
218
|
+
let consumed = 0;
|
|
219
|
+
|
|
220
|
+
while (consumed < args.length) {
|
|
221
|
+
const targetSeatId = normalizeSeatId(args[consumed]);
|
|
222
|
+
if (!targetSeatId) {
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const targetFlowMode = parseFlowModeToken(args[consumed + 1], args[consumed + 2]);
|
|
227
|
+
if (!targetFlowMode) {
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (targetSeatId === partnerSeatId) {
|
|
232
|
+
flowMode = targetFlowMode;
|
|
233
|
+
} else {
|
|
234
|
+
upsertTarget(continueTargets, {
|
|
235
|
+
targetSeatId,
|
|
236
|
+
flowMode: targetFlowMode,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
consumed += 3;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return { consumed, continueTargets, flowMode };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function parseFlowModeToken(flowToken, modeToken) {
|
|
247
|
+
const normalizedFlowToken = String(flowToken || "").trim().toLowerCase();
|
|
248
|
+
const normalizedModeToken = String(modeToken || "").trim().toLowerCase();
|
|
249
|
+
if (normalizedFlowToken === "flow" && (normalizedModeToken === "on" || normalizedModeToken === "off")) {
|
|
250
|
+
return normalizedModeToken;
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function upsertTarget(targets, nextTarget) {
|
|
256
|
+
const existingIndex = targets.findIndex((entry) => entry.targetSeatId === nextTarget.targetSeatId);
|
|
257
|
+
if (existingIndex >= 0) {
|
|
258
|
+
targets[existingIndex] = nextTarget;
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
targets.push(nextTarget);
|
|
262
|
+
}
|
|
263
|
+
|
|
167
264
|
module.exports = {
|
|
168
265
|
main,
|
|
169
266
|
};
|
package/src/runtime.js
CHANGED
|
@@ -91,34 +91,24 @@ function normalizeContinueSeatId(value) {
|
|
|
91
91
|
return seatId || null;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
function
|
|
95
|
-
const
|
|
94
|
+
function normalizeContinueTargets(targets, defaultFlowMode = "off") {
|
|
95
|
+
const normalized = [];
|
|
96
96
|
const seen = new Set();
|
|
97
|
-
const seatIds = [];
|
|
98
97
|
|
|
99
|
-
for (const entry of
|
|
100
|
-
const
|
|
101
|
-
if (!
|
|
98
|
+
for (const entry of Array.isArray(targets) ? targets : []) {
|
|
99
|
+
const targetSeatId = normalizeSeatId(entry?.targetSeatId ?? entry?.seatId ?? entry);
|
|
100
|
+
if (!targetSeatId || seen.has(targetSeatId)) {
|
|
102
101
|
continue;
|
|
103
102
|
}
|
|
104
103
|
|
|
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;
|
|
104
|
+
seen.add(targetSeatId);
|
|
105
|
+
normalized.push({
|
|
106
|
+
targetSeatId,
|
|
107
|
+
flowMode: normalizeFlowMode(entry?.flowMode ?? entry?.flow ?? defaultFlowMode),
|
|
108
|
+
});
|
|
119
109
|
}
|
|
120
110
|
|
|
121
|
-
return
|
|
111
|
+
return normalized;
|
|
122
112
|
}
|
|
123
113
|
|
|
124
114
|
function resolveShell() {
|
|
@@ -501,6 +491,7 @@ function buildAnswerSignaturePayload(sessionName, challenge, entry) {
|
|
|
501
491
|
seatId: entry.seatId,
|
|
502
492
|
origin: entry.origin,
|
|
503
493
|
phase: entry.phase || "final_answer",
|
|
494
|
+
flowMode: entry.flowMode || "off",
|
|
504
495
|
createdAt: entry.createdAt,
|
|
505
496
|
text: entry.text,
|
|
506
497
|
});
|
|
@@ -520,6 +511,7 @@ function buildContinuationEntry(sourceSessionName, targetSeatId, entry) {
|
|
|
520
511
|
chainId: entry.chainId,
|
|
521
512
|
hop: entry.hop,
|
|
522
513
|
sourceAnswerId: entry.id,
|
|
514
|
+
flowMode: entry.flowMode || null,
|
|
523
515
|
publicKey: entry.publicKey || null,
|
|
524
516
|
signature: entry.signature || null,
|
|
525
517
|
};
|
|
@@ -643,12 +635,15 @@ class ArmedSeat {
|
|
|
643
635
|
this.partnerSeatId = getPartnerSeatId(options.seatId);
|
|
644
636
|
this.anchorSeatId = isAnchorSeat(options.seatId) ? options.seatId : this.partnerSeatId;
|
|
645
637
|
this.flowMode = normalizeFlowMode(options.flowMode);
|
|
646
|
-
this.
|
|
647
|
-
|
|
638
|
+
this.continueTargets = normalizeContinueTargets(
|
|
639
|
+
options.continueTargets || (
|
|
640
|
+
options.continueSeatId ? [{ targetSeatId: options.continueSeatId, flowMode: options.flowMode }] : []
|
|
641
|
+
),
|
|
642
|
+
this.flowMode
|
|
648
643
|
);
|
|
649
|
-
this.continueSeatId = this.
|
|
644
|
+
this.continueSeatId = this.continueTargets[0]?.targetSeatId || normalizeContinueSeatId(options.continueSeatId);
|
|
650
645
|
this.cwd = normalizeWorkingPath(options.cwd);
|
|
651
|
-
if (this.
|
|
646
|
+
if (this.continueTargets.some((target) => target.targetSeatId === this.seatId)) {
|
|
652
647
|
throw new Error(`\`muuuuse ${this.seatId}\` cannot continue to itself.`);
|
|
653
648
|
}
|
|
654
649
|
this.sessionName = resolveSessionName(this.cwd, this.seatId);
|
|
@@ -724,8 +719,8 @@ class ArmedSeat {
|
|
|
724
719
|
partnerSeatId: this.partnerSeatId,
|
|
725
720
|
sessionName: this.sessionName,
|
|
726
721
|
flowMode: this.flowMode,
|
|
727
|
-
continueSeatIds: this.continueSeatIds,
|
|
728
722
|
continueSeatId: this.continueSeatId,
|
|
723
|
+
continueTargets: this.continueTargets,
|
|
729
724
|
cwd: this.cwd,
|
|
730
725
|
pid: process.pid,
|
|
731
726
|
childPid: this.childPid,
|
|
@@ -741,8 +736,8 @@ class ArmedSeat {
|
|
|
741
736
|
partnerSeatId: this.partnerSeatId,
|
|
742
737
|
sessionName: this.sessionName,
|
|
743
738
|
flowMode: this.flowMode,
|
|
744
|
-
continueSeatIds: this.continueSeatIds,
|
|
745
739
|
continueSeatId: this.continueSeatId,
|
|
740
|
+
continueTargets: this.continueTargets,
|
|
746
741
|
cwd: this.cwd,
|
|
747
742
|
pid: process.pid,
|
|
748
743
|
childPid: this.childPid,
|
|
@@ -1088,8 +1083,12 @@ class ArmedSeat {
|
|
|
1088
1083
|
return Number.isFinite(requestedAtMs) && requestedAtMs > this.startedAtMs;
|
|
1089
1084
|
}
|
|
1090
1085
|
|
|
1086
|
+
shouldCaptureCommentary() {
|
|
1087
|
+
return this.flowMode === "on" || this.continueTargets.some((target) => target.flowMode === "on");
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1091
1090
|
findContinuationTarget(targetSeatId) {
|
|
1092
|
-
const normalizedTargetSeatId =
|
|
1091
|
+
const normalizedTargetSeatId = normalizeSeatId(targetSeatId);
|
|
1093
1092
|
if (!normalizedTargetSeatId) {
|
|
1094
1093
|
return null;
|
|
1095
1094
|
}
|
|
@@ -1141,7 +1140,8 @@ class ArmedSeat {
|
|
|
1141
1140
|
return;
|
|
1142
1141
|
}
|
|
1143
1142
|
|
|
1144
|
-
|
|
1143
|
+
const inboundFlowMode = normalizeFlowMode(entry.flowMode || this.flowMode);
|
|
1144
|
+
if (!shouldAcceptInboundEntry(inboundFlowMode, entry)) {
|
|
1145
1145
|
continue;
|
|
1146
1146
|
}
|
|
1147
1147
|
|
|
@@ -1153,6 +1153,7 @@ class ArmedSeat {
|
|
|
1153
1153
|
seatId: entry.seatId,
|
|
1154
1154
|
origin: entry.origin || "unknown",
|
|
1155
1155
|
phase: getRelayPhase(entry),
|
|
1156
|
+
flowMode: inboundFlowMode,
|
|
1156
1157
|
createdAt: entry.createdAt,
|
|
1157
1158
|
text: payload,
|
|
1158
1159
|
});
|
|
@@ -1208,7 +1209,8 @@ class ArmedSeat {
|
|
|
1208
1209
|
return;
|
|
1209
1210
|
}
|
|
1210
1211
|
|
|
1211
|
-
|
|
1212
|
+
const continueFlowMode = normalizeFlowMode(entry.flowMode || this.flowMode);
|
|
1213
|
+
if (!shouldAcceptInboundEntry(continueFlowMode, entry)) {
|
|
1212
1214
|
continue;
|
|
1213
1215
|
}
|
|
1214
1216
|
|
|
@@ -1420,12 +1422,13 @@ class ArmedSeat {
|
|
|
1420
1422
|
}
|
|
1421
1423
|
|
|
1422
1424
|
const answers = [];
|
|
1425
|
+
const captureCommentary = this.shouldCaptureCommentary();
|
|
1423
1426
|
if (detectedAgent.type === "codex") {
|
|
1424
1427
|
const result = readCodexAnswers(
|
|
1425
1428
|
this.liveState.sessionFile,
|
|
1426
1429
|
this.liveState.offset,
|
|
1427
1430
|
this.liveState.captureSinceMs,
|
|
1428
|
-
{ flowMode:
|
|
1431
|
+
{ flowMode: captureCommentary }
|
|
1429
1432
|
);
|
|
1430
1433
|
this.liveState.offset = result.nextOffset;
|
|
1431
1434
|
answers.push(...result.answers);
|
|
@@ -1434,7 +1437,7 @@ class ArmedSeat {
|
|
|
1434
1437
|
this.liveState.sessionFile,
|
|
1435
1438
|
this.liveState.offset,
|
|
1436
1439
|
this.liveState.captureSinceMs,
|
|
1437
|
-
{ flowMode:
|
|
1440
|
+
{ flowMode: captureCommentary }
|
|
1438
1441
|
);
|
|
1439
1442
|
this.liveState.offset = result.nextOffset;
|
|
1440
1443
|
answers.push(...result.answers);
|
|
@@ -1443,7 +1446,7 @@ class ArmedSeat {
|
|
|
1443
1446
|
this.liveState.sessionFile,
|
|
1444
1447
|
this.liveState.lastMessageId,
|
|
1445
1448
|
this.liveState.captureSinceMs,
|
|
1446
|
-
{ flowMode:
|
|
1449
|
+
{ flowMode: captureCommentary }
|
|
1447
1450
|
);
|
|
1448
1451
|
this.liveState.lastMessageId = result.lastMessageId;
|
|
1449
1452
|
this.liveState.offset = result.fileSize;
|
|
@@ -1501,6 +1504,7 @@ class ArmedSeat {
|
|
|
1501
1504
|
seatId: this.seatId,
|
|
1502
1505
|
origin: entry.origin || "unknown",
|
|
1503
1506
|
phase: entry.phase || "final_answer",
|
|
1507
|
+
flowMode: this.flowMode,
|
|
1504
1508
|
text: payload,
|
|
1505
1509
|
createdAt: entry.createdAt || new Date().toISOString(),
|
|
1506
1510
|
chainId: pendingInboundContext?.chainId || entry.chainId || entryId,
|
|
@@ -1520,20 +1524,23 @@ class ArmedSeat {
|
|
|
1520
1524
|
}
|
|
1521
1525
|
|
|
1522
1526
|
forwardContinuation(signedEntry) {
|
|
1523
|
-
if (this.
|
|
1527
|
+
if (this.continueTargets.length === 0) {
|
|
1524
1528
|
return;
|
|
1525
1529
|
}
|
|
1526
1530
|
|
|
1527
|
-
for (const
|
|
1528
|
-
const target = this.findContinuationTarget(
|
|
1531
|
+
for (const targetEntry of this.continueTargets) {
|
|
1532
|
+
const target = this.findContinuationTarget(targetEntry.targetSeatId);
|
|
1529
1533
|
if (!target) {
|
|
1530
|
-
this.log(`[${this.seatId}] continue ${
|
|
1534
|
+
this.log(`[${this.seatId}] continue ${targetEntry.targetSeatId} unavailable`);
|
|
1531
1535
|
continue;
|
|
1532
1536
|
}
|
|
1533
1537
|
|
|
1534
|
-
const continuationEntry = buildContinuationEntry(this.sessionName, target.seatId,
|
|
1538
|
+
const continuationEntry = buildContinuationEntry(this.sessionName, target.seatId, {
|
|
1539
|
+
...signedEntry,
|
|
1540
|
+
flowMode: targetEntry.flowMode,
|
|
1541
|
+
});
|
|
1535
1542
|
appendJsonl(target.paths.continuePath, continuationEntry);
|
|
1536
|
-
this.log(`[${this.seatId} => ${target.seatId}] ${previewText(continuationEntry.text)}`);
|
|
1543
|
+
this.log(`[${this.seatId} => ${target.seatId} ${targetEntry.flowMode}] ${previewText(continuationEntry.text)}`);
|
|
1537
1544
|
}
|
|
1538
1545
|
}
|
|
1539
1546
|
|
|
@@ -1583,9 +1590,10 @@ class ArmedSeat {
|
|
|
1583
1590
|
this.log(`${BRAND} seat ${this.seatId} armed for ${this.sessionName}.`);
|
|
1584
1591
|
this.log("Use this shell normally. Codex, Claude, and Gemini relay automatically from their local session logs.");
|
|
1585
1592
|
this.log(`Seat ${this.seatId} relay mode is flow ${this.flowMode}.`);
|
|
1586
|
-
if (this.
|
|
1587
|
-
|
|
1588
|
-
|
|
1593
|
+
if (this.continueTargets.length > 0) {
|
|
1594
|
+
this.log(
|
|
1595
|
+
`Seat ${this.seatId} continues to ${this.continueTargets.map((target) => `seat ${target.targetSeatId} (${target.flowMode})`).join(", ")}.`
|
|
1596
|
+
);
|
|
1589
1597
|
}
|
|
1590
1598
|
if (isAnchorSeat(this.seatId)) {
|
|
1591
1599
|
this.log(`Seat ${this.seatId} generated the session key and is waiting for seat ${this.partnerSeatId} to sign it.`);
|
|
@@ -1684,19 +1692,20 @@ function buildSeatReport(sessionName, seatId) {
|
|
|
1684
1692
|
return null;
|
|
1685
1693
|
}
|
|
1686
1694
|
|
|
1687
|
-
const continueSeatIds = normalizeContinueSeatIds(
|
|
1688
|
-
resolveContinueSeatConfig(
|
|
1689
|
-
status?.continueSeatIds ?? meta?.continueSeatIds,
|
|
1690
|
-
status?.continueSeatId ?? meta?.continueSeatId
|
|
1691
|
-
)
|
|
1692
|
-
);
|
|
1693
|
-
|
|
1694
1695
|
return {
|
|
1695
1696
|
seatId,
|
|
1697
|
+
partnerSeatId: status?.partnerSeatId || meta?.partnerSeatId || getPartnerSeatId(seatId),
|
|
1696
1698
|
state: wrapperLive ? status?.state || "running" : "orphaned_child",
|
|
1697
1699
|
flowMode: status?.flowMode || meta?.flowMode || "off",
|
|
1698
|
-
continueSeatId:
|
|
1699
|
-
|
|
1700
|
+
continueSeatId: status?.continueSeatId || meta?.continueSeatId || null,
|
|
1701
|
+
continueTargets: normalizeContinueTargets(
|
|
1702
|
+
status?.continueTargets || meta?.continueTargets || (
|
|
1703
|
+
(status?.continueSeatId || meta?.continueSeatId)
|
|
1704
|
+
? [{ targetSeatId: status?.continueSeatId || meta?.continueSeatId, flowMode: status?.flowMode || meta?.flowMode || "off" }]
|
|
1705
|
+
: []
|
|
1706
|
+
),
|
|
1707
|
+
status?.flowMode || meta?.flowMode || "off"
|
|
1708
|
+
),
|
|
1700
1709
|
wrapperPid,
|
|
1701
1710
|
childPid,
|
|
1702
1711
|
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 on",
|
|
291
|
+
" muuuuse 1 link 2 flow on 3 flow off",
|
|
292
|
+
" muuuuse 2",
|
|
293
|
+
" muuuuse 2 link 1 flow on",
|
|
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
301
|
" 1. Run `muuuuse 1` in terminal one.",
|
|
311
302
|
" 2. Run `muuuuse 2` in terminal two.",
|
|
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 path.",
|
|
306
|
+
" 6. Linking the odd/even partner sets the normal pair flow. Extra linked seats become continuations.",
|
|
307
|
+
" 7. Legacy `flow on` / `flow off` and `continue` still parse, but `link` is the main command shape.",
|
|
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
|
"",
|