muuuuse 1.4.2 → 2.1.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
@@ -1,4 +1,10 @@
1
- const { createHash, randomBytes } = require("node:crypto");
1
+ const {
2
+ createHash,
3
+ generateKeyPairSync,
4
+ randomBytes,
5
+ sign: cryptoSign,
6
+ verify: cryptoVerify,
7
+ } = require("node:crypto");
2
8
  const fs = require("node:fs");
3
9
  const os = require("node:os");
4
10
  const path = require("node:path");
@@ -28,6 +34,13 @@ function writeJson(filePath, value) {
28
34
  fs.renameSync(tempPath, filePath);
29
35
  }
30
36
 
37
+ function writeSecret(filePath, value, mode = 0o600) {
38
+ ensureDir(path.dirname(filePath));
39
+ const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
40
+ fs.writeFileSync(tempPath, String(value), { mode });
41
+ fs.renameSync(tempPath, filePath);
42
+ }
43
+
31
44
  function readJson(filePath, fallback = null) {
32
45
  try {
33
46
  return JSON.parse(fs.readFileSync(filePath, "utf8"));
@@ -161,15 +174,63 @@ function getSeatDir(sessionName, seatId) {
161
174
  function getSeatPaths(sessionName, seatId) {
162
175
  const dir = getSeatDir(sessionName, seatId);
163
176
  return {
177
+ ackPath: path.join(dir, "ack.json"),
178
+ challengePath: path.join(dir, "challenge.json"),
179
+ claimPath: path.join(dir, "claim.json"),
164
180
  dir,
165
181
  daemonPath: path.join(dir, "daemon.json"),
166
182
  eventsPath: path.join(dir, "events.jsonl"),
183
+ privateKeyPath: path.join(dir, "id_ed25519"),
184
+ publicKeyPath: path.join(dir, "id_ed25519.pub"),
167
185
  metaPath: path.join(dir, "meta.json"),
168
186
  pipePath: path.join(dir, "pipe.log"),
169
187
  statusPath: path.join(dir, "status.json"),
170
188
  };
171
189
  }
172
190
 
191
+ function loadOrCreateSeatIdentity(paths) {
192
+ try {
193
+ const privateKey = fs.readFileSync(paths.privateKeyPath, "utf8").trim();
194
+ const publicKey = fs.readFileSync(paths.publicKeyPath, "utf8").trim();
195
+ if (privateKey && publicKey) {
196
+ return { privateKey, publicKey };
197
+ }
198
+ } catch {
199
+ // generate below
200
+ }
201
+
202
+ const { privateKey, publicKey } = generateKeyPairSync("ed25519");
203
+ const privateKeyPem = privateKey.export({ format: "pem", type: "pkcs8" });
204
+ const publicKeyPem = publicKey.export({ format: "pem", type: "spki" });
205
+ writeSecret(paths.privateKeyPath, privateKeyPem);
206
+ writeSecret(paths.publicKeyPath, publicKeyPem, 0o644);
207
+ return {
208
+ privateKey: String(privateKeyPem).trim(),
209
+ publicKey: String(publicKeyPem).trim(),
210
+ };
211
+ }
212
+
213
+ function signText(text, privateKey) {
214
+ return cryptoSign(null, Buffer.from(String(text || ""), "utf8"), privateKey).toString("base64");
215
+ }
216
+
217
+ function verifyText(text, signature, publicKey) {
218
+ if (!signature || !publicKey) {
219
+ return false;
220
+ }
221
+
222
+ try {
223
+ return cryptoVerify(
224
+ null,
225
+ Buffer.from(String(text || ""), "utf8"),
226
+ publicKey,
227
+ Buffer.from(String(signature || ""), "base64")
228
+ );
229
+ } catch {
230
+ return false;
231
+ }
232
+ }
233
+
173
234
  function listSessionNames() {
174
235
  const sessionsRoot = path.join(getStateRoot(), "sessions");
175
236
  try {
@@ -195,9 +256,10 @@ function usage() {
195
256
  "Flow:",
196
257
  " 1. Run `muuuuse 1` in terminal one.",
197
258
  " 2. Run `muuuuse 2` in terminal two.",
198
- " 3. Use those armed shells normally.",
199
- " 4. Codex, Claude, and Gemini final answers relay automatically from their local session logs.",
200
- " 5. Run `muuuuse status` or `muuuuse stop` from any shell.",
259
+ " 3. Seat 1 generates the session key and seat 2 signs it automatically.",
260
+ " 4. Use those armed shells normally.",
261
+ " 5. Codex, Claude, and Gemini final answers relay automatically from their local session logs.",
262
+ " 6. Run `muuuuse status` or `muuuuse stop` from any shell.",
201
263
  "",
202
264
  "Notes:",
203
265
  " - No tmux.",
@@ -215,6 +277,7 @@ module.exports = {
215
277
  ensureDir,
216
278
  getDefaultSessionName,
217
279
  getFileSize,
280
+ loadOrCreateSeatIdentity,
218
281
  getSeatPaths,
219
282
  getSessionPaths,
220
283
  getStateRoot,
@@ -224,7 +287,10 @@ module.exports = {
224
287
  readAppendedText,
225
288
  readJson,
226
289
  sanitizeRelayText,
290
+ signText,
227
291
  sleep,
228
292
  usage,
293
+ verifyText,
229
294
  writeJson,
295
+ writeSecret,
230
296
  };