@wangyaoshen/remux 0.3.8-dev.a8ceb0c

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 (183) 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 +312 -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 +477 -0
  19. package/apps/ios/Remux.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  20. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/Contents.json +23 -0
  21. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_1024x1024.png +0 -0
  22. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_120x120.png +0 -0
  23. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_152x152.png +0 -0
  24. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_167x167.png +0 -0
  25. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_180x180.png +0 -0
  26. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_20x20.png +0 -0
  27. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_29x29.png +0 -0
  28. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_40x40.png +0 -0
  29. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_58x58.png +0 -0
  30. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_60x60.png +0 -0
  31. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_76x76.png +0 -0
  32. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_80x80.png +0 -0
  33. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_87x87.png +0 -0
  34. package/apps/ios/Sources/Remux/Assets.xcassets/Contents.json +6 -0
  35. package/apps/ios/Sources/Remux/Extensions/FaceIDManager.swift +29 -0
  36. package/apps/ios/Sources/Remux/Extensions/InspectCache.swift +66 -0
  37. package/apps/ios/Sources/Remux/MainTabView.swift +32 -0
  38. package/apps/ios/Sources/Remux/Remux.entitlements +8 -0
  39. package/apps/ios/Sources/Remux/RemuxiOSApp.swift +14 -0
  40. package/apps/ios/Sources/Remux/RootView.swift +130 -0
  41. package/apps/ios/Sources/Remux/Views/Control/ControlView.swift +102 -0
  42. package/apps/ios/Sources/Remux/Views/Inspect/InspectView.swift +98 -0
  43. package/apps/ios/Sources/Remux/Views/Live/LiveTerminalView.swift +132 -0
  44. package/apps/ios/Sources/Remux/Views/Now/NowView.swift +173 -0
  45. package/apps/ios/Sources/Remux/Views/Onboarding/ManualConnectView.swift +55 -0
  46. package/apps/ios/Sources/Remux/Views/Onboarding/OnboardingView.swift +70 -0
  47. package/apps/ios/Sources/Remux/Views/Onboarding/QRScannerView.swift +92 -0
  48. package/apps/ios/Sources/Remux/Views/Settings/MeView.swift +136 -0
  49. package/apps/macos/Package.swift +37 -0
  50. package/apps/macos/Resources/shell-integration/bash/bash-preexec.sh +382 -0
  51. package/apps/macos/Resources/shell-integration/bash/ghostty.bash +315 -0
  52. package/apps/macos/Resources/shell-integration/elvish/lib/ghostty-integration.elv +191 -0
  53. package/apps/macos/Resources/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish +246 -0
  54. package/apps/macos/Resources/shell-integration/nushell/vendor/autoload/ghostty.nu +110 -0
  55. package/apps/macos/Resources/shell-integration/zsh/.zshenv +61 -0
  56. package/apps/macos/Resources/shell-integration/zsh/ghostty-integration +458 -0
  57. package/apps/macos/Resources/terminfo/67/ghostty +0 -0
  58. package/apps/macos/Resources/terminfo/78/xterm-ghostty +0 -0
  59. package/apps/macos/Sources/Remux/AppDelegate.swift +257 -0
  60. package/apps/macos/Sources/Remux/CrashReporter.swift +210 -0
  61. package/apps/macos/Sources/Remux/FinderIntegration.swift +117 -0
  62. package/apps/macos/Sources/Remux/GhosttyConfig.swift +311 -0
  63. package/apps/macos/Sources/Remux/KeyboardShortcuts/ShortcutAction.swift +115 -0
  64. package/apps/macos/Sources/Remux/KeyboardShortcuts/ShortcutSettingsView.swift +271 -0
  65. package/apps/macos/Sources/Remux/KeyboardShortcuts/StoredShortcut.swift +149 -0
  66. package/apps/macos/Sources/Remux/MainContentView.swift +308 -0
  67. package/apps/macos/Sources/Remux/MenuBarManager.swift +275 -0
  68. package/apps/macos/Sources/Remux/NotificationManager.swift +145 -0
  69. package/apps/macos/Sources/Remux/PortScanner.swift +152 -0
  70. package/apps/macos/Sources/Remux/RemuxApp.swift +13 -0
  71. package/apps/macos/Sources/Remux/SSHDetector.swift +151 -0
  72. package/apps/macos/Sources/Remux/SessionPersistence.swift +226 -0
  73. package/apps/macos/Sources/Remux/SocketController.swift +258 -0
  74. package/apps/macos/Sources/Remux/UpdateChecker.swift +152 -0
  75. package/apps/macos/Sources/Remux/Views/CommandPalette.swift +198 -0
  76. package/apps/macos/Sources/Remux/Views/ConnectionView.swift +84 -0
  77. package/apps/macos/Sources/Remux/Views/InspectView.swift +127 -0
  78. package/apps/macos/Sources/Remux/Views/SettingsView.swift +77 -0
  79. package/apps/macos/Sources/Remux/Views/Sidebar/SidebarView.swift +410 -0
  80. package/apps/macos/Sources/Remux/Views/SplitTree/BrowserPanel.swift +193 -0
  81. package/apps/macos/Sources/Remux/Views/SplitTree/MarkdownPanel.swift +277 -0
  82. package/apps/macos/Sources/Remux/Views/SplitTree/PanelProtocol.swift +14 -0
  83. package/apps/macos/Sources/Remux/Views/SplitTree/SplitNode.swift +149 -0
  84. package/apps/macos/Sources/Remux/Views/SplitTree/SplitView.swift +234 -0
  85. package/apps/macos/Sources/Remux/Views/SplitTree/TerminalPanel.swift +26 -0
  86. package/apps/macos/Sources/Remux/Views/TabBarView.swift +94 -0
  87. package/apps/macos/Sources/Remux/Views/Terminal/ClipboardHelper.swift +101 -0
  88. package/apps/macos/Sources/Remux/Views/Terminal/CopyModeOverlay.swift +325 -0
  89. package/apps/macos/Sources/Remux/Views/Terminal/GhosttyNativeTerminalView.swift +39 -0
  90. package/apps/macos/Sources/Remux/Views/Terminal/GhosttyNativeView.swift +559 -0
  91. package/apps/macos/Sources/Remux/Views/Terminal/SurfaceSearchOverlay.swift +109 -0
  92. package/apps/macos/Sources/Remux/Views/Terminal/TerminalContainerView.swift +95 -0
  93. package/apps/macos/Sources/Remux/Views/Terminal/TerminalRelay.swift +117 -0
  94. package/build.mjs +33 -0
  95. package/native/android/DecodeGoldenPayloads.kt +487 -0
  96. package/native/android/ProtocolModels.kt +188 -0
  97. package/native/ios/DecodeGoldenPayloads.swift +711 -0
  98. package/native/ios/ProtocolModels.swift +200 -0
  99. package/package.json +45 -0
  100. package/packages/RemuxKit/Package.swift +27 -0
  101. package/packages/RemuxKit/Sources/RemuxKit/Device/DeviceManager.swift +27 -0
  102. package/packages/RemuxKit/Sources/RemuxKit/Models/ProtocolModels.swift +206 -0
  103. package/packages/RemuxKit/Sources/RemuxKit/Networking/MessageRouter.swift +108 -0
  104. package/packages/RemuxKit/Sources/RemuxKit/Networking/RemuxConnection.swift +395 -0
  105. package/packages/RemuxKit/Sources/RemuxKit/State/RemuxState.swift +188 -0
  106. package/packages/RemuxKit/Sources/RemuxKit/Storage/KeychainStore.swift +142 -0
  107. package/packages/RemuxKit/Sources/RemuxKit/Terminal/GhosttyBridge.swift +145 -0
  108. package/packages/RemuxKit/Sources/RemuxKit/Terminal/GhosttyTerminalView.swift +35 -0
  109. package/packages/RemuxKit/Sources/RemuxKit/Terminal/Resources/ghostty-terminal.html +91 -0
  110. package/packages/RemuxKit/Tests/RemuxKitTests/ConnectionIntegrationTest.swift +74 -0
  111. package/packages/RemuxKit/Tests/RemuxKitTests/KeychainStoreTests.swift +81 -0
  112. package/packages/RemuxKit/Tests/RemuxKitTests/ProtocolModelsTests.swift +179 -0
  113. package/packages/RemuxKit/Tests/RemuxKitTests/RemuxStateTests.swift +62 -0
  114. package/playwright.config.ts +17 -0
  115. package/pnpm-lock.yaml +1588 -0
  116. package/pty-daemon.js +303 -0
  117. package/release-please-config.json +14 -0
  118. package/scripts/auto-deploy.sh +46 -0
  119. package/scripts/build-dmg.sh +121 -0
  120. package/scripts/build-ghostty-kit.sh +43 -0
  121. package/scripts/check-active-terminology.mjs +132 -0
  122. package/scripts/setup-ci-secrets.sh +80 -0
  123. package/scripts/sync-ghostty-web.sh +28 -0
  124. package/scripts/upload-testflight.sh +100 -0
  125. package/server.js +7074 -0
  126. package/src/adapters/agent-events.ts +246 -0
  127. package/src/adapters/claude-code.ts +158 -0
  128. package/src/adapters/codex.ts +210 -0
  129. package/src/adapters/generic-shell.ts +58 -0
  130. package/src/adapters/index.ts +15 -0
  131. package/src/adapters/registry.ts +99 -0
  132. package/src/adapters/types.ts +41 -0
  133. package/src/auth.ts +174 -0
  134. package/src/e2ee.ts +236 -0
  135. package/src/git-service.ts +168 -0
  136. package/src/message-buffer.ts +137 -0
  137. package/src/pty-daemon.ts +357 -0
  138. package/src/push.ts +127 -0
  139. package/src/renderers.ts +455 -0
  140. package/src/server.ts +2407 -0
  141. package/src/service.ts +226 -0
  142. package/src/session.ts +978 -0
  143. package/src/store.ts +1422 -0
  144. package/src/team.ts +123 -0
  145. package/src/tunnel.ts +126 -0
  146. package/src/types.d.ts +50 -0
  147. package/src/vt-tracker.ts +188 -0
  148. package/src/workspace-head.ts +144 -0
  149. package/src/workspace.ts +153 -0
  150. package/src/ws-handler.ts +1526 -0
  151. package/start.ps1 +83 -0
  152. package/tests/adapters.test.js +171 -0
  153. package/tests/auth.test.js +243 -0
  154. package/tests/codex-adapter.test.js +535 -0
  155. package/tests/durable-stream.test.js +153 -0
  156. package/tests/e2e/app.spec.js +530 -0
  157. package/tests/e2ee.test.js +325 -0
  158. package/tests/message-buffer.test.js +245 -0
  159. package/tests/message-routing.test.js +305 -0
  160. package/tests/pty-daemon.test.js +346 -0
  161. package/tests/push.test.js +281 -0
  162. package/tests/renderers.test.js +391 -0
  163. package/tests/search-shell.test.js +499 -0
  164. package/tests/server.test.js +882 -0
  165. package/tests/service.test.js +267 -0
  166. package/tests/store.test.js +369 -0
  167. package/tests/tunnel.test.js +67 -0
  168. package/tests/workspace-head.test.js +116 -0
  169. package/tests/workspace.test.js +417 -0
  170. package/tsconfig.backend.json +11 -0
  171. package/tsconfig.json +15 -0
  172. package/tui/client/client_test.go +125 -0
  173. package/tui/client/connection.go +342 -0
  174. package/tui/client/host_manager.go +141 -0
  175. package/tui/config/cache.go +81 -0
  176. package/tui/config/config.go +53 -0
  177. package/tui/config/config_test.go +89 -0
  178. package/tui/go.mod +32 -0
  179. package/tui/go.sum +50 -0
  180. package/tui/main.go +261 -0
  181. package/tui/tests/integration_test.go +283 -0
  182. package/tui/ui/model.go +310 -0
  183. package/vitest.config.js +10 -0
@@ -0,0 +1,95 @@
1
+ import SwiftUI
2
+ import RemuxKit
3
+
4
+ /// Container connecting GhosttyNativeTerminalView to RemuxState via TerminalRelay.
5
+ /// Data flow:
6
+ /// Remote PTY -> WebSocket -> RemuxState -> relay.writeToTerminal() -> socket -> nc stdout -> ghostty Metal render
7
+ /// User types -> ghostty -> nc stdin -> socket -> relay.onDataFromClient -> state.sendTerminalData() -> WebSocket -> remote PTY
8
+ struct TerminalContainerView: View {
9
+ @Environment(RemuxState.self) private var state
10
+ @State private var terminalView: GhosttyNativeView?
11
+ @State private var relay = TerminalRelay()
12
+
13
+ // Search state
14
+ @State private var searchVisible = false
15
+ @State private var searchText = ""
16
+ @State private var searchTotal = 0
17
+ @State private var searchSelected = -1
18
+
19
+ var body: some View {
20
+ ZStack(alignment: .topTrailing) {
21
+ GhosttyNativeTerminalView(
22
+ socketPath: relay.socketPath,
23
+ viewRef: $terminalView,
24
+ onResize: { cols, rows in
25
+ state.sendJSON(["type": "resize", "cols": cols, "rows": rows])
26
+ },
27
+ onBell: {},
28
+ onTitle: { title in
29
+ NSApp.mainWindow?.title = "Remux — \(title)"
30
+ },
31
+ onSearchStart: { needle in
32
+ searchVisible = true
33
+ if let needle, !needle.isEmpty {
34
+ searchText = needle
35
+ }
36
+ },
37
+ onSearchEnd: {
38
+ searchVisible = false
39
+ searchText = ""
40
+ searchTotal = 0
41
+ searchSelected = -1
42
+ },
43
+ onSearchTotal: { total in
44
+ searchTotal = total
45
+ },
46
+ onSearchSelected: { selected in
47
+ searchSelected = selected
48
+ }
49
+ )
50
+
51
+ SurfaceSearchOverlay(
52
+ isVisible: $searchVisible,
53
+ searchText: $searchText,
54
+ totalMatches: $searchTotal,
55
+ selectedMatch: $searchSelected,
56
+ onSearch: { query in
57
+ guard let view = terminalView else { return }
58
+ if query.isEmpty {
59
+ searchTotal = 0
60
+ searchSelected = -1
61
+ }
62
+ // Ghostty search is triggered by typing into the search bar,
63
+ // which sends the text via binding_action
64
+ let action = "search:\(query)"
65
+ view.sendText("") // ensure surface has focus
66
+ _ = action // search is driven by the overlay text field
67
+ },
68
+ onNext: {
69
+ terminalView?.searchForward()
70
+ },
71
+ onPrevious: {
72
+ terminalView?.searchBackward()
73
+ },
74
+ onClose: {
75
+ searchTotal = 0
76
+ searchSelected = -1
77
+ }
78
+ )
79
+ }
80
+ .onReceive(NotificationCenter.default.publisher(for: .remuxTerminalData)) { notification in
81
+ if let data = notification.userInfo?["data"] as? Data {
82
+ relay.writeToTerminal(data)
83
+ }
84
+ }
85
+ .onAppear {
86
+ relay.onDataFromClient = { data in
87
+ state.sendTerminalData(data)
88
+ }
89
+ relay.start()
90
+ }
91
+ .onDisappear {
92
+ relay.stop()
93
+ }
94
+ }
95
+ }
@@ -0,0 +1,117 @@
1
+ import Foundation
2
+
3
+ /// Bidirectional relay between a Unix domain socket and the WebSocket connection.
4
+ /// Ghostty spawns `nc -U <socketPath>` as its "shell" process.
5
+ /// Data flow:
6
+ /// Remote PTY → WebSocket → onDataFromServer → socket → nc stdout → ghostty renders
7
+ /// User types → ghostty → nc stdin → socket → onDataFromClient → WebSocket → remote PTY
8
+ @MainActor
9
+ final class TerminalRelay {
10
+
11
+ let socketPath: String
12
+ var onDataFromClient: ((Data) -> Void)? // user keystrokes → send to WebSocket
13
+
14
+ private var serverSocket: Int32 = -1
15
+ private var clientSocket: Int32 = -1
16
+ private var readSource: DispatchSourceRead?
17
+ private let queue = DispatchQueue(label: "remux.terminal-relay")
18
+
19
+ init() {
20
+ socketPath = NSTemporaryDirectory() + "remux-relay-\(ProcessInfo.processInfo.processIdentifier)-\(UUID().uuidString.prefix(8)).sock"
21
+ }
22
+
23
+ deinit {
24
+ stop()
25
+ }
26
+
27
+ /// Start listening on the Unix socket. Call before creating the ghostty surface.
28
+ func start() {
29
+ // Create Unix domain socket
30
+ serverSocket = socket(AF_UNIX, SOCK_STREAM, 0)
31
+ guard serverSocket >= 0 else { return }
32
+
33
+ // Remove stale socket file
34
+ unlink(socketPath)
35
+
36
+ var addr = sockaddr_un()
37
+ addr.sun_family = sa_family_t(AF_UNIX)
38
+ let pathBytes = socketPath.utf8CString
39
+ pathBytes.withUnsafeBufferPointer { buf in
40
+ withUnsafeMutableBytes(of: &addr.sun_path) { rawPath in
41
+ let count = min(buf.count, rawPath.count)
42
+ rawPath.copyBytes(from: UnsafeRawBufferPointer(buf).prefix(count))
43
+ }
44
+ }
45
+
46
+ let bindResult = withUnsafePointer(to: &addr) { ptr in
47
+ ptr.withMemoryRebound(to: sockaddr.self, capacity: 1) { sockPtr in
48
+ Darwin.bind(serverSocket, sockPtr, socklen_t(MemoryLayout<sockaddr_un>.size))
49
+ }
50
+ }
51
+ guard bindResult == 0 else {
52
+ close(serverSocket); serverSocket = -1; return
53
+ }
54
+
55
+ listen(serverSocket, 1)
56
+
57
+ // Accept connection async (nc will connect when ghostty spawns)
58
+ let fd = serverSocket
59
+ queue.async { [weak self] in
60
+ let client = accept(fd, nil, nil)
61
+ guard client >= 0 else { return }
62
+ DispatchQueue.main.async {
63
+ self?.clientSocket = client
64
+ self?.startReading()
65
+ }
66
+ }
67
+ }
68
+
69
+ /// Write data to the socket → nc → ghostty PTY stdout → rendered on screen.
70
+ func writeToTerminal(_ data: Data) {
71
+ guard clientSocket >= 0 else { return }
72
+ data.withUnsafeBytes { ptr in
73
+ guard let base = ptr.baseAddress else { return }
74
+ _ = send(clientSocket, base, ptr.count, 0)
75
+ }
76
+ }
77
+
78
+ /// Stop the relay and clean up.
79
+ nonisolated func stop() {
80
+ MainActor.assumeIsolated {
81
+ readSource?.cancel()
82
+ readSource = nil
83
+ if clientSocket >= 0 { close(clientSocket); clientSocket = -1 }
84
+ if serverSocket >= 0 { close(serverSocket); serverSocket = -1 }
85
+ unlink(socketPath)
86
+ }
87
+ }
88
+
89
+ // MARK: - Private
90
+
91
+ private func startReading() {
92
+ guard clientSocket >= 0 else { return }
93
+ let source = DispatchSource.makeReadSource(fileDescriptor: clientSocket, queue: queue)
94
+ source.setEventHandler { [weak self] in
95
+ guard let self else { return }
96
+ var buf = [UInt8](repeating: 0, count: 65536)
97
+ let n = recv(self.clientSocket, &buf, buf.count, 0)
98
+ if n > 0 {
99
+ let data = Data(buf[0..<n])
100
+ DispatchQueue.main.async {
101
+ self.onDataFromClient?(data)
102
+ }
103
+ } else if n == 0 {
104
+ // nc disconnected
105
+ source.cancel()
106
+ }
107
+ }
108
+ source.setCancelHandler { [weak self] in
109
+ guard let self else { return }
110
+ DispatchQueue.main.async {
111
+ self.clientSocket = -1
112
+ }
113
+ }
114
+ source.resume()
115
+ readSource = source
116
+ }
117
+ }
package/build.mjs ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * esbuild script — bundles src/ TypeScript modules into output files.
3
+ * Two entry points:
4
+ * 1. server.ts → server.js (main server)
5
+ * 2. pty-daemon.ts → pty-daemon.js (independent PTY daemon process)
6
+ * Externalizes native/npm dependencies (node-pty, ws, ghostty-web, qrcode-terminal).
7
+ */
8
+
9
+ import esbuild from "esbuild";
10
+
11
+ const commonOptions = {
12
+ bundle: true,
13
+ platform: "node",
14
+ target: "node20",
15
+ format: "esm",
16
+ external: ["node-pty", "ws", "ghostty-web", "qrcode-terminal", "better-sqlite3", "web-push", "simple-git"],
17
+ };
18
+
19
+ // Main server bundle
20
+ esbuild.buildSync({
21
+ ...commonOptions,
22
+ entryPoints: ["src/server.ts"],
23
+ outfile: "server.js",
24
+ banner: { js: "#!/usr/bin/env node" },
25
+ });
26
+
27
+ // PTY daemon bundle (independent process)
28
+ esbuild.buildSync({
29
+ ...commonOptions,
30
+ entryPoints: ["src/pty-daemon.ts"],
31
+ outfile: "pty-daemon.js",
32
+ banner: { js: "#!/usr/bin/env node" },
33
+ });