@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.
- package/.github/ISSUE_TEMPLATE/bug_report.md +47 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +38 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +28 -0
- package/.github/dependabot.yml +33 -0
- package/.github/workflows/ci.yml +65 -0
- package/.github/workflows/deploy.yml +65 -0
- package/.github/workflows/publish.yml +138 -0
- package/.github/workflows/release-please.yml +21 -0
- package/.gitmodules +3 -0
- package/.nvmrc +1 -0
- package/.release-please-manifest.json +3 -0
- package/CLAUDE.md +104 -0
- package/Dockerfile +23 -0
- package/LICENSE +21 -0
- package/README.md +120 -0
- package/apps/ios/Config/signing.xcconfig +4 -0
- package/apps/ios/Package.swift +26 -0
- package/apps/ios/Remux.xcodeproj/project.pbxproj +456 -0
- package/apps/ios/Remux.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/apps/ios/Sources/Remux/Extensions/FaceIDManager.swift +29 -0
- package/apps/ios/Sources/Remux/Extensions/InspectCache.swift +66 -0
- package/apps/ios/Sources/Remux/MainTabView.swift +32 -0
- package/apps/ios/Sources/Remux/Remux.entitlements +8 -0
- package/apps/ios/Sources/Remux/RemuxiOSApp.swift +14 -0
- package/apps/ios/Sources/Remux/RootView.swift +130 -0
- package/apps/ios/Sources/Remux/Views/Control/ControlView.swift +102 -0
- package/apps/ios/Sources/Remux/Views/Inspect/InspectView.swift +98 -0
- package/apps/ios/Sources/Remux/Views/Live/LiveTerminalView.swift +132 -0
- package/apps/ios/Sources/Remux/Views/Now/NowView.swift +173 -0
- package/apps/ios/Sources/Remux/Views/Onboarding/ManualConnectView.swift +55 -0
- package/apps/ios/Sources/Remux/Views/Onboarding/OnboardingView.swift +70 -0
- package/apps/ios/Sources/Remux/Views/Onboarding/QRScannerView.swift +92 -0
- package/apps/ios/Sources/Remux/Views/Settings/MeView.swift +136 -0
- package/apps/macos/Package.swift +37 -0
- package/apps/macos/Resources/shell-integration/bash/bash-preexec.sh +382 -0
- package/apps/macos/Resources/shell-integration/bash/ghostty.bash +315 -0
- package/apps/macos/Resources/shell-integration/elvish/lib/ghostty-integration.elv +191 -0
- package/apps/macos/Resources/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish +246 -0
- package/apps/macos/Resources/shell-integration/nushell/vendor/autoload/ghostty.nu +110 -0
- package/apps/macos/Resources/shell-integration/zsh/.zshenv +61 -0
- package/apps/macos/Resources/shell-integration/zsh/ghostty-integration +458 -0
- package/apps/macos/Resources/terminfo/67/ghostty +0 -0
- package/apps/macos/Resources/terminfo/78/xterm-ghostty +0 -0
- package/apps/macos/Sources/Remux/AppDelegate.swift +257 -0
- package/apps/macos/Sources/Remux/CrashReporter.swift +210 -0
- package/apps/macos/Sources/Remux/FinderIntegration.swift +117 -0
- package/apps/macos/Sources/Remux/GhosttyConfig.swift +311 -0
- package/apps/macos/Sources/Remux/KeyboardShortcuts/ShortcutAction.swift +115 -0
- package/apps/macos/Sources/Remux/KeyboardShortcuts/ShortcutSettingsView.swift +271 -0
- package/apps/macos/Sources/Remux/KeyboardShortcuts/StoredShortcut.swift +149 -0
- package/apps/macos/Sources/Remux/MainContentView.swift +308 -0
- package/apps/macos/Sources/Remux/MenuBarManager.swift +275 -0
- package/apps/macos/Sources/Remux/NotificationManager.swift +145 -0
- package/apps/macos/Sources/Remux/PortScanner.swift +152 -0
- package/apps/macos/Sources/Remux/RemuxApp.swift +13 -0
- package/apps/macos/Sources/Remux/SSHDetector.swift +151 -0
- package/apps/macos/Sources/Remux/SessionPersistence.swift +226 -0
- package/apps/macos/Sources/Remux/SocketController.swift +258 -0
- package/apps/macos/Sources/Remux/UpdateChecker.swift +152 -0
- package/apps/macos/Sources/Remux/Views/CommandPalette.swift +198 -0
- package/apps/macos/Sources/Remux/Views/ConnectionView.swift +84 -0
- package/apps/macos/Sources/Remux/Views/InspectView.swift +127 -0
- package/apps/macos/Sources/Remux/Views/SettingsView.swift +77 -0
- package/apps/macos/Sources/Remux/Views/Sidebar/SidebarView.swift +410 -0
- package/apps/macos/Sources/Remux/Views/SplitTree/BrowserPanel.swift +193 -0
- package/apps/macos/Sources/Remux/Views/SplitTree/MarkdownPanel.swift +277 -0
- package/apps/macos/Sources/Remux/Views/SplitTree/PanelProtocol.swift +14 -0
- package/apps/macos/Sources/Remux/Views/SplitTree/SplitNode.swift +149 -0
- package/apps/macos/Sources/Remux/Views/SplitTree/SplitView.swift +234 -0
- package/apps/macos/Sources/Remux/Views/SplitTree/TerminalPanel.swift +26 -0
- package/apps/macos/Sources/Remux/Views/TabBarView.swift +94 -0
- package/apps/macos/Sources/Remux/Views/Terminal/ClipboardHelper.swift +101 -0
- package/apps/macos/Sources/Remux/Views/Terminal/CopyModeOverlay.swift +325 -0
- package/apps/macos/Sources/Remux/Views/Terminal/GhosttyNativeTerminalView.swift +39 -0
- package/apps/macos/Sources/Remux/Views/Terminal/GhosttyNativeView.swift +559 -0
- package/apps/macos/Sources/Remux/Views/Terminal/SurfaceSearchOverlay.swift +109 -0
- package/apps/macos/Sources/Remux/Views/Terminal/TerminalContainerView.swift +95 -0
- package/apps/macos/Sources/Remux/Views/Terminal/TerminalRelay.swift +117 -0
- package/build.mjs +33 -0
- package/native/android/DecodeGoldenPayloads.kt +487 -0
- package/native/android/ProtocolModels.kt +188 -0
- package/native/ios/DecodeGoldenPayloads.swift +711 -0
- package/native/ios/ProtocolModels.swift +200 -0
- package/package.json +45 -0
- package/packages/RemuxKit/Package.swift +27 -0
- package/packages/RemuxKit/Sources/RemuxKit/Device/DeviceManager.swift +27 -0
- package/packages/RemuxKit/Sources/RemuxKit/Models/ProtocolModels.swift +206 -0
- package/packages/RemuxKit/Sources/RemuxKit/Networking/MessageRouter.swift +108 -0
- package/packages/RemuxKit/Sources/RemuxKit/Networking/RemuxConnection.swift +395 -0
- package/packages/RemuxKit/Sources/RemuxKit/State/RemuxState.swift +188 -0
- package/packages/RemuxKit/Sources/RemuxKit/Storage/KeychainStore.swift +142 -0
- package/packages/RemuxKit/Sources/RemuxKit/Terminal/GhosttyBridge.swift +145 -0
- package/packages/RemuxKit/Sources/RemuxKit/Terminal/GhosttyTerminalView.swift +35 -0
- package/packages/RemuxKit/Sources/RemuxKit/Terminal/Resources/ghostty-terminal.html +91 -0
- package/packages/RemuxKit/Tests/RemuxKitTests/ConnectionIntegrationTest.swift +74 -0
- package/packages/RemuxKit/Tests/RemuxKitTests/KeychainStoreTests.swift +81 -0
- package/packages/RemuxKit/Tests/RemuxKitTests/ProtocolModelsTests.swift +179 -0
- package/packages/RemuxKit/Tests/RemuxKitTests/RemuxStateTests.swift +62 -0
- package/playwright.config.ts +17 -0
- package/pnpm-lock.yaml +1588 -0
- package/pty-daemon.js +303 -0
- package/release-please-config.json +14 -0
- package/scripts/auto-deploy.sh +46 -0
- package/scripts/build-dmg.sh +121 -0
- package/scripts/build-ghostty-kit.sh +43 -0
- package/scripts/check-active-terminology.mjs +132 -0
- package/scripts/sync-ghostty-web.sh +28 -0
- package/server.js +7074 -0
- package/src/adapters/agent-events.ts +246 -0
- package/src/adapters/claude-code.ts +158 -0
- package/src/adapters/codex.ts +210 -0
- package/src/adapters/generic-shell.ts +58 -0
- package/src/adapters/index.ts +15 -0
- package/src/adapters/registry.ts +99 -0
- package/src/adapters/types.ts +41 -0
- package/src/auth.ts +174 -0
- package/src/e2ee.ts +236 -0
- package/src/git-service.ts +168 -0
- package/src/message-buffer.ts +137 -0
- package/src/pty-daemon.ts +357 -0
- package/src/push.ts +127 -0
- package/src/renderers.ts +455 -0
- package/src/server.ts +2407 -0
- package/src/service.ts +226 -0
- package/src/session.ts +978 -0
- package/src/store.ts +1422 -0
- package/src/team.ts +123 -0
- package/src/tunnel.ts +126 -0
- package/src/types.d.ts +50 -0
- package/src/vt-tracker.ts +188 -0
- package/src/workspace-head.ts +144 -0
- package/src/workspace.ts +153 -0
- package/src/ws-handler.ts +1526 -0
- package/start.ps1 +83 -0
- package/tests/adapters.test.js +171 -0
- package/tests/auth.test.js +243 -0
- package/tests/codex-adapter.test.js +535 -0
- package/tests/durable-stream.test.js +153 -0
- package/tests/e2e/app.spec.js +530 -0
- package/tests/e2ee.test.js +325 -0
- package/tests/message-buffer.test.js +245 -0
- package/tests/message-routing.test.js +305 -0
- package/tests/pty-daemon.test.js +346 -0
- package/tests/push.test.js +281 -0
- package/tests/renderers.test.js +391 -0
- package/tests/search-shell.test.js +499 -0
- package/tests/server.test.js +882 -0
- package/tests/service.test.js +267 -0
- package/tests/store.test.js +369 -0
- package/tests/tunnel.test.js +67 -0
- package/tests/workspace-head.test.js +116 -0
- package/tests/workspace.test.js +417 -0
- package/tsconfig.backend.json +11 -0
- package/tsconfig.json +15 -0
- package/tui/client/client_test.go +125 -0
- package/tui/client/connection.go +342 -0
- package/tui/client/host_manager.go +141 -0
- package/tui/config/cache.go +81 -0
- package/tui/config/config.go +53 -0
- package/tui/config/config_test.go +89 -0
- package/tui/go.mod +32 -0
- package/tui/go.sum +50 -0
- package/tui/main.go +261 -0
- package/tui/tests/integration_test.go +283 -0
- package/tui/ui/model.go +310 -0
- package/vitest.config.js +10 -0
|
@@ -0,0 +1,711 @@
|
|
|
1
|
+
@main
|
|
2
|
+
struct DecodeGoldenPayloads {
|
|
3
|
+
static func main() throws {
|
|
4
|
+
let arguments = CommandLine.arguments
|
|
5
|
+
guard arguments.count == 2 else {
|
|
6
|
+
throw DecodeError.message("expected compiled fixture JSON string")
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
var parser = SimpleJSONParser(arguments[1])
|
|
10
|
+
let fixtures = try parser.parseArray()
|
|
11
|
+
var decodedTargets: [String] = []
|
|
12
|
+
|
|
13
|
+
for fixture in fixtures {
|
|
14
|
+
let fixtureObject = try fixture.asObject()
|
|
15
|
+
let target = try fixtureObject.requireString("target")
|
|
16
|
+
let json = try fixtureObject.requireValue("json")
|
|
17
|
+
|
|
18
|
+
switch target {
|
|
19
|
+
case "LegacyAuth":
|
|
20
|
+
let value = try decodeLegacyAuth(from: json)
|
|
21
|
+
try check(value.type == "auth", "unexpected legacy auth type")
|
|
22
|
+
decodedTargets.append(target)
|
|
23
|
+
case "AuthEnvelope":
|
|
24
|
+
let value = try decodeEnvelope(from: json, payloadDecoder: decodeAuthPayload)
|
|
25
|
+
try check(value.domain == "core", "unexpected auth domain")
|
|
26
|
+
decodedTargets.append(target)
|
|
27
|
+
case "LegacyAuthOk":
|
|
28
|
+
let value = try decodeLegacyAuthOk(from: json)
|
|
29
|
+
try check(value.type == "auth_ok", "unexpected auth ok type")
|
|
30
|
+
decodedTargets.append(target)
|
|
31
|
+
case "AuthOkEnvelope":
|
|
32
|
+
let value = try decodeEnvelope(from: json, payloadDecoder: decodeAuthOkPayload)
|
|
33
|
+
try check(value.domain == "core", "unexpected auth ok domain")
|
|
34
|
+
decodedTargets.append(target)
|
|
35
|
+
case "LegacyAuthError":
|
|
36
|
+
let value = try decodeLegacyAuthError(from: json)
|
|
37
|
+
try check(value.type == "auth_error", "unexpected auth error type")
|
|
38
|
+
decodedTargets.append(target)
|
|
39
|
+
case "LegacyErrorMessage":
|
|
40
|
+
let value = try decodeLegacyErrorMessage(from: json)
|
|
41
|
+
try check(value.type == "error", "unexpected legacy error type")
|
|
42
|
+
decodedTargets.append(target)
|
|
43
|
+
case "LegacyPong":
|
|
44
|
+
let value = try decodeLegacyPong(from: json)
|
|
45
|
+
try check(value.type == "pong", "unexpected legacy pong type")
|
|
46
|
+
decodedTargets.append(target)
|
|
47
|
+
case "AuthErrorEnvelope":
|
|
48
|
+
let value = try decodeEnvelope(from: json, payloadDecoder: decodeAuthErrorPayload)
|
|
49
|
+
try check(value.domain == "core", "unexpected auth error domain")
|
|
50
|
+
decodedTargets.append(target)
|
|
51
|
+
case "LegacyInspectContent":
|
|
52
|
+
let value = try decodeLegacyInspectContent(from: json)
|
|
53
|
+
try check(value.type == "inspect_content", "unexpected legacy inspect content type")
|
|
54
|
+
decodedTargets.append(target)
|
|
55
|
+
case "InspectContentEnvelope":
|
|
56
|
+
let value = try decodeEnvelope(from: json, payloadDecoder: decodeInspectContentPayload)
|
|
57
|
+
try check(value.domain == "core", "unexpected inspect content domain")
|
|
58
|
+
decodedTargets.append(target)
|
|
59
|
+
case "LegacyRequestInspect":
|
|
60
|
+
let value = try decodeLegacyInspectRequest(from: json)
|
|
61
|
+
try check(value.type == "request_inspect", "unexpected legacy inspect request type")
|
|
62
|
+
decodedTargets.append(target)
|
|
63
|
+
case "RequestInspectEnvelope":
|
|
64
|
+
let value = try decodeEnvelope(from: json, payloadDecoder: decodeInspectRequest)
|
|
65
|
+
try check(value.domain == "inspect", "unexpected inspect request domain")
|
|
66
|
+
decodedTargets.append(target)
|
|
67
|
+
case "LegacyInspectSnapshot":
|
|
68
|
+
let value = try decodeLegacyInspectSnapshot(from: json)
|
|
69
|
+
try check(value.type == "inspect_snapshot", "unexpected legacy inspect snapshot type")
|
|
70
|
+
decodedTargets.append(target)
|
|
71
|
+
case "InspectSnapshotEnvelope":
|
|
72
|
+
let value = try decodeEnvelope(from: json, payloadDecoder: decodeInspectSnapshot)
|
|
73
|
+
try check(value.domain == "inspect", "unexpected inspect snapshot domain")
|
|
74
|
+
decodedTargets.append(target)
|
|
75
|
+
case "LegacyWorkspaceState":
|
|
76
|
+
let value = try decodeLegacyWorkspaceState(from: json)
|
|
77
|
+
try check(value.type == "workspace_state", "unexpected legacy workspace state type")
|
|
78
|
+
decodedTargets.append(target)
|
|
79
|
+
case "WorkspaceStateEnvelope":
|
|
80
|
+
let value = try decodeEnvelope(from: json, payloadDecoder: decodeWorkspaceState)
|
|
81
|
+
try check(value.domain == "runtime", "unexpected workspace state domain")
|
|
82
|
+
decodedTargets.append(target)
|
|
83
|
+
case "LegacyBandwidthStats":
|
|
84
|
+
let value = try decodeLegacyBandwidthStats(from: json)
|
|
85
|
+
try check(value.type == "bandwidth_stats", "unexpected legacy bandwidth stats type")
|
|
86
|
+
decodedTargets.append(target)
|
|
87
|
+
case "BandwidthStatsEnvelope":
|
|
88
|
+
let value = try decodeEnvelope(from: json, payloadDecoder: decodeBandwidthStatsPayload)
|
|
89
|
+
try check(value.domain == "admin", "unexpected bandwidth stats domain")
|
|
90
|
+
decodedTargets.append(target)
|
|
91
|
+
default:
|
|
92
|
+
throw DecodeError.message("unknown fixture target \(target)")
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
print(decodedTargets.joined(separator: ","))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private static func decodeLegacyAuth(from value: JSONValue) throws -> LegacyAuth {
|
|
100
|
+
let object = try value.asObject()
|
|
101
|
+
return LegacyAuth(
|
|
102
|
+
type: try object.requireString("type"),
|
|
103
|
+
token: try object.requireString("token"),
|
|
104
|
+
password: object.optionalString("password"),
|
|
105
|
+
cols: object.optionalInt("cols"),
|
|
106
|
+
rows: object.optionalInt("rows"),
|
|
107
|
+
capabilities: try object.optionalDecoded("capabilities", decodeProtocolCapabilities)
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private static func decodeAuthPayload(from value: JSONValue) throws -> AuthPayload {
|
|
112
|
+
let object = try value.asObject()
|
|
113
|
+
return AuthPayload(
|
|
114
|
+
token: try object.requireString("token"),
|
|
115
|
+
password: object.optionalString("password"),
|
|
116
|
+
cols: object.optionalInt("cols"),
|
|
117
|
+
rows: object.optionalInt("rows"),
|
|
118
|
+
capabilities: try object.optionalDecoded("capabilities", decodeProtocolCapabilities)
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private static func decodeLegacyAuthOk(from value: JSONValue) throws -> LegacyAuthOk {
|
|
123
|
+
let object = try value.asObject()
|
|
124
|
+
return LegacyAuthOk(
|
|
125
|
+
type: try object.requireString("type"),
|
|
126
|
+
capabilities: try decodeProtocolCapabilities(from: object.requireValue("capabilities"))
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private static func decodeAuthOkPayload(from value: JSONValue) throws -> AuthOkPayload {
|
|
131
|
+
let object = try value.asObject()
|
|
132
|
+
return AuthOkPayload(
|
|
133
|
+
capabilities: try decodeProtocolCapabilities(from: object.requireValue("capabilities"))
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private static func decodeLegacyAuthError(from value: JSONValue) throws -> LegacyAuthError {
|
|
138
|
+
let object = try value.asObject()
|
|
139
|
+
return LegacyAuthError(
|
|
140
|
+
type: try object.requireString("type"),
|
|
141
|
+
reason: try object.requireString("reason")
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private static func decodeAuthErrorPayload(from value: JSONValue) throws -> AuthErrorPayload {
|
|
146
|
+
let object = try value.asObject()
|
|
147
|
+
return AuthErrorPayload(reason: try object.requireString("reason"))
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private static func decodeLegacyErrorMessage(from value: JSONValue) throws -> LegacyErrorMessage {
|
|
151
|
+
let object = try value.asObject()
|
|
152
|
+
return LegacyErrorMessage(
|
|
153
|
+
type: try object.requireString("type"),
|
|
154
|
+
code: object.optionalInt("code"),
|
|
155
|
+
message: try object.requireString("message")
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private static func decodeLegacyPong(from value: JSONValue) throws -> LegacyPong {
|
|
160
|
+
let object = try value.asObject()
|
|
161
|
+
return LegacyPong(
|
|
162
|
+
type: try object.requireString("type"),
|
|
163
|
+
timestamp: try object.requireDouble("timestamp")
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private static func decodeLegacyInspectContent(from value: JSONValue) throws -> LegacyInspectContent {
|
|
168
|
+
let object = try value.asObject()
|
|
169
|
+
return LegacyInspectContent(
|
|
170
|
+
type: try object.requireString("type"),
|
|
171
|
+
content: try object.requireString("content")
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private static func decodeInspectContentPayload(from value: JSONValue) throws -> InspectContentPayload {
|
|
176
|
+
let object = try value.asObject()
|
|
177
|
+
return InspectContentPayload(content: try object.requireString("content"))
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private static func decodeProtocolCapabilities(from value: JSONValue) throws -> ProtocolCapabilities {
|
|
181
|
+
let object = try value.asObject()
|
|
182
|
+
return ProtocolCapabilities(
|
|
183
|
+
envelope: try object.requireBool("envelope"),
|
|
184
|
+
inspectV2: try object.requireBool("inspectV2"),
|
|
185
|
+
deviceTrust: try object.requireBool("deviceTrust")
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private static func decodeLegacyWorkspaceState(from value: JSONValue) throws -> LegacyWorkspaceState {
|
|
190
|
+
let object = try value.asObject()
|
|
191
|
+
let tabs = try object.requireArray("tabs").map(decodeWorkspaceTab)
|
|
192
|
+
return LegacyWorkspaceState(
|
|
193
|
+
type: try object.requireString("type"),
|
|
194
|
+
session: try object.requireString("session"),
|
|
195
|
+
tabs: tabs,
|
|
196
|
+
activeTabIndex: try object.requireInt("activeTabIndex")
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private static func decodeWorkspaceState(from value: JSONValue) throws -> WorkspaceState {
|
|
201
|
+
let object = try value.asObject()
|
|
202
|
+
let tabs = try object.requireArray("tabs").map(decodeWorkspaceTab)
|
|
203
|
+
return WorkspaceState(
|
|
204
|
+
session: try object.requireString("session"),
|
|
205
|
+
tabs: tabs,
|
|
206
|
+
activeTabIndex: try object.requireInt("activeTabIndex")
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private static func decodeWorkspaceTab(from value: JSONValue) throws -> WorkspaceTab {
|
|
211
|
+
let object = try value.asObject()
|
|
212
|
+
let panes = try object.requireArray("panes").map(decodeWorkspacePane)
|
|
213
|
+
return WorkspaceTab(
|
|
214
|
+
index: try object.requireInt("index"),
|
|
215
|
+
name: try object.requireString("name"),
|
|
216
|
+
active: try object.requireBool("active"),
|
|
217
|
+
isFullscreen: try object.requireBool("isFullscreen"),
|
|
218
|
+
hasBell: try object.requireBool("hasBell"),
|
|
219
|
+
panes: panes
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private static func decodeWorkspacePane(from value: JSONValue) throws -> WorkspacePane {
|
|
224
|
+
let object = try value.asObject()
|
|
225
|
+
return WorkspacePane(
|
|
226
|
+
id: try object.requireString("id"),
|
|
227
|
+
focused: try object.requireBool("focused"),
|
|
228
|
+
title: try object.requireString("title"),
|
|
229
|
+
command: object.optionalString("command"),
|
|
230
|
+
cwd: object.optionalString("cwd"),
|
|
231
|
+
rows: try object.requireInt("rows"),
|
|
232
|
+
cols: try object.requireInt("cols"),
|
|
233
|
+
x: try object.requireInt("x"),
|
|
234
|
+
y: try object.requireInt("y")
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private static func decodeLegacyInspectRequest(from value: JSONValue) throws -> LegacyInspectRequest {
|
|
239
|
+
let object = try value.asObject()
|
|
240
|
+
return LegacyInspectRequest(
|
|
241
|
+
type: try object.requireString("type"),
|
|
242
|
+
scope: try object.requireString("scope"),
|
|
243
|
+
paneId: object.optionalString("paneId"),
|
|
244
|
+
tabIndex: object.optionalInt("tabIndex"),
|
|
245
|
+
cursor: object.optionalString("cursor"),
|
|
246
|
+
query: object.optionalString("query"),
|
|
247
|
+
limit: object.optionalInt("limit")
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private static func decodeInspectRequest(from value: JSONValue) throws -> InspectRequest {
|
|
252
|
+
let object = try value.asObject()
|
|
253
|
+
return InspectRequest(
|
|
254
|
+
scope: try object.requireString("scope"),
|
|
255
|
+
paneId: object.optionalString("paneId"),
|
|
256
|
+
tabIndex: object.optionalInt("tabIndex"),
|
|
257
|
+
cursor: object.optionalString("cursor"),
|
|
258
|
+
query: object.optionalString("query"),
|
|
259
|
+
limit: object.optionalInt("limit")
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private static func decodeLegacyInspectSnapshot(from value: JSONValue) throws -> LegacyInspectSnapshot {
|
|
264
|
+
let object = try value.asObject()
|
|
265
|
+
let items = try object.requireArray("items").map(decodeInspectItem)
|
|
266
|
+
return LegacyInspectSnapshot(
|
|
267
|
+
type: try object.requireString("type"),
|
|
268
|
+
descriptor: try decodeInspectDescriptor(from: object.requireValue("descriptor")),
|
|
269
|
+
items: items,
|
|
270
|
+
cursor: object.optionalString("cursor"),
|
|
271
|
+
truncated: try object.requireBool("truncated")
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private static func decodeInspectSnapshot(from value: JSONValue) throws -> InspectSnapshot {
|
|
276
|
+
let object = try value.asObject()
|
|
277
|
+
let items = try object.requireArray("items").map(decodeInspectItem)
|
|
278
|
+
return InspectSnapshot(
|
|
279
|
+
descriptor: try decodeInspectDescriptor(from: object.requireValue("descriptor")),
|
|
280
|
+
items: items,
|
|
281
|
+
cursor: object.optionalString("cursor"),
|
|
282
|
+
truncated: try object.requireBool("truncated")
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private static func decodeInspectDescriptor(from value: JSONValue) throws -> InspectDescriptor {
|
|
287
|
+
let object = try value.asObject()
|
|
288
|
+
return InspectDescriptor(
|
|
289
|
+
scope: try object.requireString("scope"),
|
|
290
|
+
source: try object.requireString("source"),
|
|
291
|
+
precision: try object.requireString("precision"),
|
|
292
|
+
staleness: try object.requireString("staleness"),
|
|
293
|
+
capturedAt: try object.requireString("capturedAt"),
|
|
294
|
+
paneId: object.optionalString("paneId"),
|
|
295
|
+
tabIndex: object.optionalInt("tabIndex"),
|
|
296
|
+
totalItems: object.optionalInt("totalItems")
|
|
297
|
+
)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private static func decodeInspectItem(from value: JSONValue) throws -> InspectItem {
|
|
301
|
+
let object = try value.asObject()
|
|
302
|
+
let highlights = try object.optionalArray("highlights")?.map(decodeInspectHighlight)
|
|
303
|
+
return InspectItem(
|
|
304
|
+
type: try object.requireString("type"),
|
|
305
|
+
content: try object.requireString("content"),
|
|
306
|
+
lineNumber: object.optionalInt("lineNumber"),
|
|
307
|
+
timestamp: try object.requireString("timestamp"),
|
|
308
|
+
paneId: object.optionalString("paneId"),
|
|
309
|
+
highlights: highlights
|
|
310
|
+
)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
private static func decodeInspectHighlight(from value: JSONValue) throws -> InspectHighlight {
|
|
314
|
+
let object = try value.asObject()
|
|
315
|
+
return InspectHighlight(
|
|
316
|
+
start: try object.requireInt("start"),
|
|
317
|
+
end: try object.requireInt("end")
|
|
318
|
+
)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private static func decodeLegacyBandwidthStats(from value: JSONValue) throws -> LegacyBandwidthStats {
|
|
322
|
+
let object = try value.asObject()
|
|
323
|
+
return LegacyBandwidthStats(
|
|
324
|
+
type: try object.requireString("type"),
|
|
325
|
+
stats: try decodeBandwidthStats(from: object.requireValue("stats"))
|
|
326
|
+
)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private static func decodeBandwidthStatsPayload(from value: JSONValue) throws -> BandwidthStatsPayload {
|
|
330
|
+
let object = try value.asObject()
|
|
331
|
+
return BandwidthStatsPayload(
|
|
332
|
+
stats: try decodeBandwidthStats(from: object.requireValue("stats"))
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
private static func decodeBandwidthStats(from value: JSONValue) throws -> BandwidthStats {
|
|
337
|
+
let object = try value.asObject()
|
|
338
|
+
return BandwidthStats(
|
|
339
|
+
rawBytesPerSec: try object.requireDouble("rawBytesPerSec"),
|
|
340
|
+
compressedBytesPerSec: try object.requireDouble("compressedBytesPerSec"),
|
|
341
|
+
savedPercent: try object.requireDouble("savedPercent"),
|
|
342
|
+
fullSnapshotsSent: try object.requireInt("fullSnapshotsSent"),
|
|
343
|
+
diffUpdatesSent: try object.requireInt("diffUpdatesSent"),
|
|
344
|
+
avgChangedRowsPerDiff: try object.requireDouble("avgChangedRowsPerDiff"),
|
|
345
|
+
totalRawBytes: try object.requireInt("totalRawBytes"),
|
|
346
|
+
totalCompressedBytes: try object.requireInt("totalCompressedBytes"),
|
|
347
|
+
totalSavedBytes: try object.requireInt("totalSavedBytes"),
|
|
348
|
+
rttMs: object.optionalInt("rttMs"),
|
|
349
|
+
protocolName: try object.requireString("protocol")
|
|
350
|
+
)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
private static func decodeEnvelope<T>(
|
|
354
|
+
from value: JSONValue,
|
|
355
|
+
payloadDecoder: (JSONValue) throws -> T
|
|
356
|
+
) throws -> RemuxEnvelope<T> {
|
|
357
|
+
let object = try value.asObject()
|
|
358
|
+
return RemuxEnvelope(
|
|
359
|
+
domain: try object.requireString("domain"),
|
|
360
|
+
type: try object.requireString("type"),
|
|
361
|
+
version: try object.requireInt("version"),
|
|
362
|
+
requestId: object.optionalString("requestId"),
|
|
363
|
+
emittedAt: try object.requireString("emittedAt"),
|
|
364
|
+
source: try object.requireString("source"),
|
|
365
|
+
payload: try payloadDecoder(object.requireValue("payload"))
|
|
366
|
+
)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
private static func check(_ condition: Bool, _ message: String) throws {
|
|
370
|
+
if !condition {
|
|
371
|
+
throw DecodeError.message(message)
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
private enum JSONValue {
|
|
377
|
+
case string(String)
|
|
378
|
+
case number(Double)
|
|
379
|
+
case bool(Bool)
|
|
380
|
+
case object([String: JSONValue])
|
|
381
|
+
case array([JSONValue])
|
|
382
|
+
case null
|
|
383
|
+
|
|
384
|
+
func asObject() throws -> [String: JSONValue] {
|
|
385
|
+
guard case let .object(value) = self else {
|
|
386
|
+
throw DecodeError.message("expected object")
|
|
387
|
+
}
|
|
388
|
+
return value
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
private struct SimpleJSONParser {
|
|
393
|
+
let input: [Character]
|
|
394
|
+
var index: Int = 0
|
|
395
|
+
|
|
396
|
+
init(_ input: String) {
|
|
397
|
+
self.input = Array(input)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
mutating func parseArray() throws -> [JSONValue] {
|
|
401
|
+
try skipWhitespace()
|
|
402
|
+
try expect("[")
|
|
403
|
+
try skipWhitespace()
|
|
404
|
+
if peek() == "]" {
|
|
405
|
+
index += 1
|
|
406
|
+
return []
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
var values: [JSONValue] = []
|
|
410
|
+
while true {
|
|
411
|
+
values.append(try parseValue())
|
|
412
|
+
try skipWhitespace()
|
|
413
|
+
if peek() == "," {
|
|
414
|
+
index += 1
|
|
415
|
+
try skipWhitespace()
|
|
416
|
+
continue
|
|
417
|
+
}
|
|
418
|
+
if peek() == "]" {
|
|
419
|
+
index += 1
|
|
420
|
+
return values
|
|
421
|
+
}
|
|
422
|
+
throw parserError("expected ',' or ']'")
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
mutating func parseValue() throws -> JSONValue {
|
|
427
|
+
try skipWhitespace()
|
|
428
|
+
switch peek() {
|
|
429
|
+
case "\"":
|
|
430
|
+
return .string(try parseString())
|
|
431
|
+
case "{":
|
|
432
|
+
return try parseObject()
|
|
433
|
+
case "[":
|
|
434
|
+
return try parseArrayValue()
|
|
435
|
+
case "t":
|
|
436
|
+
try expect("true")
|
|
437
|
+
return .bool(true)
|
|
438
|
+
case "f":
|
|
439
|
+
try expect("false")
|
|
440
|
+
return .bool(false)
|
|
441
|
+
case "n":
|
|
442
|
+
try expect("null")
|
|
443
|
+
return .null
|
|
444
|
+
default:
|
|
445
|
+
return .number(try parseNumber())
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
private mutating func parseObject() throws -> JSONValue {
|
|
450
|
+
try expect("{")
|
|
451
|
+
try skipWhitespace()
|
|
452
|
+
if peek() == "}" {
|
|
453
|
+
index += 1
|
|
454
|
+
return .object([:])
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
var values: [String: JSONValue] = [:]
|
|
458
|
+
while true {
|
|
459
|
+
let key = try parseString()
|
|
460
|
+
try skipWhitespace()
|
|
461
|
+
try expect(":")
|
|
462
|
+
values[key] = try parseValue()
|
|
463
|
+
try skipWhitespace()
|
|
464
|
+
if peek() == "," {
|
|
465
|
+
index += 1
|
|
466
|
+
try skipWhitespace()
|
|
467
|
+
continue
|
|
468
|
+
}
|
|
469
|
+
if peek() == "}" {
|
|
470
|
+
index += 1
|
|
471
|
+
return .object(values)
|
|
472
|
+
}
|
|
473
|
+
throw parserError("expected ',' or '}'")
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
private mutating func parseArrayValue() throws -> JSONValue {
|
|
478
|
+
try expect("[")
|
|
479
|
+
try skipWhitespace()
|
|
480
|
+
if peek() == "]" {
|
|
481
|
+
index += 1
|
|
482
|
+
return .array([])
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
var values: [JSONValue] = []
|
|
486
|
+
while true {
|
|
487
|
+
values.append(try parseValue())
|
|
488
|
+
try skipWhitespace()
|
|
489
|
+
if peek() == "," {
|
|
490
|
+
index += 1
|
|
491
|
+
try skipWhitespace()
|
|
492
|
+
continue
|
|
493
|
+
}
|
|
494
|
+
if peek() == "]" {
|
|
495
|
+
index += 1
|
|
496
|
+
return .array(values)
|
|
497
|
+
}
|
|
498
|
+
throw parserError("expected ',' or ']'")
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
private mutating func parseString() throws -> String {
|
|
503
|
+
try expect("\"")
|
|
504
|
+
var result = ""
|
|
505
|
+
while index < input.count {
|
|
506
|
+
let character = input[index]
|
|
507
|
+
index += 1
|
|
508
|
+
if character == "\"" {
|
|
509
|
+
return result
|
|
510
|
+
}
|
|
511
|
+
if character == "\\" {
|
|
512
|
+
let escaped = try nextCharacter()
|
|
513
|
+
switch escaped {
|
|
514
|
+
case "\"", "\\", "/":
|
|
515
|
+
result.append(escaped)
|
|
516
|
+
case "b":
|
|
517
|
+
result.append("\u{8}")
|
|
518
|
+
case "f":
|
|
519
|
+
result.append("\u{c}")
|
|
520
|
+
case "n":
|
|
521
|
+
result.append("\n")
|
|
522
|
+
case "r":
|
|
523
|
+
result.append("\r")
|
|
524
|
+
case "t":
|
|
525
|
+
result.append("\t")
|
|
526
|
+
case "u":
|
|
527
|
+
let scalar = try parseUnicodeEscape()
|
|
528
|
+
result.append(Character(scalar))
|
|
529
|
+
default:
|
|
530
|
+
throw parserError("unsupported escape sequence")
|
|
531
|
+
}
|
|
532
|
+
continue
|
|
533
|
+
}
|
|
534
|
+
result.append(character)
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
throw parserError("unterminated string")
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
private mutating func parseUnicodeEscape() throws -> UnicodeScalar {
|
|
541
|
+
var hex = ""
|
|
542
|
+
for _ in 0..<4 {
|
|
543
|
+
hex.append(try nextCharacter())
|
|
544
|
+
}
|
|
545
|
+
guard let value = UInt32(hex, radix: 16), let scalar = UnicodeScalar(value) else {
|
|
546
|
+
throw parserError("invalid unicode escape")
|
|
547
|
+
}
|
|
548
|
+
return scalar
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
private mutating func parseNumber() throws -> Double {
|
|
552
|
+
let start = index
|
|
553
|
+
if peek() == "-" {
|
|
554
|
+
index += 1
|
|
555
|
+
}
|
|
556
|
+
while peek().isNumber {
|
|
557
|
+
index += 1
|
|
558
|
+
}
|
|
559
|
+
if peek() == "." {
|
|
560
|
+
index += 1
|
|
561
|
+
while peek().isNumber {
|
|
562
|
+
index += 1
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if peek() == "e" || peek() == "E" {
|
|
566
|
+
index += 1
|
|
567
|
+
if peek() == "+" || peek() == "-" {
|
|
568
|
+
index += 1
|
|
569
|
+
}
|
|
570
|
+
while peek().isNumber {
|
|
571
|
+
index += 1
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
let literal = String(input[start..<index])
|
|
575
|
+
guard let value = Double(literal) else {
|
|
576
|
+
throw parserError("invalid number")
|
|
577
|
+
}
|
|
578
|
+
return value
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
private mutating func skipWhitespace() throws {
|
|
582
|
+
while peek().isWhitespace {
|
|
583
|
+
index += 1
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
private func peek() -> Character {
|
|
588
|
+
guard index < input.count else {
|
|
589
|
+
return "\0"
|
|
590
|
+
}
|
|
591
|
+
return input[index]
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
private mutating func nextCharacter() throws -> Character {
|
|
595
|
+
guard index < input.count else {
|
|
596
|
+
throw parserError("unexpected end of input")
|
|
597
|
+
}
|
|
598
|
+
defer { index += 1 }
|
|
599
|
+
return input[index]
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
private mutating func expect(_ literal: String) throws {
|
|
603
|
+
for expected in literal {
|
|
604
|
+
guard index < input.count, input[index] == expected else {
|
|
605
|
+
throw parserError("expected '\(literal)'")
|
|
606
|
+
}
|
|
607
|
+
index += 1
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
private func parserError(_ message: String) -> DecodeError {
|
|
612
|
+
.message("\(message) at index \(index)")
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
private extension Dictionary where Key == String, Value == JSONValue {
|
|
617
|
+
func requireValue(_ key: String) throws -> JSONValue {
|
|
618
|
+
guard let value = self[key] else {
|
|
619
|
+
throw DecodeError.message("missing key \(key)")
|
|
620
|
+
}
|
|
621
|
+
return value
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
func requireString(_ key: String) throws -> String {
|
|
625
|
+
guard case let .string(value) = try requireValue(key) else {
|
|
626
|
+
throw DecodeError.message("expected string for \(key)")
|
|
627
|
+
}
|
|
628
|
+
return value
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
func optionalString(_ key: String) -> String? {
|
|
632
|
+
guard let value = self[key] else {
|
|
633
|
+
return nil
|
|
634
|
+
}
|
|
635
|
+
if case .null = value {
|
|
636
|
+
return nil
|
|
637
|
+
}
|
|
638
|
+
if case let .string(string) = value {
|
|
639
|
+
return string
|
|
640
|
+
}
|
|
641
|
+
return nil
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
func requireBool(_ key: String) throws -> Bool {
|
|
645
|
+
guard case let .bool(value) = try requireValue(key) else {
|
|
646
|
+
throw DecodeError.message("expected bool for \(key)")
|
|
647
|
+
}
|
|
648
|
+
return value
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
func requireInt(_ key: String) throws -> Int {
|
|
652
|
+
Int(try requireDouble(key))
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
func optionalInt(_ key: String) -> Int? {
|
|
656
|
+
guard let value = self[key] else {
|
|
657
|
+
return nil
|
|
658
|
+
}
|
|
659
|
+
if case .null = value {
|
|
660
|
+
return nil
|
|
661
|
+
}
|
|
662
|
+
if case let .number(number) = value {
|
|
663
|
+
return Int(number)
|
|
664
|
+
}
|
|
665
|
+
return nil
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
func requireDouble(_ key: String) throws -> Double {
|
|
669
|
+
guard case let .number(value) = try requireValue(key) else {
|
|
670
|
+
throw DecodeError.message("expected number for \(key)")
|
|
671
|
+
}
|
|
672
|
+
return value
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
func requireArray(_ key: String) throws -> [JSONValue] {
|
|
676
|
+
guard case let .array(value) = try requireValue(key) else {
|
|
677
|
+
throw DecodeError.message("expected array for \(key)")
|
|
678
|
+
}
|
|
679
|
+
return value
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
func optionalArray(_ key: String) throws -> [JSONValue]? {
|
|
683
|
+
guard let value = self[key] else {
|
|
684
|
+
return nil
|
|
685
|
+
}
|
|
686
|
+
if case .null = value {
|
|
687
|
+
return nil
|
|
688
|
+
}
|
|
689
|
+
guard case let .array(array) = value else {
|
|
690
|
+
throw DecodeError.message("expected array for \(key)")
|
|
691
|
+
}
|
|
692
|
+
return array
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
func optionalDecoded<T>(
|
|
696
|
+
_ key: String,
|
|
697
|
+
_ decoder: (JSONValue) throws -> T
|
|
698
|
+
) throws -> T? {
|
|
699
|
+
guard let value = self[key] else {
|
|
700
|
+
return nil
|
|
701
|
+
}
|
|
702
|
+
if case .null = value {
|
|
703
|
+
return nil
|
|
704
|
+
}
|
|
705
|
+
return try decoder(value)
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
private enum DecodeError: Error {
|
|
710
|
+
case message(String)
|
|
711
|
+
}
|