@wangyaoshen/remux 0.3.9-dev.390cb29 → 0.3.10-dev.574c4d2

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.
@@ -7,6 +7,7 @@ import net from "net";
7
7
  import fs from "fs";
8
8
  import path from "path";
9
9
  import { fileURLToPath } from "url";
10
+ import pty from "node-pty";
10
11
 
11
12
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
13
  const DAEMON_SCRIPT = path.join(__dirname, "..", "pty-daemon.js");
@@ -21,6 +22,24 @@ const TAG_SNAPSHOT_REQ = 0x06;
21
22
  const TAG_SNAPSHOT_RES = 0x07;
22
23
  const TAG_SHUTDOWN = 0xff;
23
24
 
25
+ function supportsNodePty() {
26
+ try {
27
+ const proc = pty.spawn("/bin/sh", ["-lc", "exit 0"], {
28
+ name: "xterm-256color",
29
+ cols: 80,
30
+ rows: 24,
31
+ cwd: "/tmp",
32
+ env: process.env,
33
+ });
34
+ proc.kill();
35
+ return true;
36
+ } catch {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ const describePtyLifecycle = supportsNodePty() ? describe : describe.skip;
42
+
24
43
  function encodeFrame(tag, payload) {
25
44
  const data =
26
45
  typeof payload === "string" ? Buffer.from(payload, "utf8") : payload;
@@ -137,7 +156,7 @@ describe("TLV Frame Codec", () => {
137
156
  });
138
157
  });
139
158
 
140
- describe("PTY Daemon Lifecycle", () => {
159
+ describePtyLifecycle("PTY Daemon Lifecycle", () => {
141
160
  let daemon;
142
161
  let socketPath;
143
162
 
@@ -11,6 +11,7 @@ import WebSocket from "ws";
11
11
  import fs from "fs";
12
12
  import path from "path";
13
13
  import { homedir } from "os";
14
+ import pty from "node-pty";
14
15
 
15
16
  const PORT = 19876 + Math.floor(Math.random() * 1000); // randomized test port
16
17
  const TOKEN = "test-token-" + Date.now();
@@ -20,6 +21,24 @@ const PERSIST_FILE = path.join(PERSIST_DIR, `sessions-${INSTANCE_ID}.json`);
20
21
  const DB_FILE = path.join(PERSIST_DIR, `remux-${INSTANCE_ID}.db`);
21
22
  let serverProc;
22
23
 
24
+ function supportsNodePty() {
25
+ try {
26
+ const proc = pty.spawn("/bin/sh", ["-lc", "exit 0"], {
27
+ name: "xterm-256color",
28
+ cols: 80,
29
+ rows: 24,
30
+ cwd: "/tmp",
31
+ env: process.env,
32
+ });
33
+ proc.kill();
34
+ return true;
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ const PTY_SUPPORTED = supportsNodePty();
41
+
23
42
  function httpGet(urlPath) {
24
43
  return new Promise((resolve, reject) => {
25
44
  http.get(`http://localhost:${PORT}${urlPath}`, (res) => {
@@ -113,7 +132,7 @@ beforeAll(async () => {
113
132
  // Explicitly remove REMUX_PASSWORD to avoid env leaking from parent
114
133
  const cleanEnv = { ...process.env };
115
134
  delete cleanEnv.REMUX_PASSWORD;
116
- serverProc = spawn("node", ["server.js"], {
135
+ serverProc = spawn(process.execPath, ["server.js"], {
117
136
  env: {
118
137
  ...cleanEnv,
119
138
  PORT: String(PORT),
@@ -185,6 +204,15 @@ describe("HTTP", () => {
185
204
  expect(res.body).toContain("<title>Remux</title>");
186
205
  });
187
206
 
207
+ it("serves emergency mode shell without workspace or device chrome", async () => {
208
+ const res = await httpGet(`/?token=${TOKEN}`);
209
+ expect(res.status).toBe(200);
210
+ expect(res.body).toContain("cmd-input");
211
+ expect(res.body).not.toContain("Workspace");
212
+ expect(res.body).not.toContain("Devices");
213
+ expect(res.body).not.toContain("Enable Notifications");
214
+ });
215
+
188
216
  it("serves ghostty-web JS", async () => {
189
217
  const res = await httpGet("/dist/ghostty-web.js");
190
218
  expect(res.status).toBe(200);
@@ -513,16 +541,18 @@ describe("client connection state", () => {
513
541
  const ws2 = await connectAuthed();
514
542
 
515
543
  // ws1 attaches first (active)
516
- await sendAndCollect(
544
+ const msgs1 = await sendAndCollect(
517
545
  ws1,
518
546
  { type: "attach_first", session: "main", cols: 80, rows: 24 },
519
547
  { timeout: 3000 },
520
548
  );
549
+ const att1 = msgs1.find((m) => m.type === "attached");
550
+ expect(att1).toBeDefined();
521
551
 
522
552
  // ws2 attaches to same tab (observer)
523
553
  const msgs2 = await sendAndCollect(
524
554
  ws2,
525
- { type: "attach_first", session: "main", cols: 80, rows: 24 },
555
+ { type: "attach_tab", tabId: att1.tabId, cols: 80, rows: 24 },
526
556
  { timeout: 3000 },
527
557
  );
528
558
  const att2 = msgs2.find((m) => m.type === "attached");
@@ -552,11 +582,12 @@ describe("client connection state", () => {
552
582
  const att1 = msgs1.find((m) => m.type === "attached");
553
583
  expect(att1.role).toBe("active");
554
584
  const clientId1 = att1.clientId;
585
+ const tabId = att1.tabId;
555
586
 
556
587
  // ws2 attaches (observer)
557
588
  const msgs2 = await sendAndCollect(
558
589
  ws2,
559
- { type: "attach_first", session: "main", cols: 80, rows: 24 },
590
+ { type: "attach_tab", tabId, cols: 80, rows: 24 },
560
591
  { timeout: 3000 },
561
592
  );
562
593
  const att2 = msgs2.find((m) => m.type === "attached");
@@ -617,11 +648,12 @@ describe("client connection state", () => {
617
648
  const att1 = msgs1.find((m) => m.type === "attached");
618
649
  expect(att1.role).toBe("active");
619
650
  const clientId1 = att1.clientId;
651
+ const tabId = att1.tabId;
620
652
 
621
653
  // ws2 attaches (observer)
622
654
  const msgs2 = await sendAndCollect(
623
655
  ws2,
624
- { type: "attach_first", session: "main", cols: 80, rows: 24 },
656
+ { type: "attach_tab", tabId, cols: 80, rows: 24 },
625
657
  { timeout: 3000 },
626
658
  );
627
659
  const att2 = msgs2.find((m) => m.type === "attached");
@@ -669,16 +701,18 @@ describe("client connection state", () => {
669
701
  const ws2 = await connectAuthed();
670
702
 
671
703
  // ws1 attaches (active)
672
- await sendAndCollect(
704
+ const msgs1 = await sendAndCollect(
673
705
  ws1,
674
706
  { type: "attach_first", session: "main", cols: 80, rows: 24 },
675
707
  { timeout: 3000 },
676
708
  );
709
+ const att1 = msgs1.find((m) => m.type === "attached");
710
+ expect(att1).toBeDefined();
677
711
 
678
712
  // ws2 attaches (observer)
679
713
  const msgs2 = await sendAndCollect(
680
714
  ws2,
681
- { type: "attach_first", session: "main", cols: 80, rows: 24 },
715
+ { type: "attach_tab", tabId: att1.tabId, cols: 80, rows: 24 },
682
716
  { timeout: 3000 },
683
717
  );
684
718
  const att2 = msgs2.find((m) => m.type === "attached");
@@ -720,15 +754,15 @@ describe("multi-client", () => {
720
754
  { type: "attach_first", session: "main", cols: 100, rows: 30 },
721
755
  { timeout: 3000 },
722
756
  );
757
+ const att1 = msgs1.find((m) => m.type === "attached");
758
+ expect(att1).toBeDefined();
723
759
  const msgs2 = await sendAndCollect(
724
760
  ws2,
725
- { type: "attach_first", session: "main", cols: 80, rows: 24 },
761
+ { type: "attach_tab", tabId: att1.tabId, cols: 80, rows: 24 },
726
762
  { timeout: 3000 },
727
763
  );
728
764
 
729
- const att1 = msgs1.find((m) => m.type === "attached");
730
765
  const att2 = msgs2.find((m) => m.type === "attached");
731
- expect(att1).toBeDefined();
732
766
  expect(att2).toBeDefined();
733
767
  expect(att1.tabId).toBe(att2.tabId);
734
768
 
@@ -840,7 +874,11 @@ describe("inspect", () => {
840
874
  const result = msgs.find((m) => m.type === "inspect_result");
841
875
  expect(result).toBeDefined();
842
876
  expect(typeof result.text).toBe("string");
843
- expect(result.text).toContain("inspect-test-output");
877
+ if (PTY_SUPPORTED) {
878
+ expect(result.text).toContain("inspect-test-output");
879
+ } else {
880
+ expect(result.text).toContain("Shell unavailable on this machine");
881
+ }
844
882
  expect(result.meta).toBeDefined();
845
883
  expect(result.meta.session).toBe("main");
846
884
  expect(typeof result.meta.cols).toBe("number");
package/vitest.config.js CHANGED
@@ -2,6 +2,7 @@ import { defineConfig } from "vitest/config";
2
2
 
3
3
  export default defineConfig({
4
4
  test: {
5
+ fileParallelism: false,
5
6
  testTimeout: 20000,
6
7
  hookTimeout: 40000,
7
8
  include: ["tests/**/*.test.{js,ts}"],