@wangyaoshen/remux 0.3.8-dev.bab6c95

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.
Files changed (166) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +47 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +38 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +28 -0
  4. package/.github/dependabot.yml +33 -0
  5. package/.github/workflows/ci.yml +65 -0
  6. package/.github/workflows/deploy.yml +65 -0
  7. package/.github/workflows/publish.yml +138 -0
  8. package/.github/workflows/release-please.yml +21 -0
  9. package/.gitmodules +3 -0
  10. package/.nvmrc +1 -0
  11. package/.release-please-manifest.json +3 -0
  12. package/CLAUDE.md +104 -0
  13. package/Dockerfile +23 -0
  14. package/LICENSE +21 -0
  15. package/README.md +120 -0
  16. package/apps/ios/Config/signing.xcconfig +4 -0
  17. package/apps/ios/Package.swift +26 -0
  18. package/apps/ios/Remux.xcodeproj/project.pbxproj +456 -0
  19. package/apps/ios/Remux.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  20. package/apps/ios/Sources/Remux/Extensions/FaceIDManager.swift +29 -0
  21. package/apps/ios/Sources/Remux/Extensions/InspectCache.swift +66 -0
  22. package/apps/ios/Sources/Remux/MainTabView.swift +32 -0
  23. package/apps/ios/Sources/Remux/Remux.entitlements +8 -0
  24. package/apps/ios/Sources/Remux/RemuxiOSApp.swift +14 -0
  25. package/apps/ios/Sources/Remux/RootView.swift +130 -0
  26. package/apps/ios/Sources/Remux/Views/Control/ControlView.swift +102 -0
  27. package/apps/ios/Sources/Remux/Views/Inspect/InspectView.swift +98 -0
  28. package/apps/ios/Sources/Remux/Views/Live/LiveTerminalView.swift +132 -0
  29. package/apps/ios/Sources/Remux/Views/Now/NowView.swift +173 -0
  30. package/apps/ios/Sources/Remux/Views/Onboarding/ManualConnectView.swift +55 -0
  31. package/apps/ios/Sources/Remux/Views/Onboarding/OnboardingView.swift +70 -0
  32. package/apps/ios/Sources/Remux/Views/Onboarding/QRScannerView.swift +92 -0
  33. package/apps/ios/Sources/Remux/Views/Settings/MeView.swift +136 -0
  34. package/apps/macos/Package.swift +37 -0
  35. package/apps/macos/Resources/shell-integration/bash/bash-preexec.sh +382 -0
  36. package/apps/macos/Resources/shell-integration/bash/ghostty.bash +315 -0
  37. package/apps/macos/Resources/shell-integration/elvish/lib/ghostty-integration.elv +191 -0
  38. package/apps/macos/Resources/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish +246 -0
  39. package/apps/macos/Resources/shell-integration/nushell/vendor/autoload/ghostty.nu +110 -0
  40. package/apps/macos/Resources/shell-integration/zsh/.zshenv +61 -0
  41. package/apps/macos/Resources/shell-integration/zsh/ghostty-integration +458 -0
  42. package/apps/macos/Resources/terminfo/67/ghostty +0 -0
  43. package/apps/macos/Resources/terminfo/78/xterm-ghostty +0 -0
  44. package/apps/macos/Sources/Remux/AppDelegate.swift +257 -0
  45. package/apps/macos/Sources/Remux/CrashReporter.swift +210 -0
  46. package/apps/macos/Sources/Remux/FinderIntegration.swift +117 -0
  47. package/apps/macos/Sources/Remux/GhosttyConfig.swift +311 -0
  48. package/apps/macos/Sources/Remux/KeyboardShortcuts/ShortcutAction.swift +115 -0
  49. package/apps/macos/Sources/Remux/KeyboardShortcuts/ShortcutSettingsView.swift +271 -0
  50. package/apps/macos/Sources/Remux/KeyboardShortcuts/StoredShortcut.swift +149 -0
  51. package/apps/macos/Sources/Remux/MainContentView.swift +308 -0
  52. package/apps/macos/Sources/Remux/MenuBarManager.swift +275 -0
  53. package/apps/macos/Sources/Remux/NotificationManager.swift +145 -0
  54. package/apps/macos/Sources/Remux/PortScanner.swift +152 -0
  55. package/apps/macos/Sources/Remux/RemuxApp.swift +13 -0
  56. package/apps/macos/Sources/Remux/SSHDetector.swift +151 -0
  57. package/apps/macos/Sources/Remux/SessionPersistence.swift +226 -0
  58. package/apps/macos/Sources/Remux/SocketController.swift +258 -0
  59. package/apps/macos/Sources/Remux/UpdateChecker.swift +152 -0
  60. package/apps/macos/Sources/Remux/Views/CommandPalette.swift +198 -0
  61. package/apps/macos/Sources/Remux/Views/ConnectionView.swift +84 -0
  62. package/apps/macos/Sources/Remux/Views/InspectView.swift +127 -0
  63. package/apps/macos/Sources/Remux/Views/SettingsView.swift +77 -0
  64. package/apps/macos/Sources/Remux/Views/Sidebar/SidebarView.swift +410 -0
  65. package/apps/macos/Sources/Remux/Views/SplitTree/BrowserPanel.swift +193 -0
  66. package/apps/macos/Sources/Remux/Views/SplitTree/MarkdownPanel.swift +277 -0
  67. package/apps/macos/Sources/Remux/Views/SplitTree/PanelProtocol.swift +14 -0
  68. package/apps/macos/Sources/Remux/Views/SplitTree/SplitNode.swift +149 -0
  69. package/apps/macos/Sources/Remux/Views/SplitTree/SplitView.swift +234 -0
  70. package/apps/macos/Sources/Remux/Views/SplitTree/TerminalPanel.swift +26 -0
  71. package/apps/macos/Sources/Remux/Views/TabBarView.swift +94 -0
  72. package/apps/macos/Sources/Remux/Views/Terminal/ClipboardHelper.swift +101 -0
  73. package/apps/macos/Sources/Remux/Views/Terminal/CopyModeOverlay.swift +325 -0
  74. package/apps/macos/Sources/Remux/Views/Terminal/GhosttyNativeTerminalView.swift +39 -0
  75. package/apps/macos/Sources/Remux/Views/Terminal/GhosttyNativeView.swift +559 -0
  76. package/apps/macos/Sources/Remux/Views/Terminal/SurfaceSearchOverlay.swift +109 -0
  77. package/apps/macos/Sources/Remux/Views/Terminal/TerminalContainerView.swift +95 -0
  78. package/apps/macos/Sources/Remux/Views/Terminal/TerminalRelay.swift +117 -0
  79. package/build.mjs +33 -0
  80. package/native/android/DecodeGoldenPayloads.kt +487 -0
  81. package/native/android/ProtocolModels.kt +188 -0
  82. package/native/ios/DecodeGoldenPayloads.swift +711 -0
  83. package/native/ios/ProtocolModels.swift +200 -0
  84. package/package.json +45 -0
  85. package/packages/RemuxKit/Package.swift +27 -0
  86. package/packages/RemuxKit/Sources/RemuxKit/Device/DeviceManager.swift +27 -0
  87. package/packages/RemuxKit/Sources/RemuxKit/Models/ProtocolModels.swift +206 -0
  88. package/packages/RemuxKit/Sources/RemuxKit/Networking/MessageRouter.swift +108 -0
  89. package/packages/RemuxKit/Sources/RemuxKit/Networking/RemuxConnection.swift +395 -0
  90. package/packages/RemuxKit/Sources/RemuxKit/State/RemuxState.swift +188 -0
  91. package/packages/RemuxKit/Sources/RemuxKit/Storage/KeychainStore.swift +142 -0
  92. package/packages/RemuxKit/Sources/RemuxKit/Terminal/GhosttyBridge.swift +145 -0
  93. package/packages/RemuxKit/Sources/RemuxKit/Terminal/GhosttyTerminalView.swift +35 -0
  94. package/packages/RemuxKit/Sources/RemuxKit/Terminal/Resources/ghostty-terminal.html +91 -0
  95. package/packages/RemuxKit/Tests/RemuxKitTests/ConnectionIntegrationTest.swift +74 -0
  96. package/packages/RemuxKit/Tests/RemuxKitTests/KeychainStoreTests.swift +81 -0
  97. package/packages/RemuxKit/Tests/RemuxKitTests/ProtocolModelsTests.swift +179 -0
  98. package/packages/RemuxKit/Tests/RemuxKitTests/RemuxStateTests.swift +62 -0
  99. package/playwright.config.ts +17 -0
  100. package/pnpm-lock.yaml +1588 -0
  101. package/pty-daemon.js +303 -0
  102. package/release-please-config.json +14 -0
  103. package/scripts/auto-deploy.sh +46 -0
  104. package/scripts/build-dmg.sh +121 -0
  105. package/scripts/build-ghostty-kit.sh +43 -0
  106. package/scripts/check-active-terminology.mjs +132 -0
  107. package/scripts/sync-ghostty-web.sh +28 -0
  108. package/server.js +7074 -0
  109. package/src/adapters/agent-events.ts +246 -0
  110. package/src/adapters/claude-code.ts +158 -0
  111. package/src/adapters/codex.ts +210 -0
  112. package/src/adapters/generic-shell.ts +58 -0
  113. package/src/adapters/index.ts +15 -0
  114. package/src/adapters/registry.ts +99 -0
  115. package/src/adapters/types.ts +41 -0
  116. package/src/auth.ts +174 -0
  117. package/src/e2ee.ts +236 -0
  118. package/src/git-service.ts +168 -0
  119. package/src/message-buffer.ts +137 -0
  120. package/src/pty-daemon.ts +357 -0
  121. package/src/push.ts +127 -0
  122. package/src/renderers.ts +455 -0
  123. package/src/server.ts +2407 -0
  124. package/src/service.ts +226 -0
  125. package/src/session.ts +978 -0
  126. package/src/store.ts +1422 -0
  127. package/src/team.ts +123 -0
  128. package/src/tunnel.ts +126 -0
  129. package/src/types.d.ts +50 -0
  130. package/src/vt-tracker.ts +188 -0
  131. package/src/workspace-head.ts +144 -0
  132. package/src/workspace.ts +153 -0
  133. package/src/ws-handler.ts +1526 -0
  134. package/start.ps1 +83 -0
  135. package/tests/adapters.test.js +171 -0
  136. package/tests/auth.test.js +243 -0
  137. package/tests/codex-adapter.test.js +535 -0
  138. package/tests/durable-stream.test.js +153 -0
  139. package/tests/e2e/app.spec.js +530 -0
  140. package/tests/e2ee.test.js +325 -0
  141. package/tests/message-buffer.test.js +245 -0
  142. package/tests/message-routing.test.js +305 -0
  143. package/tests/pty-daemon.test.js +346 -0
  144. package/tests/push.test.js +281 -0
  145. package/tests/renderers.test.js +391 -0
  146. package/tests/search-shell.test.js +499 -0
  147. package/tests/server.test.js +882 -0
  148. package/tests/service.test.js +267 -0
  149. package/tests/store.test.js +369 -0
  150. package/tests/tunnel.test.js +67 -0
  151. package/tests/workspace-head.test.js +116 -0
  152. package/tests/workspace.test.js +417 -0
  153. package/tsconfig.backend.json +11 -0
  154. package/tsconfig.json +15 -0
  155. package/tui/client/client_test.go +125 -0
  156. package/tui/client/connection.go +342 -0
  157. package/tui/client/host_manager.go +141 -0
  158. package/tui/config/cache.go +81 -0
  159. package/tui/config/config.go +53 -0
  160. package/tui/config/config_test.go +89 -0
  161. package/tui/go.mod +32 -0
  162. package/tui/go.sum +50 -0
  163. package/tui/main.go +261 -0
  164. package/tui/tests/integration_test.go +283 -0
  165. package/tui/ui/model.go +310 -0
  166. package/vitest.config.js +10 -0
@@ -0,0 +1,179 @@
1
+ import Testing
2
+ import Foundation
3
+ @testable import RemuxKit
4
+
5
+ @Suite("Protocol Models")
6
+ struct ProtocolModelsTests {
7
+
8
+ @Test("Decode auth_ok envelope")
9
+ func decodeAuthOkEnvelope() throws {
10
+ let json = """
11
+ {
12
+ "v": 1,
13
+ "type": "auth_ok",
14
+ "domain": "core",
15
+ "emittedAt": "2026-04-01T00:00:00Z",
16
+ "source": "server",
17
+ "requestId": null,
18
+ "payload": {
19
+ "capabilities": {
20
+ "envelope": true,
21
+ "inspectV2": true,
22
+ "deviceTrust": true
23
+ }
24
+ }
25
+ }
26
+ """
27
+ let data = Data(json.utf8)
28
+ let envelope = try JSONDecoder().decode(RemuxEnvelope<AuthOkPayload>.self, from: data)
29
+ #expect(envelope.domain == "core")
30
+ #expect(envelope.type == "auth_ok")
31
+ #expect(envelope.version == 1)
32
+ #expect(envelope.payload.capabilities.envelope == true)
33
+ #expect(envelope.payload.capabilities.inspectV2 == true)
34
+ #expect(envelope.payload.capabilities.deviceTrust == true)
35
+ }
36
+
37
+ @Test("Decode workspace state")
38
+ func decodeWorkspaceState() throws {
39
+ let json = """
40
+ {
41
+ "session": "main",
42
+ "tabs": [
43
+ {
44
+ "index": 0,
45
+ "name": "zsh",
46
+ "active": true,
47
+ "isFullscreen": false,
48
+ "hasBell": false,
49
+ "panes": [
50
+ {
51
+ "id": "pane-1",
52
+ "focused": true,
53
+ "title": "zsh",
54
+ "command": null,
55
+ "cwd": "/Users/test",
56
+ "rows": 24,
57
+ "cols": 80,
58
+ "x": 0,
59
+ "y": 0
60
+ }
61
+ ]
62
+ }
63
+ ],
64
+ "activeTabIndex": 0
65
+ }
66
+ """
67
+ let data = Data(json.utf8)
68
+ let state = try JSONDecoder().decode(WorkspaceState.self, from: data)
69
+ #expect(state.session == "main")
70
+ #expect(state.tabs.count == 1)
71
+ #expect(state.tabs[0].name == "zsh")
72
+ #expect(state.tabs[0].panes[0].id == "pane-1")
73
+ #expect(state.tabs[0].panes[0].cols == 80)
74
+ }
75
+
76
+ @Test("Decode inspect snapshot")
77
+ func decodeInspectSnapshot() throws {
78
+ let json = """
79
+ {
80
+ "descriptor": {
81
+ "scope": "tab",
82
+ "source": "state_tracker",
83
+ "precision": "precise",
84
+ "staleness": "fresh",
85
+ "capturedAt": "2026-04-01T00:00:00Z",
86
+ "paneId": null,
87
+ "tabIndex": 0,
88
+ "totalItems": 3
89
+ },
90
+ "items": [
91
+ {
92
+ "type": "output",
93
+ "content": "$ ls",
94
+ "lineNumber": 1,
95
+ "timestamp": "2026-04-01T00:00:00Z",
96
+ "paneId": "pane-1",
97
+ "highlights": null
98
+ }
99
+ ],
100
+ "cursor": null,
101
+ "truncated": false
102
+ }
103
+ """
104
+ let data = Data(json.utf8)
105
+ let snapshot = try JSONDecoder().decode(InspectSnapshot.self, from: data)
106
+ #expect(snapshot.descriptor.scope == "tab")
107
+ #expect(snapshot.descriptor.precision == "precise")
108
+ #expect(snapshot.items.count == 1)
109
+ #expect(snapshot.items[0].content == "$ ls")
110
+ #expect(snapshot.truncated == false)
111
+ }
112
+ }
113
+
114
+ @Suite("Message Router")
115
+ struct MessageRouterTests {
116
+
117
+ @Test("Route workspace state message")
118
+ func routeStateMessage() throws {
119
+ let router = MessageRouter()
120
+ let json = """
121
+ {"v":1,"type":"state","domain":"runtime","emittedAt":"2026-04-01T00:00:00Z","source":"server","payload":{"session":"main","tabs":[],"activeTabIndex":0}}
122
+ """
123
+ let result = router.route(json)
124
+ guard case .state(let state) = result else {
125
+ Issue.record("Expected .state, got \(String(describing: result))")
126
+ return
127
+ }
128
+ #expect(state.session == "main")
129
+ }
130
+
131
+ @Test("Route legacy state message")
132
+ func routeLegacyStateMessage() throws {
133
+ let router = MessageRouter()
134
+ let json = """
135
+ {"type":"state","session":"dev","tabs":[],"activeTabIndex":0}
136
+ """
137
+ let result = router.route(json)
138
+ guard case .state(let state) = result else {
139
+ Issue.record("Expected .state, got \(String(describing: result))")
140
+ return
141
+ }
142
+ #expect(state.session == "dev")
143
+ }
144
+
145
+ @Test("Route role_changed message")
146
+ func routeRoleChanged() throws {
147
+ let router = MessageRouter()
148
+ let json = """
149
+ {"v":1,"type":"role_changed","domain":"core","emittedAt":"","source":"server","payload":{"role":"observer"}}
150
+ """
151
+ let result = router.route(json)
152
+ guard case .roleChanged(let role) = result else {
153
+ Issue.record("Expected .roleChanged")
154
+ return
155
+ }
156
+ #expect(role == "observer")
157
+ }
158
+
159
+ @Test("Route unknown message type returns .unknown")
160
+ func routeUnknownType() throws {
161
+ let router = MessageRouter()
162
+ let json = """
163
+ {"type":"future_message","foo":"bar"}
164
+ """
165
+ let result = router.route(json)
166
+ guard case .unknown(let type, _) = result else {
167
+ Issue.record("Expected .unknown")
168
+ return
169
+ }
170
+ #expect(type == "future_message")
171
+ }
172
+
173
+ @Test("Invalid JSON returns nil")
174
+ func routeInvalidJSON() throws {
175
+ let router = MessageRouter()
176
+ let result = router.route("not json")
177
+ #expect(result == nil)
178
+ }
179
+ }
@@ -0,0 +1,62 @@
1
+ import Testing
2
+ import Foundation
3
+ @testable import RemuxKit
4
+
5
+ @Suite("RemuxState")
6
+ struct RemuxStateTests {
7
+
8
+ @Test("Initial state is disconnected")
9
+ @MainActor
10
+ func initialState() {
11
+ let state = RemuxState()
12
+ #expect(state.connectionStatus == .disconnected)
13
+ #expect(state.currentSession == "")
14
+ #expect(state.tabs.isEmpty)
15
+ #expect(state.clientRole == "active")
16
+ }
17
+
18
+ @Test("Process workspace state message")
19
+ @MainActor
20
+ func processWorkspaceState() {
21
+ let state = RemuxState()
22
+ let json = """
23
+ {"type":"state","session":"main","tabs":[{"index":0,"name":"zsh","active":true,"isFullscreen":false,"hasBell":false,"panes":[{"id":"p1","focused":true,"title":"zsh","command":null,"cwd":"/tmp","rows":24,"cols":80,"x":0,"y":0}]}],"activeTabIndex":0}
24
+ """
25
+ state.connectionDidReceiveMessage(json)
26
+ #expect(state.currentSession == "main")
27
+ #expect(state.tabs.count == 1)
28
+ #expect(state.tabs[0].name == "zsh")
29
+ }
30
+
31
+ @Test("Process role_changed message")
32
+ @MainActor
33
+ func processRoleChanged() {
34
+ let state = RemuxState()
35
+ let json = """
36
+ {"type":"role_changed","role":"observer"}
37
+ """
38
+ state.connectionDidReceiveMessage(json)
39
+ #expect(state.clientRole == "observer")
40
+ }
41
+
42
+ @Test("Process inspect_result message")
43
+ @MainActor
44
+ func processInspectResult() {
45
+ let state = RemuxState()
46
+ let json = """
47
+ {"type":"inspect_result","descriptor":{"scope":"tab","source":"state_tracker","precision":"precise","staleness":"fresh","capturedAt":"2026-04-01T00:00:00Z","paneId":null,"tabIndex":0,"totalItems":1},"items":[{"type":"output","content":"hello","lineNumber":1,"timestamp":"2026-04-01T00:00:00Z","paneId":"p1","highlights":null}],"cursor":null,"truncated":false}
48
+ """
49
+ state.connectionDidReceiveMessage(json)
50
+ #expect(state.inspectSnapshot != nil)
51
+ #expect(state.inspectSnapshot?.items.count == 1)
52
+ #expect(state.inspectSnapshot?.items[0].content == "hello")
53
+ }
54
+
55
+ @Test("ConnectionStatus is Equatable")
56
+ func statusEquatable() {
57
+ #expect(ConnectionStatus.connected == ConnectionStatus.connected)
58
+ #expect(ConnectionStatus.disconnected != ConnectionStatus.connected)
59
+ #expect(ConnectionStatus.reconnecting(attempt: 1) == ConnectionStatus.reconnecting(attempt: 1))
60
+ #expect(ConnectionStatus.reconnecting(attempt: 1) != ConnectionStatus.reconnecting(attempt: 2))
61
+ }
62
+ }
@@ -0,0 +1,17 @@
1
+ import { defineConfig } from "@playwright/test";
2
+
3
+ export default defineConfig({
4
+ testDir: "tests/e2e",
5
+ testIgnore: ["tests/e2e/legacy/**"],
6
+ timeout: 30_000,
7
+ fullyParallel: false,
8
+ workers: 1,
9
+ use: {
10
+ browserName: "chromium",
11
+ headless: true,
12
+ viewport: { width: 1280, height: 720 },
13
+ trace: "retain-on-failure",
14
+ screenshot: "only-on-failure",
15
+ video: "retain-on-failure",
16
+ },
17
+ });