@wangyaoshen/remux 0.3.8-dev.29e114b

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
package/pty-daemon.js ADDED
@@ -0,0 +1,303 @@
1
+ #!/usr/bin/env node
2
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
+ }) : x)(function(x) {
5
+ if (typeof require !== "undefined") return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
8
+
9
+ // src/pty-daemon.ts
10
+ import net from "net";
11
+ import pty from "node-pty";
12
+ import { parseArgs } from "util";
13
+ var TAG_PTY_OUTPUT = 1;
14
+ var TAG_CLIENT_INPUT = 2;
15
+ var TAG_RESIZE = 3;
16
+ var TAG_STATUS_REQ = 4;
17
+ var TAG_STATUS_RES = 5;
18
+ var TAG_SNAPSHOT_REQ = 6;
19
+ var TAG_SNAPSHOT_RES = 7;
20
+ var TAG_SCROLLBACK_REQ = 8;
21
+ var TAG_SCROLLBACK_RES = 9;
22
+ var TAG_SHUTDOWN = 255;
23
+ function encodeFrame(tag, payload) {
24
+ const data = typeof payload === "string" ? Buffer.from(payload, "utf8") : payload;
25
+ const frame = Buffer.alloc(5 + data.length);
26
+ frame[0] = tag;
27
+ frame.writeUInt32BE(data.length, 1);
28
+ data.copy(frame, 5);
29
+ return frame;
30
+ }
31
+ var FrameParser = class {
32
+ buffer = Buffer.alloc(0);
33
+ onFrame;
34
+ constructor(onFrame) {
35
+ this.onFrame = onFrame;
36
+ }
37
+ feed(data) {
38
+ this.buffer = Buffer.concat([this.buffer, data]);
39
+ while (this.buffer.length >= 5) {
40
+ const tag = this.buffer[0];
41
+ const length = this.buffer.readUInt32BE(1);
42
+ if (this.buffer.length < 5 + length) break;
43
+ const payload = this.buffer.subarray(5, 5 + length);
44
+ this.buffer = this.buffer.subarray(5 + length);
45
+ this.onFrame(tag, payload);
46
+ }
47
+ }
48
+ };
49
+ var RingBuffer = class {
50
+ buf;
51
+ maxBytes;
52
+ writePos;
53
+ length;
54
+ constructor(maxBytes = 10 * 1024 * 1024) {
55
+ this.buf = Buffer.alloc(maxBytes);
56
+ this.maxBytes = maxBytes;
57
+ this.writePos = 0;
58
+ this.length = 0;
59
+ }
60
+ write(data) {
61
+ const bytes = typeof data === "string" ? Buffer.from(data) : data;
62
+ if (bytes.length >= this.maxBytes) {
63
+ bytes.copy(this.buf, 0, bytes.length - this.maxBytes);
64
+ this.writePos = 0;
65
+ this.length = this.maxBytes;
66
+ return;
67
+ }
68
+ const space = this.maxBytes - this.writePos;
69
+ if (bytes.length <= space) {
70
+ bytes.copy(this.buf, this.writePos);
71
+ } else {
72
+ bytes.copy(this.buf, this.writePos, 0, space);
73
+ bytes.copy(this.buf, 0, space);
74
+ }
75
+ this.writePos = (this.writePos + bytes.length) % this.maxBytes;
76
+ this.length = Math.min(this.length + bytes.length, this.maxBytes);
77
+ }
78
+ read() {
79
+ if (this.length === 0) return Buffer.alloc(0);
80
+ if (this.length < this.maxBytes) {
81
+ return Buffer.from(this.buf.subarray(this.writePos - this.length, this.writePos));
82
+ }
83
+ return Buffer.concat([
84
+ this.buf.subarray(this.writePos),
85
+ this.buf.subarray(0, this.writePos)
86
+ ]);
87
+ }
88
+ };
89
+ function parseCliArgs() {
90
+ const { values } = parseArgs({
91
+ options: {
92
+ socket: { type: "string" },
93
+ shell: { type: "string" },
94
+ cols: { type: "string" },
95
+ rows: { type: "string" },
96
+ cwd: { type: "string" },
97
+ "tab-id": { type: "string" }
98
+ },
99
+ strict: true
100
+ });
101
+ if (!values.socket || !values.shell) {
102
+ console.error("Usage: pty-daemon --socket <path> --shell <shell> [--cols N] [--rows N] [--cwd dir] [--tab-id id]");
103
+ process.exit(1);
104
+ }
105
+ return {
106
+ socket: values.socket,
107
+ shell: values.shell,
108
+ cols: parseInt(values.cols || "80", 10),
109
+ rows: parseInt(values.rows || "24", 10),
110
+ cwd: values.cwd || process.env.HOME || "/",
111
+ tabId: values["tab-id"] || "0"
112
+ };
113
+ }
114
+ function main() {
115
+ const args = parseCliArgs();
116
+ let seq = 0;
117
+ if (typeof process.disconnect === "function") {
118
+ try {
119
+ process.disconnect();
120
+ } catch {
121
+ }
122
+ }
123
+ const ptyProcess = pty.spawn(args.shell, [], {
124
+ name: "xterm-256color",
125
+ cols: args.cols,
126
+ rows: args.rows,
127
+ cwd: args.cwd,
128
+ env: {
129
+ ...process.env,
130
+ TERM: "xterm-256color",
131
+ COLORTERM: "truecolor"
132
+ }
133
+ });
134
+ const scrollback = new RingBuffer();
135
+ const clients = /* @__PURE__ */ new Set();
136
+ let alive = true;
137
+ console.log(`[pty-daemon] started: pid=${ptyProcess.pid} socket=${args.socket} tab-id=${args.tabId}`);
138
+ ptyProcess.onData((data) => {
139
+ seq++;
140
+ scrollback.write(data);
141
+ const frame = encodeFrame(TAG_PTY_OUTPUT, data);
142
+ for (const client of clients) {
143
+ try {
144
+ client.write(frame);
145
+ } catch {
146
+ }
147
+ }
148
+ });
149
+ ptyProcess.onExit(({ exitCode }) => {
150
+ alive = false;
151
+ console.log(`[pty-daemon] PTY exited: code=${exitCode} tab-id=${args.tabId}`);
152
+ const exitMsg = `\r
153
+ \x1B[33mShell exited (code: ${exitCode})\x1B[0m\r
154
+ `;
155
+ const frame = encodeFrame(TAG_PTY_OUTPUT, exitMsg);
156
+ for (const client of clients) {
157
+ try {
158
+ client.write(frame);
159
+ } catch {
160
+ }
161
+ }
162
+ setTimeout(() => {
163
+ for (const client of clients) {
164
+ try {
165
+ client.end();
166
+ } catch {
167
+ }
168
+ }
169
+ cleanup();
170
+ }, 2e3);
171
+ });
172
+ const server = net.createServer((socket) => {
173
+ clients.add(socket);
174
+ console.log(`[pty-daemon] client connected (total: ${clients.size})`);
175
+ const parser = new FrameParser((tag, payload) => {
176
+ switch (tag) {
177
+ case TAG_CLIENT_INPUT:
178
+ if (alive) {
179
+ ptyProcess.write(payload.toString("utf8"));
180
+ }
181
+ break;
182
+ case TAG_RESIZE: {
183
+ try {
184
+ const { cols, rows } = JSON.parse(payload.toString("utf8"));
185
+ if (alive && cols > 0 && rows > 0) {
186
+ ptyProcess.resize(
187
+ Math.max(1, Math.min(cols, 500)),
188
+ Math.max(1, Math.min(rows, 200))
189
+ );
190
+ }
191
+ } catch {
192
+ }
193
+ break;
194
+ }
195
+ case TAG_STATUS_REQ: {
196
+ const status = JSON.stringify({
197
+ pid: ptyProcess.pid,
198
+ cols: args.cols,
199
+ rows: args.rows,
200
+ alive,
201
+ cwd: args.cwd,
202
+ tabId: args.tabId,
203
+ seq
204
+ });
205
+ socket.write(encodeFrame(TAG_STATUS_RES, status));
206
+ break;
207
+ }
208
+ case TAG_SNAPSHOT_REQ: {
209
+ const data = scrollback.read();
210
+ socket.write(encodeFrame(TAG_SNAPSHOT_RES, data));
211
+ break;
212
+ }
213
+ case TAG_SCROLLBACK_REQ: {
214
+ const data = scrollback.read();
215
+ socket.write(encodeFrame(TAG_SCROLLBACK_RES, data));
216
+ break;
217
+ }
218
+ case TAG_SHUTDOWN:
219
+ console.log(`[pty-daemon] shutdown requested`);
220
+ if (alive) {
221
+ try {
222
+ ptyProcess.kill();
223
+ } catch {
224
+ }
225
+ }
226
+ for (const c of clients) {
227
+ try {
228
+ c.end();
229
+ } catch {
230
+ }
231
+ }
232
+ cleanup();
233
+ break;
234
+ }
235
+ });
236
+ socket.on("data", (data) => {
237
+ parser.feed(Buffer.isBuffer(data) ? data : Buffer.from(data));
238
+ });
239
+ socket.on("close", () => {
240
+ clients.delete(socket);
241
+ console.log(`[pty-daemon] client disconnected (total: ${clients.size})`);
242
+ });
243
+ socket.on("error", (err) => {
244
+ console.error(`[pty-daemon] client socket error:`, err.message);
245
+ clients.delete(socket);
246
+ });
247
+ });
248
+ try {
249
+ const fs = __require("fs");
250
+ if (fs.existsSync(args.socket)) {
251
+ fs.unlinkSync(args.socket);
252
+ }
253
+ } catch {
254
+ }
255
+ server.listen(args.socket, () => {
256
+ console.log(`[pty-daemon] listening on ${args.socket}`);
257
+ });
258
+ server.on("error", (err) => {
259
+ console.error(`[pty-daemon] server error:`, err.message);
260
+ cleanup();
261
+ });
262
+ function cleanup() {
263
+ try {
264
+ const fs = __require("fs");
265
+ if (fs.existsSync(args.socket)) {
266
+ fs.unlinkSync(args.socket);
267
+ }
268
+ } catch {
269
+ }
270
+ server.close();
271
+ process.exit(0);
272
+ }
273
+ process.on("SIGTERM", () => {
274
+ console.log(`[pty-daemon] SIGTERM received`);
275
+ if (alive) {
276
+ try {
277
+ ptyProcess.kill();
278
+ } catch {
279
+ }
280
+ }
281
+ cleanup();
282
+ });
283
+ process.on("SIGINT", () => {
284
+ });
285
+ }
286
+ var isMainEntry = process.argv[1]?.includes("pty-daemon") || process.argv.includes("--socket");
287
+ if (isMainEntry) {
288
+ main();
289
+ }
290
+ export {
291
+ FrameParser,
292
+ TAG_CLIENT_INPUT,
293
+ TAG_PTY_OUTPUT,
294
+ TAG_RESIZE,
295
+ TAG_SCROLLBACK_REQ,
296
+ TAG_SCROLLBACK_RES,
297
+ TAG_SHUTDOWN,
298
+ TAG_SNAPSHOT_REQ,
299
+ TAG_SNAPSHOT_RES,
300
+ TAG_STATUS_REQ,
301
+ TAG_STATUS_RES,
302
+ encodeFrame
303
+ };
@@ -0,0 +1,14 @@
1
+ {
2
+ "packages": {
3
+ ".": {
4
+ "release-type": "node",
5
+ "changelog-sections": [
6
+ { "type": "feat", "section": "Features" },
7
+ { "type": "fix", "section": "Bug Fixes" },
8
+ { "type": "chore", "section": "Miscellaneous" },
9
+ { "type": "refactor", "section": "Refactoring" },
10
+ { "type": "docs", "section": "Documentation" }
11
+ ]
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,46 @@
1
+ #!/bin/bash
2
+ # Auto-deploy script for Remux runtime instances.
3
+ # Usage: auto-deploy.sh <branch> <worktree-path> <launchd-label>
4
+ #
5
+ # Checks if origin/<branch> has new commits, pulls, installs deps, and restarts.
6
+ # Designed to be called by launchd on a schedule (e.g. every 2 minutes).
7
+
8
+ set -euo pipefail
9
+
10
+ BRANCH="${1:?Usage: auto-deploy.sh <branch> <worktree-path> <launchd-label>}"
11
+ WORKTREE="${2:?}"
12
+ LABEL="${3:?}"
13
+ LOG_PREFIX="[deploy:${BRANCH}]"
14
+
15
+ cd "$WORKTREE" || { echo "$LOG_PREFIX worktree not found: $WORKTREE"; exit 1; }
16
+
17
+ # Fetch latest from origin
18
+ git fetch origin "$BRANCH" --quiet 2>/dev/null || { echo "$LOG_PREFIX fetch failed"; exit 1; }
19
+
20
+ LOCAL=$(git rev-parse HEAD 2>/dev/null)
21
+ REMOTE=$(git rev-parse "origin/${BRANCH}" 2>/dev/null)
22
+
23
+ if [ "$LOCAL" = "$REMOTE" ]; then
24
+ # Already up to date
25
+ exit 0
26
+ fi
27
+
28
+ echo "$LOG_PREFIX updating ${LOCAL:0:7} → ${REMOTE:0:7}"
29
+
30
+ # Pull changes
31
+ git checkout --detach "origin/${BRANCH}" --quiet 2>/dev/null
32
+
33
+ # Install dependencies if lockfile changed
34
+ if ! git diff --quiet "$LOCAL" "$REMOTE" -- pnpm-lock.yaml 2>/dev/null; then
35
+ echo "$LOG_PREFIX pnpm-lock.yaml changed, installing deps"
36
+ /opt/homebrew/bin/pnpm install --frozen-lockfile --prefer-offline 2>/dev/null || true
37
+ fi
38
+
39
+ # Fix node-pty spawn-helper permissions (needed after fresh install)
40
+ find node_modules -name "spawn-helper" -type f -exec chmod +x {} \; 2>/dev/null || true
41
+
42
+ # Restart the service
43
+ echo "$LOG_PREFIX restarting $LABEL"
44
+ /bin/launchctl kickstart -k "gui/501/${LABEL}" 2>/dev/null || true
45
+
46
+ echo "$LOG_PREFIX deployed ${REMOTE:0:7}"
@@ -0,0 +1,121 @@
1
+ #!/bin/bash
2
+ # Build Remux.app and create a DMG installer.
3
+ # Usage: ./scripts/build-dmg.sh [--version X.Y.Z]
4
+ set -euo pipefail
5
+
6
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
7
+ ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
8
+ APP_DIR="$ROOT_DIR/apps/macos"
9
+ BUILD_DIR="$ROOT_DIR/build"
10
+
11
+ # Read version from package.json or CLI arg
12
+ VERSION="${1:-}"
13
+ if [ -z "$VERSION" ]; then
14
+ VERSION=$(node -p "require('$ROOT_DIR/package.json').version" 2>/dev/null || echo "0.0.0")
15
+ fi
16
+ # Strip leading 'v' if present
17
+ VERSION="${VERSION#v}"
18
+
19
+ echo "Building Remux.app v${VERSION}..."
20
+
21
+ # Step 1: Build ghostty xcframework if missing
22
+ XCFW="$ROOT_DIR/vendor/ghostty/macos/GhosttyKit.xcframework"
23
+ if [ ! -d "$XCFW" ] || [ ! -f "$XCFW/Info.plist" ]; then
24
+ echo "GhosttyKit.xcframework not found. Building from source..."
25
+ cd "$ROOT_DIR/vendor/ghostty"
26
+ git submodule update --init 2>/dev/null || true
27
+ zig build -Demit-xcframework=true -Dxcframework-target=native -Doptimize=ReleaseFast
28
+ echo "GhosttyKit built."
29
+ fi
30
+
31
+ # Step 2: Build macOS app (release)
32
+ cd "$APP_DIR"
33
+ swift build -c release 2>&1
34
+
35
+ BINARY=$(swift build -c release --show-bin-path)/Remux
36
+ if [ ! -f "$BINARY" ]; then
37
+ echo "Error: Binary not found at $BINARY"
38
+ exit 1
39
+ fi
40
+
41
+ # Step 3: Create .app bundle
42
+ rm -rf "$BUILD_DIR/Remux.app"
43
+ mkdir -p "$BUILD_DIR/Remux.app/Contents/MacOS"
44
+ mkdir -p "$BUILD_DIR/Remux.app/Contents/Resources"
45
+
46
+ cp "$BINARY" "$BUILD_DIR/Remux.app/Contents/MacOS/Remux"
47
+
48
+ # Copy ghostty resources if available
49
+ if [ -d "$APP_DIR/Resources/terminfo" ]; then
50
+ cp -r "$APP_DIR/Resources/terminfo" "$BUILD_DIR/Remux.app/Contents/Resources/"
51
+ fi
52
+ if [ -d "$APP_DIR/Resources/shell-integration" ]; then
53
+ cp -r "$APP_DIR/Resources/shell-integration" "$BUILD_DIR/Remux.app/Contents/Resources/"
54
+ fi
55
+
56
+ cat > "$BUILD_DIR/Remux.app/Contents/Info.plist" << PLIST
57
+ <?xml version="1.0" encoding="UTF-8"?>
58
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
59
+ <plist version="1.0">
60
+ <dict>
61
+ <key>CFBundleName</key>
62
+ <string>Remux</string>
63
+ <key>CFBundleDisplayName</key>
64
+ <string>Remux</string>
65
+ <key>CFBundleIdentifier</key>
66
+ <string>com.remux.desktop</string>
67
+ <key>CFBundleVersion</key>
68
+ <string>${VERSION}</string>
69
+ <key>CFBundleShortVersionString</key>
70
+ <string>${VERSION}</string>
71
+ <key>CFBundleExecutable</key>
72
+ <string>Remux</string>
73
+ <key>CFBundlePackageType</key>
74
+ <string>APPL</string>
75
+ <key>CFBundleInfoDictionaryVersion</key>
76
+ <string>6.0</string>
77
+ <key>LSMinimumSystemVersion</key>
78
+ <string>14.0</string>
79
+ <key>NSHighResolutionCapable</key>
80
+ <true/>
81
+ <key>LSUIElement</key>
82
+ <false/>
83
+ <key>NSPrincipalClass</key>
84
+ <string>NSApplication</string>
85
+ </dict>
86
+ </plist>
87
+ PLIST
88
+
89
+ echo "✓ Remux.app created at $BUILD_DIR/Remux.app"
90
+
91
+ # Step 4: Create DMG
92
+ DMG_NAME="Remux-${VERSION}-arm64.dmg"
93
+ rm -f "$BUILD_DIR/$DMG_NAME"
94
+
95
+ if command -v create-dmg &> /dev/null; then
96
+ echo "Creating DMG with create-dmg..."
97
+ create-dmg \
98
+ --volname "Remux" \
99
+ --window-pos 200 120 \
100
+ --window-size 600 400 \
101
+ --icon-size 100 \
102
+ --icon "Remux.app" 150 190 \
103
+ --app-drop-link 450 190 \
104
+ "$BUILD_DIR/$DMG_NAME" \
105
+ "$BUILD_DIR/Remux.app" \
106
+ 2>/dev/null || true
107
+ else
108
+ # Fallback: simple hdiutil DMG
109
+ echo "Creating DMG with hdiutil..."
110
+ hdiutil create -volname "Remux" \
111
+ -srcfolder "$BUILD_DIR/Remux.app" \
112
+ -ov -format UDZO \
113
+ "$BUILD_DIR/$DMG_NAME"
114
+ fi
115
+
116
+ if [ -f "$BUILD_DIR/$DMG_NAME" ]; then
117
+ SIZE=$(du -h "$BUILD_DIR/$DMG_NAME" | cut -f1)
118
+ echo "✓ DMG created: $BUILD_DIR/$DMG_NAME ($SIZE)"
119
+ else
120
+ echo "⚠ DMG creation failed, but Remux.app is available"
121
+ fi
@@ -0,0 +1,43 @@
1
+ #!/bin/bash
2
+ # Build GhosttyKit.xcframework from the ghostty submodule.
3
+ # Prerequisites: zig (brew install zig), Xcode Command Line Tools
4
+ # Usage: ./scripts/build-ghostty-kit.sh [--debug]
5
+
6
+ set -euo pipefail
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
9
+ ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
10
+ GHOSTTY_DIR="$ROOT_DIR/vendor/ghostty"
11
+
12
+ if [ ! -f "$GHOSTTY_DIR/build.zig" ]; then
13
+ echo "Error: ghostty submodule not found at $GHOSTTY_DIR"
14
+ echo "Run: git submodule update --init vendor/ghostty"
15
+ exit 1
16
+ fi
17
+
18
+ if ! command -v zig &> /dev/null; then
19
+ echo "Error: zig not found. Install with: brew install zig"
20
+ exit 1
21
+ fi
22
+
23
+ OPTIMIZE="ReleaseFast"
24
+ if [ "${1:-}" = "--debug" ]; then
25
+ OPTIMIZE="Debug"
26
+ fi
27
+
28
+ echo "Building GhosttyKit.xcframework (optimize=$OPTIMIZE)..."
29
+ cd "$GHOSTTY_DIR"
30
+ zig build -Demit-xcframework=true -Doptimize="$OPTIMIZE"
31
+
32
+ # Check output — ghostty emits to macos/GhosttyKit.xcframework
33
+ XCFW_PATH="$GHOSTTY_DIR/macos/GhosttyKit.xcframework"
34
+ if [ -d "$XCFW_PATH" ]; then
35
+ echo "Success: $XCFW_PATH"
36
+ echo "Size: $(du -sh "$XCFW_PATH" | cut -f1)"
37
+ else
38
+ echo "Error: xcframework not found at expected path"
39
+ echo "Checking macos/ and zig-out/ contents:"
40
+ ls -la "$GHOSTTY_DIR/macos/" 2>/dev/null || echo "macos/ not found"
41
+ ls -la "$GHOSTTY_DIR/zig-out/" 2>/dev/null || echo "zig-out/ not found"
42
+ exit 1
43
+ fi
@@ -0,0 +1,132 @@
1
+ import { readdir, readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ const root = process.cwd();
5
+ const entrypoints = ["README.md", "AGENTS.md", "docs", "src", "tests", ".github"];
6
+ const ignoredDirNames = new Set(["node_modules", "dist", ".git", "coverage"]);
7
+ const allowedExtensions = new Set([".md", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".json", ".yml", ".yaml"]);
8
+ const allowedFiles = new Map([
9
+ ["docs/ACTIVE_DOCS_INDEX.md", "active index must point to the archived runtime-v2 document set by its real path"],
10
+ ["docs/adr/ADR_TERMINOLOGY.md", "terminology ADR must name the banned product term explicitly"],
11
+ ["docs/remux-master-plan-2026-v2.md", "current planning document compares legacy assumptions against the current baseline"],
12
+ ["docs/LEGACY_PLAN_GAPS.md", "legacy gap ledger must name the invalidated assumptions"],
13
+ ["docs/TERMINOLOGY_AUDIT.md", "audit output must record the archived terms it scanned"],
14
+ ]);
15
+ const archivedRuntimePatterns = [
16
+ { label: "runtime-v2", regex: /\bruntime-v2\b/i },
17
+ { label: "remuxd", regex: /\bremuxd\b/i },
18
+ { label: "old runtime", regex: /\bold runtime\b/i },
19
+ { label: "daemon", regex: /\bdaemon\b/i },
20
+ ];
21
+ const productScrollPattern = /\bscroll\b/i;
22
+ const allowedScrollLinePatterns = [
23
+ /\bscrollback\b/i,
24
+ /\bscrollbar\b/i,
25
+ /\boverscroll\b/i,
26
+ /\bscrolling\b/i,
27
+ /\bscrollable\b/i,
28
+ /\bscrolltoline\b/i,
29
+ /\bxterm-scrollable-element\b/i,
30
+ /scroll-snap/i,
31
+ /-webkit-overflow-scrolling/i,
32
+ /addEventListener\(\s*["']scroll["']/,
33
+ /removeEventListener\(\s*["']scroll["']/,
34
+ /new Event\(\s*["']scroll["']/,
35
+ /\bscroll fixes\b/i,
36
+ ];
37
+
38
+ async function collectFiles(relativePath) {
39
+ const absolutePath = path.join(root, relativePath);
40
+ const stats = await readdir(absolutePath, { withFileTypes: true }).catch(() => null);
41
+ if (stats === null) {
42
+ return [relativePath];
43
+ }
44
+
45
+ const files = [];
46
+ for (const entry of stats) {
47
+ if (ignoredDirNames.has(entry.name)) {
48
+ continue;
49
+ }
50
+
51
+ const nextRelative = path.posix.join(relativePath, entry.name);
52
+ if (nextRelative.startsWith("docs/archive/") || nextRelative.startsWith("docs/assets/")) {
53
+ continue;
54
+ }
55
+
56
+ if (entry.isDirectory()) {
57
+ files.push(...(await collectFiles(nextRelative)));
58
+ continue;
59
+ }
60
+
61
+ if (allowedExtensions.has(path.extname(entry.name))) {
62
+ files.push(nextRelative);
63
+ }
64
+ }
65
+
66
+ return files;
67
+ }
68
+
69
+ async function main() {
70
+ const files = [];
71
+ for (const entrypoint of entrypoints) {
72
+ const absolute = path.join(root, entrypoint);
73
+ const isDirectory = await readdir(absolute, { withFileTypes: true }).then(() => true).catch(() => false);
74
+ if (isDirectory) {
75
+ files.push(...(await collectFiles(entrypoint)));
76
+ continue;
77
+ }
78
+
79
+ files.push(entrypoint);
80
+ }
81
+
82
+ const violations = [];
83
+ for (const relativePath of files) {
84
+ if (allowedFiles.has(relativePath)) {
85
+ continue;
86
+ }
87
+
88
+ const source = await readFile(path.join(root, relativePath), "utf8");
89
+ const matchedLabels = archivedRuntimePatterns
90
+ .filter(({ regex }) => regex.test(source))
91
+ .map(({ label }) => label);
92
+
93
+ const lines = source.split("\n");
94
+ for (const [index, line] of lines.entries()) {
95
+ if (!productScrollPattern.test(line)) {
96
+ continue;
97
+ }
98
+
99
+ if (allowedScrollLinePatterns.some((regex) => regex.test(line))) {
100
+ continue;
101
+ }
102
+
103
+ matchedLabels.push(`scroll(product-term)@L${index + 1}`);
104
+ }
105
+
106
+ if (matchedLabels.length > 0) {
107
+ violations.push({ relativePath, matchedLabels });
108
+ }
109
+ }
110
+
111
+ if (violations.length === 0) {
112
+ console.log("Terminology guard passed.");
113
+ return;
114
+ }
115
+
116
+ console.error("Archived-runtime terminology found in active files:");
117
+ for (const violation of violations) {
118
+ console.error(`- ${violation.relativePath}: ${violation.matchedLabels.join(", ")}`);
119
+ }
120
+
121
+ console.error("\nAllowed exceptions:");
122
+ for (const [relativePath, reason] of allowedFiles.entries()) {
123
+ console.error(`- ${relativePath}: ${reason}`);
124
+ }
125
+
126
+ process.exit(1);
127
+ }
128
+
129
+ main().catch((error) => {
130
+ console.error(error);
131
+ process.exit(1);
132
+ });