muuuuse 3.1.1 → 3.3.2
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 +13 -11
- package/package.json +2 -2
- package/src/cli.js +90 -82
- package/src/runtime.js +201 -451
- package/src/util.js +18 -28
package/README.md
CHANGED
|
@@ -5,11 +5,10 @@
|
|
|
5
5
|
It does one job:
|
|
6
6
|
- arm terminal one with `muuuuse 1`
|
|
7
7
|
- arm terminal two with `muuuuse 2`
|
|
8
|
-
-
|
|
9
|
-
- additional isolated pairs work the same way: `3/4`, `5/6`, `7/8`, ...
|
|
8
|
+
- let each seat define signed relay links to any other armed seat
|
|
10
9
|
- choose per-seat relay mode with `flow on` or `flow off`
|
|
11
10
|
- watch Codex, Claude, or Gemini for local assistant output
|
|
12
|
-
- inject that output into
|
|
11
|
+
- inject that output into linked armed terminals
|
|
13
12
|
- keep looping until you stop it
|
|
14
13
|
|
|
15
14
|
The whole surface is:
|
|
@@ -19,9 +18,11 @@ muuuuse 1
|
|
|
19
18
|
muuuuse 1 flow on
|
|
20
19
|
muuuuse 1 flow off
|
|
21
20
|
muuuuse 1 flow off continue 5
|
|
21
|
+
muuuuse 1 link 2 flow on 3 flow off 5 flow off
|
|
22
22
|
muuuuse 2
|
|
23
23
|
muuuuse 2 flow off
|
|
24
24
|
muuuuse 2 flow on continue 3
|
|
25
|
+
muuuuse 2 link 1 flow off 3 flow on 4 flow on
|
|
25
26
|
muuuuse 3
|
|
26
27
|
muuuuse 3 flow on
|
|
27
28
|
muuuuse 4
|
|
@@ -35,20 +36,20 @@ muuuuse stop
|
|
|
35
36
|
Terminal 1:
|
|
36
37
|
|
|
37
38
|
```bash
|
|
38
|
-
muuuuse 1 flow on
|
|
39
|
+
muuuuse 1 link 2 flow on
|
|
39
40
|
```
|
|
40
41
|
|
|
41
42
|
Terminal 2:
|
|
42
43
|
|
|
43
44
|
```bash
|
|
44
|
-
muuuuse 2 flow off
|
|
45
|
+
muuuuse 2 link 1 flow off link 3 flow on link 4 flow on
|
|
45
46
|
```
|
|
46
47
|
|
|
47
|
-
Now both shells are armed
|
|
48
|
+
Now both shells are armed in the same cwd and join the same relay graph. Every seat has its own Ed25519 keypair. Each forwarded relay is signed by the sending seat. A target seat only accepts inbound relays when the sender linked to that target, so the graph can be open-ended without becoming an all-to-all broadcast.
|
|
48
49
|
|
|
49
|
-
`flow on` means that
|
|
50
|
+
`link <seat> flow on` means that outbound edge sends commentary and final answers. `link <seat> flow off` means that outbound edge sends final answers only. This is sender-side routing, not receiver-side filtering.
|
|
50
51
|
|
|
51
|
-
`continue <seat>`
|
|
52
|
+
`continue <seat>` is shorthand for one outbound link that uses the seat's default `flow on|off`. Explicit `link ... flow ...` edges are the full model and can be arranged into loops such as `1 -> 2 -> 3 -> 4 -> 1`.
|
|
52
53
|
|
|
53
54
|
If you want Codex in one and Gemini in the other, start them inside the armed shells:
|
|
54
55
|
|
|
@@ -60,7 +61,7 @@ codex
|
|
|
60
61
|
gemini
|
|
61
62
|
```
|
|
62
63
|
|
|
63
|
-
`🔌Muuuuse` tails the local session logs for supported CLIs, relays according to each
|
|
64
|
+
`🔌Muuuuse` tails the local session logs for supported CLIs, relays according to each outbound link's flow mode, types that output into the target seat, and then sends Enter as a separate keystroke.
|
|
64
65
|
|
|
65
66
|
Check the live state from any terminal:
|
|
66
67
|
|
|
@@ -77,8 +78,9 @@ muuuuse stop
|
|
|
77
78
|
## Notes
|
|
78
79
|
|
|
79
80
|
- state lives under `~/.muuuuse`
|
|
80
|
-
-
|
|
81
|
-
-
|
|
81
|
+
- all armed seats in the same cwd share one relay session graph
|
|
82
|
+
- only signed relays from senders that linked the target seat are accepted
|
|
83
|
+
- `continue <seat>` is a convenience alias for a single signed outbound link
|
|
82
84
|
- supported relay detection is built for Codex, Claude, and Gemini
|
|
83
85
|
|
|
84
86
|
## Install
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muuuuse",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "🔌Muuuuse arms regular terminals
|
|
3
|
+
"version": "3.3.2",
|
|
4
|
+
"description": "🔌Muuuuse arms regular terminals and relays assistant output across signed terminal links.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
7
7
|
"muuuuse": "bin/muuse.js"
|
package/src/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { BRAND,
|
|
1
|
+
const { BRAND, normalizeSeatId, usage } = require("./util");
|
|
2
2
|
const { ArmedSeat, getStatusReport, stopAllSessions } = require("./runtime");
|
|
3
3
|
|
|
4
4
|
async function main(argv = process.argv.slice(2)) {
|
|
@@ -59,8 +59,8 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
59
59
|
const { flowMode, continueSeatId, continueTargets } = parseSeatOptions(command, argv.slice(1));
|
|
60
60
|
const seat = new ArmedSeat({
|
|
61
61
|
cwd: process.cwd(),
|
|
62
|
-
continueTargets,
|
|
63
62
|
continueSeatId,
|
|
63
|
+
continueTargets,
|
|
64
64
|
flowMode,
|
|
65
65
|
seatId,
|
|
66
66
|
});
|
|
@@ -81,15 +81,11 @@ function renderSeatStatus(seat) {
|
|
|
81
81
|
`child ${seat.childPid || "-"}`,
|
|
82
82
|
];
|
|
83
83
|
|
|
84
|
-
if (seat.
|
|
85
|
-
bits.push(
|
|
86
|
-
}
|
|
87
|
-
const renderedLinks = renderLinkTargets(seat);
|
|
88
|
-
if (renderedLinks) {
|
|
89
|
-
bits.push(`link ${renderedLinks}`);
|
|
84
|
+
if (seat.continueSeatId) {
|
|
85
|
+
bits.push(`continue ${seat.continueSeatId}`);
|
|
90
86
|
}
|
|
91
|
-
if (seat.
|
|
92
|
-
bits.push(`
|
|
87
|
+
if (Array.isArray(seat.continueTargets) && seat.continueTargets.length > 0) {
|
|
88
|
+
bits.push(`links ${renderLinkTargets(seat.continueTargets)}`);
|
|
93
89
|
}
|
|
94
90
|
if (seat.lastAnswerAt) {
|
|
95
91
|
bits.push(`last answer ${seat.lastAnswerAt}`);
|
|
@@ -105,21 +101,10 @@ function renderSeatStatus(seat) {
|
|
|
105
101
|
return output;
|
|
106
102
|
}
|
|
107
103
|
|
|
108
|
-
function renderLinkTargets(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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(", ");
|
|
104
|
+
function renderLinkTargets(targets) {
|
|
105
|
+
return targets
|
|
106
|
+
.map((target) => `${target.seatId}:${target.flowMode}`)
|
|
107
|
+
.join(", ");
|
|
123
108
|
}
|
|
124
109
|
|
|
125
110
|
function parseSeatOptions(command, args) {
|
|
@@ -127,98 +112,121 @@ function parseSeatOptions(command, args) {
|
|
|
127
112
|
let flowMode = "off";
|
|
128
113
|
let continueSeatId = null;
|
|
129
114
|
let continueTargets = [];
|
|
130
|
-
|
|
131
|
-
return { flowMode, continueSeatId, continueTargets };
|
|
132
|
-
}
|
|
115
|
+
let index = 0;
|
|
133
116
|
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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;
|
|
117
|
+
for (; index < args.length;) {
|
|
118
|
+
const token = String(args[index] || "").trim().toLowerCase();
|
|
119
|
+
|
|
120
|
+
if (token === "flow") {
|
|
121
|
+
const nextFlowMode = parseFlowModeToken(args[index + 1]);
|
|
122
|
+
if (nextFlowMode) {
|
|
123
|
+
flowMode = nextFlowMode;
|
|
124
|
+
index += 2;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
break;
|
|
151
128
|
}
|
|
152
129
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
130
|
+
if (token === "continue") {
|
|
131
|
+
const targetSeatId = normalizeSeatId(args[index + 1]);
|
|
132
|
+
if (targetSeatId && targetSeatId !== seatId) {
|
|
133
|
+
continueSeatId = targetSeatId;
|
|
134
|
+
index += 2;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
159
138
|
}
|
|
160
139
|
|
|
161
|
-
if (
|
|
162
|
-
|
|
140
|
+
if (token === "link") {
|
|
141
|
+
const parsed = parseLinkTargets(seatId, args, index + 1);
|
|
142
|
+
if (!parsed) {
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
continueTargets = mergeTargets(continueTargets, parsed.continueTargets);
|
|
147
|
+
index = parsed.nextIndex;
|
|
148
|
+
continue;
|
|
163
149
|
}
|
|
150
|
+
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (index === args.length) {
|
|
155
|
+
return { flowMode, continueSeatId, continueTargets };
|
|
164
156
|
}
|
|
165
157
|
|
|
166
158
|
throw new Error(
|
|
167
|
-
`\`muuuuse ${command}\` accepts
|
|
159
|
+
`\`muuuuse ${command}\` accepts \`flow on\` / \`flow off\`, optional \`continue <seat>\`, and optional \`link <seat> flow on|off ...\` groups. Run it directly in the terminal you want to arm.`
|
|
168
160
|
);
|
|
169
161
|
}
|
|
170
162
|
|
|
171
|
-
function
|
|
172
|
-
const
|
|
163
|
+
function mergeTargets(currentTargets, nextTargets) {
|
|
164
|
+
const merged = [...currentTargets];
|
|
165
|
+
for (const target of nextTargets) {
|
|
166
|
+
const currentIndex = merged.findIndex((entry) => entry.seatId === target.seatId);
|
|
167
|
+
if (currentIndex !== -1) {
|
|
168
|
+
merged.splice(currentIndex, 1);
|
|
169
|
+
}
|
|
170
|
+
merged.push(target);
|
|
171
|
+
}
|
|
172
|
+
return merged;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function parseLinkTargets(seatId, args, startIndex) {
|
|
176
|
+
let index = startIndex;
|
|
173
177
|
const continueTargets = [];
|
|
174
|
-
let flowMode = defaultFlowMode;
|
|
175
|
-
let consumed = 0;
|
|
176
178
|
|
|
177
|
-
while (
|
|
178
|
-
const targetSeatId = normalizeSeatId(args[
|
|
179
|
-
if (!targetSeatId) {
|
|
179
|
+
while (index < args.length) {
|
|
180
|
+
const targetSeatId = normalizeSeatId(args[index]);
|
|
181
|
+
if (!targetSeatId || targetSeatId === seatId) {
|
|
180
182
|
break;
|
|
181
183
|
}
|
|
182
184
|
|
|
183
|
-
|
|
184
|
-
if (!targetFlowMode) {
|
|
185
|
+
if (String(args[index + 1] || "").trim().toLowerCase() !== "flow") {
|
|
185
186
|
break;
|
|
186
187
|
}
|
|
187
188
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
upsertTarget(continueTargets, {
|
|
192
|
-
targetSeatId,
|
|
193
|
-
flowMode: targetFlowMode,
|
|
194
|
-
});
|
|
189
|
+
const targetFlowMode = parseFlowModeToken(args[index + 2]);
|
|
190
|
+
if (!targetFlowMode) {
|
|
191
|
+
break;
|
|
195
192
|
}
|
|
196
193
|
|
|
197
|
-
|
|
194
|
+
upsertTarget(continueTargets, {
|
|
195
|
+
seatId: targetSeatId,
|
|
196
|
+
flowMode: targetFlowMode,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
index += 3;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (index === startIndex) {
|
|
203
|
+
return null;
|
|
198
204
|
}
|
|
199
205
|
|
|
200
|
-
return {
|
|
206
|
+
return {
|
|
207
|
+
continueTargets,
|
|
208
|
+
nextIndex: index,
|
|
209
|
+
};
|
|
201
210
|
}
|
|
202
211
|
|
|
203
|
-
function parseFlowModeToken(
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return normalizedModeToken;
|
|
212
|
+
function parseFlowModeToken(value) {
|
|
213
|
+
const token = String(value || "").trim().toLowerCase();
|
|
214
|
+
if (token === "on" || token === "off") {
|
|
215
|
+
return token;
|
|
208
216
|
}
|
|
209
217
|
return null;
|
|
210
218
|
}
|
|
211
219
|
|
|
212
220
|
function upsertTarget(targets, nextTarget) {
|
|
213
|
-
const
|
|
214
|
-
if (
|
|
215
|
-
targets
|
|
221
|
+
const currentIndex = targets.findIndex((target) => target.seatId === nextTarget.seatId);
|
|
222
|
+
if (currentIndex === -1) {
|
|
223
|
+
targets.push(nextTarget);
|
|
216
224
|
return;
|
|
217
225
|
}
|
|
218
|
-
|
|
226
|
+
|
|
227
|
+
targets[currentIndex] = nextTarget;
|
|
219
228
|
}
|
|
220
229
|
|
|
221
230
|
module.exports = {
|
|
222
231
|
main,
|
|
223
|
-
parseSeatOptions,
|
|
224
232
|
};
|