@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,310 @@
1
+ // Package tui implements the terminal user interface using bubbletea.
2
+ package tui
3
+
4
+ import (
5
+ "fmt"
6
+ "strings"
7
+
8
+ tea "github.com/charmbracelet/bubbletea"
9
+ "github.com/charmbracelet/lipgloss"
10
+ "github.com/eisber/remux/tui/client"
11
+ )
12
+
13
+ // View mode for the TUI.
14
+ type viewMode int
15
+
16
+ const (
17
+ viewTerminal viewMode = iota
18
+ viewSessionPicker
19
+ )
20
+
21
+ // Model is the bubbletea model for the remux TUI.
22
+ type Model struct {
23
+ manager *client.HostManager
24
+ activeHost string
25
+ activeSession string
26
+ mode viewMode
27
+ width, height int
28
+
29
+ // Terminal output buffer (raw bytes from the remote PTY).
30
+ terminalOutput strings.Builder
31
+
32
+ // Session picker state.
33
+ pickerCursor int
34
+
35
+ // Status messages.
36
+ statusMsg string
37
+ errMsg string
38
+
39
+ // Whether we've received terminal data.
40
+ connected bool
41
+ }
42
+
43
+ // terminalDataMsg is sent when terminal output arrives from the server.
44
+ type terminalDataMsg struct {
45
+ data []byte
46
+ }
47
+
48
+ // stateUpdateMsg is sent when the session list changes.
49
+ type stateUpdateMsg struct{}
50
+
51
+ // attachedMsg is sent when we've attached to a session.
52
+ type attachedMsg struct {
53
+ session string
54
+ }
55
+
56
+ // errMsg is sent on connection errors.
57
+ type connectionErrMsg struct {
58
+ err error
59
+ }
60
+
61
+ // NewModel creates a new TUI model.
62
+ func NewModel(manager *client.HostManager) Model {
63
+ return Model{
64
+ manager: manager,
65
+ mode: viewTerminal,
66
+ }
67
+ }
68
+
69
+ // Init implements tea.Model.
70
+ func (m Model) Init() tea.Cmd {
71
+ return tea.SetWindowTitle("remux")
72
+ }
73
+
74
+ // Update implements tea.Model.
75
+ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
76
+ switch msg := msg.(type) {
77
+ case tea.KeyMsg:
78
+ return m.handleKey(msg)
79
+
80
+ case tea.WindowSizeMsg:
81
+ m.width = msg.Width
82
+ m.height = msg.Height
83
+ // Resize the active terminal session.
84
+ if m.activeHost != "" {
85
+ conn := m.manager.GetConnection(m.activeHost)
86
+ if conn != nil {
87
+ _ = conn.SendResize(msg.Width, msg.Height-2) // Reserve 2 lines for status bar
88
+ }
89
+ }
90
+ return m, nil
91
+
92
+ case terminalDataMsg:
93
+ m.terminalOutput.Write(msg.data)
94
+ m.connected = true
95
+ return m, nil
96
+
97
+ case stateUpdateMsg:
98
+ return m, nil
99
+
100
+ case attachedMsg:
101
+ m.activeSession = msg.session
102
+ m.statusMsg = fmt.Sprintf("attached to %s", msg.session)
103
+ m.mode = viewTerminal
104
+ return m, nil
105
+
106
+ case connectionErrMsg:
107
+ m.errMsg = msg.err.Error()
108
+ return m, nil
109
+ }
110
+
111
+ return m, nil
112
+ }
113
+
114
+ func (m Model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
115
+ switch m.mode {
116
+ case viewTerminal:
117
+ return m.handleTerminalKey(msg)
118
+ case viewSessionPicker:
119
+ return m.handlePickerKey(msg)
120
+ }
121
+ return m, nil
122
+ }
123
+
124
+ func (m Model) handleTerminalKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
125
+ switch msg.String() {
126
+ case "ctrl+o":
127
+ // Open session picker.
128
+ m.mode = viewSessionPicker
129
+ m.pickerCursor = 0
130
+ return m, nil
131
+
132
+ case "ctrl+d":
133
+ // Detach — quit the TUI (sessions keep running).
134
+ return m, tea.Quit
135
+
136
+ case "ctrl+c":
137
+ return m, tea.Quit
138
+
139
+ default:
140
+ // Forward all other input to the active terminal.
141
+ if m.activeHost != "" {
142
+ conn := m.manager.GetConnection(m.activeHost)
143
+ if conn != nil {
144
+ _ = conn.SendInput(msg.String())
145
+ }
146
+ }
147
+ return m, nil
148
+ }
149
+ }
150
+
151
+ func (m Model) handlePickerKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
152
+ sessions := m.manager.Sessions()
153
+
154
+ switch msg.String() {
155
+ case "up", "k":
156
+ if m.pickerCursor > 0 {
157
+ m.pickerCursor--
158
+ }
159
+ case "down", "j":
160
+ if m.pickerCursor < len(sessions)-1 {
161
+ m.pickerCursor++
162
+ }
163
+ case "enter":
164
+ if m.pickerCursor < len(sessions) {
165
+ selected := sessions[m.pickerCursor]
166
+ m.activeHost = selected.HostName
167
+ conn := m.manager.GetConnection(selected.HostName)
168
+ if conn != nil {
169
+ _ = conn.SelectSession(selected.Name)
170
+ }
171
+ m.mode = viewTerminal
172
+ m.terminalOutput.Reset()
173
+ m.statusMsg = fmt.Sprintf("switching to %s/%s...", selected.HostName, selected.Name)
174
+ }
175
+ case "escape", "ctrl+o":
176
+ m.mode = viewTerminal
177
+ case "ctrl+c", "ctrl+d":
178
+ return m, tea.Quit
179
+ }
180
+ return m, nil
181
+ }
182
+
183
+ // View implements tea.Model.
184
+ func (m Model) View() string {
185
+ if m.width == 0 {
186
+ return "initializing..."
187
+ }
188
+
189
+ switch m.mode {
190
+ case viewSessionPicker:
191
+ return m.renderPicker()
192
+ default:
193
+ return m.renderTerminal()
194
+ }
195
+ }
196
+
197
+ func (m Model) renderTerminal() string {
198
+ // Terminal content fills the screen minus the status bar.
199
+ contentHeight := m.height - 2
200
+ if contentHeight < 1 {
201
+ contentHeight = 1
202
+ }
203
+
204
+ // Get terminal output and take the last N lines.
205
+ output := m.terminalOutput.String()
206
+ lines := strings.Split(output, "\n")
207
+ if len(lines) > contentHeight {
208
+ lines = lines[len(lines)-contentHeight:]
209
+ }
210
+ content := strings.Join(lines, "\n")
211
+
212
+ // Status bar.
213
+ statusStyle := lipgloss.NewStyle().
214
+ Background(lipgloss.Color("236")).
215
+ Foreground(lipgloss.Color("252")).
216
+ Width(m.width).
217
+ Padding(0, 1)
218
+
219
+ var statusLeft string
220
+ if m.activeHost != "" && m.activeSession != "" {
221
+ statusLeft = fmt.Sprintf("⬤ %s/%s", m.activeHost, m.activeSession)
222
+ } else if m.errMsg != "" {
223
+ statusLeft = fmt.Sprintf("✗ %s", m.errMsg)
224
+ } else {
225
+ statusLeft = m.statusMsg
226
+ }
227
+ statusRight := "Ctrl-O: sessions │ Ctrl-D: detach"
228
+
229
+ padding := m.width - lipgloss.Width(statusLeft) - lipgloss.Width(statusRight) - 2
230
+ if padding < 1 {
231
+ padding = 1
232
+ }
233
+ statusBar := statusStyle.Render(statusLeft + strings.Repeat(" ", padding) + statusRight)
234
+
235
+ return content + "\n" + statusBar
236
+ }
237
+
238
+ func (m Model) renderPicker() string {
239
+ sessions := m.manager.Sessions()
240
+
241
+ titleStyle := lipgloss.NewStyle().
242
+ Bold(true).
243
+ Foreground(lipgloss.Color("212")).
244
+ Padding(1, 2)
245
+
246
+ itemStyle := lipgloss.NewStyle().Padding(0, 2)
247
+ selectedStyle := itemStyle.Background(lipgloss.Color("236")).Foreground(lipgloss.Color("212"))
248
+
249
+ var b strings.Builder
250
+ b.WriteString(titleStyle.Render("Sessions (↑/↓ select, Enter attach, Esc cancel)"))
251
+ b.WriteString("\n\n")
252
+
253
+ if len(sessions) == 0 {
254
+ b.WriteString(itemStyle.Render(" No sessions available"))
255
+ return b.String()
256
+ }
257
+
258
+ currentHost := ""
259
+ for i, s := range sessions {
260
+ // Group header for host.
261
+ if s.HostName != currentHost {
262
+ currentHost = s.HostName
263
+ headerStyle := lipgloss.NewStyle().
264
+ Bold(true).
265
+ Foreground(lipgloss.Color("245")).
266
+ Padding(0, 2)
267
+ b.WriteString(headerStyle.Render(fmt.Sprintf("── %s ──", currentHost)))
268
+ b.WriteString("\n")
269
+ }
270
+
271
+ prefix := " "
272
+ style := itemStyle
273
+ if i == m.pickerCursor {
274
+ prefix = "▸ "
275
+ style = selectedStyle
276
+ }
277
+
278
+ attached := ""
279
+ if s.Attached {
280
+ attached = " (attached)"
281
+ }
282
+
283
+ line := fmt.Sprintf("%s%s%s", prefix, s.Name, attached)
284
+ b.WriteString(style.Render(line))
285
+ b.WriteString("\n")
286
+ }
287
+
288
+ return b.String()
289
+ }
290
+
291
+ // SetupCallbacks wires the HostManager callbacks to bubbletea messages.
292
+ func SetupCallbacks(manager *client.HostManager, p *tea.Program) {
293
+ manager.OnChange(func() {
294
+ p.Send(stateUpdateMsg{})
295
+ })
296
+ }
297
+
298
+ // SetupConnectionCallbacks wires a Connection's callbacks to bubbletea messages.
299
+ func SetupConnectionCallbacks(conn *client.Connection, p *tea.Program) {
300
+ conn.OnTerminalData(func(data []byte) {
301
+ p.Send(terminalDataMsg{data: data})
302
+ })
303
+ conn.OnAttached(func(session string) {
304
+ p.Send(attachedMsg{session: session})
305
+ })
306
+ conn.OnError(func(err error) {
307
+ p.Send(connectionErrMsg{err: err})
308
+ })
309
+ }
310
+
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ testTimeout: 20000,
6
+ hookTimeout: 40000,
7
+ include: ["tests/**/*.test.{js,ts}"],
8
+ exclude: [".worktrees/**", "node_modules/**"],
9
+ },
10
+ });