handmux 0.23.0 → 0.24.0

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 (252) hide show
  1. package/README.md +3 -3
  2. package/README.zh-CN.md +3 -3
  3. package/bin/handmux.js +3 -689
  4. package/dist/bin/handmux.js +850 -0
  5. package/{hooks → dist/hooks}/handmux-statusline.cjs +28 -6
  6. package/{hooks → dist/hooks}/handmux-write.cjs +24 -3
  7. package/dist/package.json +126 -0
  8. package/dist/public/assets/index-CicdY9E9.css +32 -0
  9. package/dist/public/assets/index-D4VP5t8h.js +337 -0
  10. package/dist/public/assets/pcm-worklet-CxcDy7PB.js +1 -0
  11. package/{public → dist/public}/index.html +5 -4
  12. package/{src → dist/src}/agents/claude.js +105 -83
  13. package/dist/src/agents/codex.js +217 -0
  14. package/{src → dist/src}/agents/index.js +7 -7
  15. package/dist/src/agents/processIdentity.js +80 -0
  16. package/dist/src/agents/scanUtils.js +262 -0
  17. package/dist/src/apiErrors.js +77 -0
  18. package/{src → dist/src}/appName.js +12 -13
  19. package/dist/src/asr/iflyConfig.js +7 -0
  20. package/dist/src/asr/iflySign.js +14 -0
  21. package/dist/src/auth.js +27 -0
  22. package/dist/src/browser/bootstrap.js +72 -0
  23. package/dist/src/browser/cookieProfiles.js +413 -0
  24. package/dist/src/browser/coordinator.js +51 -0
  25. package/dist/src/browser/hammerheadCompat.js +104 -0
  26. package/dist/src/browser/hammerheadRedirectCompat.js +37 -0
  27. package/dist/src/browser/manager.js +560 -0
  28. package/dist/src/browser/originLabel.js +15 -0
  29. package/dist/src/browser/profilePersistence.js +235 -0
  30. package/dist/src/browser/publicProxy.js +176 -0
  31. package/dist/src/browser/routes.js +159 -0
  32. package/dist/src/browser/sessionStore.js +72 -0
  33. package/dist/src/browser/siteVersion.js +121 -0
  34. package/dist/src/browser/targetPolicy.js +163 -0
  35. package/{src → dist/src}/browser/worker.js +14 -18
  36. package/dist/src/browser/workerClient.js +286 -0
  37. package/dist/src/browser/workerServer.js +91 -0
  38. package/dist/src/captureBackground.js +95 -0
  39. package/dist/src/claudeEvents.js +499 -0
  40. package/{src → dist/src}/cli/claudeHooks.js +146 -108
  41. package/{src → dist/src}/cli/cloudflareUrl.js +2 -3
  42. package/dist/src/cli/cloudflared.js +106 -0
  43. package/dist/src/cli/codexManaged.js +77 -0
  44. package/{src → dist/src}/cli/cpolarUrl.js +8 -9
  45. package/dist/src/cli/drivers.js +98 -0
  46. package/dist/src/cli/hookScaffold.js +36 -0
  47. package/dist/src/cli/i18n/en.js +366 -0
  48. package/{src → dist/src}/cli/i18n/index.js +27 -25
  49. package/dist/src/cli/i18n/zh.js +365 -0
  50. package/dist/src/cli/legacyCodexHooks.js +52 -0
  51. package/{src → dist/src}/cli/natappUrl.js +2 -3
  52. package/dist/src/cli/options.js +245 -0
  53. package/dist/src/cli/probe.js +16 -0
  54. package/{src → dist/src}/cli/prompt.js +4 -6
  55. package/dist/src/cli/pushCmd.js +102 -0
  56. package/{src → dist/src}/cli/qr.js +19 -19
  57. package/dist/src/cli/service.js +144 -0
  58. package/dist/src/cli/setupModel.js +194 -0
  59. package/dist/src/cli/setupWizard.js +647 -0
  60. package/dist/src/cli/shortcutEditor.js +351 -0
  61. package/dist/src/cli/sshTunnel.js +18 -0
  62. package/dist/src/cli/state.js +114 -0
  63. package/{src → dist/src}/cli/statusLine.js +63 -46
  64. package/dist/src/cli/supervisor.js +278 -0
  65. package/dist/src/cli/supervisorHealth.js +21 -0
  66. package/dist/src/cli/supervisorLaunch.js +148 -0
  67. package/dist/src/cli/supervisorProcesses.js +62 -0
  68. package/dist/src/cli/supervisorState.js +62 -0
  69. package/dist/src/cli/tmuxVersion.js +53 -0
  70. package/{src → dist/src}/cli/tunlite.js +7 -10
  71. package/dist/src/cli/tunnelClients.js +72 -0
  72. package/{src → dist/src}/cli/updateCheck.js +98 -69
  73. package/{src → dist/src}/cli/urlHost.js +8 -4
  74. package/dist/src/cli/workspaceCmd.js +347 -0
  75. package/dist/src/codexAppServer.js +2299 -0
  76. package/dist/src/codexConversationProjection.js +199 -0
  77. package/dist/src/codexDiff.js +118 -0
  78. package/dist/src/codexMessageIdentity.js +18 -0
  79. package/dist/src/codexPlan.js +30 -0
  80. package/dist/src/codexQueueProtocol.js +96 -0
  81. package/dist/src/codexStreamProtocol.js +207 -0
  82. package/dist/src/codexToolProtocol.js +68 -0
  83. package/dist/src/codexTranscriptParse.js +724 -0
  84. package/dist/src/codexUsageSnapshot.js +205 -0
  85. package/dist/src/config.js +8 -0
  86. package/{src → dist/src}/docPath.js +22 -24
  87. package/dist/src/docs.js +337 -0
  88. package/dist/src/git.js +272 -0
  89. package/dist/src/healthProtocol.js +97 -0
  90. package/dist/src/httpApi.js +68 -0
  91. package/dist/src/jsonStore.js +15 -0
  92. package/{src → dist/src}/keyNames.js +3 -3
  93. package/dist/src/notifications.js +111 -0
  94. package/dist/src/orphans.js +157 -0
  95. package/dist/src/pendingPrompt.js +194 -0
  96. package/dist/src/previewServer.js +202 -0
  97. package/dist/src/previews.js +179 -0
  98. package/dist/src/privateStateStore.js +89 -0
  99. package/dist/src/push.js +224 -0
  100. package/dist/src/routes/codex.js +606 -0
  101. package/dist/src/routes/files.js +230 -0
  102. package/dist/src/routes/git.js +78 -0
  103. package/dist/src/routes/health.js +16 -0
  104. package/dist/src/routes/notifications.js +16 -0
  105. package/dist/src/routes/previews.js +43 -0
  106. package/dist/src/routes/push.js +145 -0
  107. package/dist/src/routes/sessions.js +232 -0
  108. package/dist/src/routes/system.js +156 -0
  109. package/dist/src/routes/terminal.js +238 -0
  110. package/dist/src/routes/transcript.js +197 -0
  111. package/dist/src/routes/workspace.js +267 -0
  112. package/{src → dist/src}/server.js +100 -74
  113. package/dist/src/shortcutConfig.js +83 -0
  114. package/{src → dist/src}/staticCache.js +5 -3
  115. package/{src → dist/src}/staticCompression.js +5 -6
  116. package/dist/src/terminalStream.js +441 -0
  117. package/{src → dist/src}/tmux/commands.js +156 -171
  118. package/dist/src/tmux/format.js +47 -0
  119. package/dist/src/transcriptParse.js +240 -0
  120. package/dist/src/transcriptReader.js +113 -0
  121. package/{src → dist/src}/trimCapture.js +11 -11
  122. package/dist/src/uploadTypes.js +29 -0
  123. package/dist/src/urlPolicy.js +18 -0
  124. package/dist/src/usage.js +260 -0
  125. package/dist/src/workspace/agentRunner.js +204 -0
  126. package/dist/src/workspace/atomicJson.js +31 -0
  127. package/dist/src/workspace/capture.js +139 -0
  128. package/dist/src/workspace/checkpointer.js +228 -0
  129. package/dist/src/workspace/environment.js +71 -0
  130. package/dist/src/workspace/lock.js +137 -0
  131. package/dist/src/workspace/mapping.js +128 -0
  132. package/dist/src/workspace/operations.js +271 -0
  133. package/dist/src/workspace/paths.js +15 -0
  134. package/dist/src/workspace/planner.js +292 -0
  135. package/dist/src/workspace/restore.js +314 -0
  136. package/dist/src/workspace/runtime.js +318 -0
  137. package/dist/src/workspace/schema.js +156 -0
  138. package/dist/src/workspace/store.js +464 -0
  139. package/dist/src/workspace/tmuxAdapter.js +694 -0
  140. package/dist/src/workspaceProtocol.js +158 -0
  141. package/package.json +27 -15
  142. package/public/assets/index-C3N7OkyP.css +0 -32
  143. package/public/assets/index-D1DWcC5u.js +0 -337
  144. package/src/.gitkeep +0 -0
  145. package/src/agents/codex.js +0 -168
  146. package/src/agents/processIdentity.js +0 -72
  147. package/src/agents/scanUtils.js +0 -225
  148. package/src/asr/iflyConfig.js +0 -10
  149. package/src/asr/iflySign.js +0 -16
  150. package/src/auth.js +0 -30
  151. package/src/browser/bootstrap.js +0 -68
  152. package/src/browser/cookieProfiles.js +0 -403
  153. package/src/browser/coordinator.js +0 -47
  154. package/src/browser/hammerheadCompat.js +0 -86
  155. package/src/browser/hammerheadRedirectCompat.js +0 -38
  156. package/src/browser/manager.js +0 -556
  157. package/src/browser/originLabel.js +0 -17
  158. package/src/browser/profilePersistence.js +0 -241
  159. package/src/browser/publicProxy.js +0 -175
  160. package/src/browser/routes.js +0 -154
  161. package/src/browser/sessionStore.js +0 -78
  162. package/src/browser/siteVersion.js +0 -124
  163. package/src/browser/targetPolicy.js +0 -137
  164. package/src/browser/workerClient.js +0 -253
  165. package/src/browser/workerServer.js +0 -98
  166. package/src/captureBackground.js +0 -93
  167. package/src/claudeEvents.js +0 -399
  168. package/src/cli/cloudflared.js +0 -99
  169. package/src/cli/codexManaged.js +0 -77
  170. package/src/cli/drivers.js +0 -97
  171. package/src/cli/hookScaffold.js +0 -33
  172. package/src/cli/i18n/en.js +0 -385
  173. package/src/cli/i18n/zh.js +0 -384
  174. package/src/cli/legacyCodexHooks.js +0 -49
  175. package/src/cli/options.js +0 -211
  176. package/src/cli/probe.js +0 -16
  177. package/src/cli/pushCmd.js +0 -60
  178. package/src/cli/service.js +0 -126
  179. package/src/cli/setupModel.js +0 -154
  180. package/src/cli/setupWizard.js +0 -506
  181. package/src/cli/shortcutEditor.js +0 -288
  182. package/src/cli/sshTunnel.js +0 -12
  183. package/src/cli/state.js +0 -90
  184. package/src/cli/supervisor.js +0 -183
  185. package/src/cli/supervisorProcesses.js +0 -57
  186. package/src/cli/tmuxVersion.js +0 -49
  187. package/src/cli/tunnelClients.js +0 -62
  188. package/src/cli/workspaceCmd.js +0 -300
  189. package/src/codexAppServer.js +0 -1620
  190. package/src/codexDiff.js +0 -108
  191. package/src/codexTranscriptParse.js +0 -413
  192. package/src/codexUsageSnapshot.cjs +0 -129
  193. package/src/config.js +0 -9
  194. package/src/docs.js +0 -228
  195. package/src/git.js +0 -208
  196. package/src/httpApi.js +0 -72
  197. package/src/jsonStore.js +0 -24
  198. package/src/notifications.js +0 -68
  199. package/src/orphans.js +0 -152
  200. package/src/pendingPrompt.js +0 -163
  201. package/src/previewServer.js +0 -168
  202. package/src/previews.js +0 -116
  203. package/src/push.js +0 -183
  204. package/src/routes/codex.js +0 -473
  205. package/src/routes/files.js +0 -172
  206. package/src/routes/git.js +0 -57
  207. package/src/routes/notifications.js +0 -19
  208. package/src/routes/previews.js +0 -35
  209. package/src/routes/push.js +0 -119
  210. package/src/routes/sessions.js +0 -168
  211. package/src/routes/system.js +0 -132
  212. package/src/routes/terminal.js +0 -210
  213. package/src/routes/transcript.js +0 -171
  214. package/src/routes/workspace.js +0 -267
  215. package/src/shortcutConfig.js +0 -75
  216. package/src/terminalStream.js +0 -391
  217. package/src/tmux/format.js +0 -45
  218. package/src/transcriptParse.js +0 -180
  219. package/src/transcriptReader.js +0 -101
  220. package/src/uploadTypes.js +0 -30
  221. package/src/urlPolicy.js +0 -14
  222. package/src/usage.js +0 -260
  223. package/src/workspace/agentRunner.js +0 -188
  224. package/src/workspace/atomicJson.js +0 -32
  225. package/src/workspace/capture.js +0 -139
  226. package/src/workspace/checkpointer.js +0 -187
  227. package/src/workspace/environment.js +0 -75
  228. package/src/workspace/lock.js +0 -128
  229. package/src/workspace/mapping.js +0 -124
  230. package/src/workspace/operations.js +0 -252
  231. package/src/workspace/paths.js +0 -16
  232. package/src/workspace/planner.js +0 -247
  233. package/src/workspace/restore.js +0 -279
  234. package/src/workspace/runtime.js +0 -314
  235. package/src/workspace/schema.js +0 -136
  236. package/src/workspace/store.js +0 -382
  237. package/src/workspace/tmuxAdapter.js +0 -554
  238. /package/{hooks → dist/hooks}/handmux-notify.sh +0 -0
  239. /package/{public → dist/public}/fonts/JetBrainsMonoNerdFontMono-Regular.woff2 +0 -0
  240. /package/{public → dist/public}/fonts/TWUnifont.woff2 +0 -0
  241. /package/{public → dist/public}/icons/apple-touch-icon.png +0 -0
  242. /package/{public → dist/public}/icons/badge-96.png +0 -0
  243. /package/{public → dist/public}/icons/icon-192-maskable.png +0 -0
  244. /package/{public → dist/public}/icons/icon-192.png +0 -0
  245. /package/{public → dist/public}/icons/icon-512-maskable.png +0 -0
  246. /package/{public → dist/public}/icons/icon-512.png +0 -0
  247. /package/{public → dist/public}/icons/logo.svg +0 -0
  248. /package/{public → dist/public}/manifest.webmanifest +0 -0
  249. /package/{public → dist/public}/offline.html +0 -0
  250. /package/{public → dist/public}/sw.js +0 -0
  251. /package/{src → dist/src}/browser/protocol.js +0 -0
  252. /package/{src → dist/src}/cli/cfNamed.js +0 -0
@@ -21,6 +21,22 @@ const raw = (() => { try { return fs.readFileSync(0, 'utf8'); } catch { return '
21
21
  let j = {};
22
22
  try { j = JSON.parse(raw || '{}'); } catch { /* not JSON → leave j = {} */ }
23
23
 
24
+ function ensurePrivateDirectory(directory) {
25
+ const resolved = path.resolve(directory);
26
+ const parsed = path.parse(resolved);
27
+ const parts = resolved.slice(parsed.root.length).split(path.sep).filter(Boolean);
28
+ const privateRoot = parts.indexOf('.handmux');
29
+ if (privateRoot < 0) {
30
+ fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
31
+ return;
32
+ }
33
+ for (let i = privateRoot; i < parts.length; i++) {
34
+ const current = path.join(parsed.root, ...parts.slice(0, i + 1));
35
+ fs.mkdirSync(current, { recursive: true, mode: 0o700 });
36
+ fs.chmodSync(current, 0o700);
37
+ }
38
+ }
39
+
24
40
  // One rate-limit window → our shape, or undefined if the field is absent (rate_limits only appears for
25
41
  // Pro/Max plans, and only after a session's first API response).
26
42
  function win(o) {
@@ -48,7 +64,8 @@ if (file) {
48
64
  rateLimits,
49
65
  };
50
66
  if (snap.context === undefined) delete snap.context;
51
- fs.mkdirSync(path.dirname(file), { recursive: true });
67
+ ensurePrivateDirectory(path.dirname(file));
68
+ if (fs.existsSync(file)) fs.chmodSync(file, 0o600);
52
69
  let previousHasQuota = false;
53
70
  try {
54
71
  const previous = JSON.parse(fs.readFileSync(file, 'utf8'));
@@ -58,12 +75,13 @@ if (file) {
58
75
  // previous machine-wide quota until a real rate-limit payload refreshes it instead of flashing empty.
59
76
  if (Object.keys(rateLimits).length > 0) {
60
77
  const tmp = `${file}.${process.pid}.tmp`;
61
- fs.writeFileSync(tmp, JSON.stringify(snap));
78
+ fs.writeFileSync(tmp, JSON.stringify(snap), { mode: 0o600 });
62
79
  fs.renameSync(tmp, file); // atomic: concurrent statuslines (multiple sessions) can't tear the snapshot
80
+ fs.chmodSync(file, 0o600);
63
81
  } else if (!previousHasQuota) {
64
82
  // Create the initial "capturer active, awaiting data" marker only if no concurrent session has
65
83
  // already published a real quota snapshot. An empty session can therefore never win that race.
66
- try { fs.writeFileSync(file, JSON.stringify(snap), { flag: 'wx' }); } catch { /* file now exists */ }
84
+ try { fs.writeFileSync(file, JSON.stringify(snap), { flag: 'wx', mode: 0o600 }); } catch { /* file now exists */ }
67
85
  }
68
86
 
69
87
  // Per-session context snapshot. The global file above is last-writer-wins across ALL sessions, so it
@@ -73,12 +91,13 @@ if (file) {
73
91
  const sid = typeof j.session_id === 'string' ? j.session_id : null;
74
92
  if (sid && /^[\w-]+$/.test(sid) && snap.context) {
75
93
  const cdir = path.join(path.dirname(file), 'context');
76
- fs.mkdirSync(cdir, { recursive: true });
94
+ ensurePrivateDirectory(cdir);
77
95
  const cfile = path.join(cdir, `${sid}.json`);
78
96
  const csnap = { sessionId: sid, model: snap.model, usedPercent: snap.context.usedPercent, updatedAt: snap.updatedAt };
79
97
  const ctmp = `${cfile}.${process.pid}.tmp`;
80
- fs.writeFileSync(ctmp, JSON.stringify(csnap));
98
+ fs.writeFileSync(ctmp, JSON.stringify(csnap), { mode: 0o600 });
81
99
  fs.renameSync(ctmp, cfile);
100
+ fs.chmodSync(cfile, 0o600);
82
101
  // Best-effort prune of stale session files (ended sessions never clean up after themselves) so the
83
102
  // dir can't grow without bound. A day is well past any live session's last statusLine render.
84
103
  try {
@@ -86,7 +105,10 @@ if (file) {
86
105
  for (const nm of fs.readdirSync(cdir)) {
87
106
  if (!nm.endsWith('.json')) continue;
88
107
  const fp = path.join(cdir, nm);
89
- try { if (fs.statSync(fp).mtimeMs < cutoff) fs.unlinkSync(fp); } catch { /* skip */ }
108
+ try {
109
+ fs.chmodSync(fp, 0o600);
110
+ if (fs.statSync(fp).mtimeMs < cutoff) fs.unlinkSync(fp);
111
+ } catch { /* skip */ }
90
112
  }
91
113
  } catch { /* prune is best-effort */ }
92
114
  }
@@ -14,6 +14,7 @@
14
14
  // or corrupt the file. Best-effort throughout and silent — the hook is fire-and-forget and must never
15
15
  // fail Claude (the shell wrapper swallows errors and always exits 0).
16
16
  const fs = require('node:fs');
17
+ const path = require('node:path');
17
18
 
18
19
  const [, , file, pane, src, ts, host = ''] = process.argv;
19
20
  if (!file || !pane || !src) process.exit(0);
@@ -34,7 +35,26 @@ const nap = (ms) => {
34
35
  catch { const end = Date.now() + ms; while (Date.now() < end) { /* spin */ } }
35
36
  };
36
37
 
37
- try { fs.mkdirSync(path.dirname(file), { recursive: true }); } catch { /* ignore */ }
38
+ function ensurePrivateDirectory(directory) {
39
+ const resolved = path.resolve(directory);
40
+ const parsed = path.parse(resolved);
41
+ const parts = resolved.slice(parsed.root.length).split(path.sep).filter(Boolean);
42
+ const privateRoot = parts.indexOf('.handmux');
43
+ if (privateRoot < 0) {
44
+ fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
45
+ return;
46
+ }
47
+ for (let i = privateRoot; i < parts.length; i++) {
48
+ const current = path.join(parsed.root, ...parts.slice(0, i + 1));
49
+ fs.mkdirSync(current, { recursive: true, mode: 0o700 });
50
+ fs.chmodSync(current, 0o700);
51
+ }
52
+ }
53
+
54
+ try {
55
+ ensurePrivateDirectory(path.dirname(file));
56
+ if (fs.existsSync(file)) fs.chmodSync(file, 0o600);
57
+ } catch { /* ignore */ }
38
58
 
39
59
  function update() {
40
60
  let obj = {};
@@ -61,14 +81,15 @@ function update() {
61
81
  obj[pane] = { ts: Number(ts) || 0, src, host, payload };
62
82
  }
63
83
  const tmp = `${file}.${process.pid}.tmp`;
64
- fs.writeFileSync(tmp, JSON.stringify(obj));
84
+ fs.writeFileSync(tmp, JSON.stringify(obj), { mode: 0o600 });
65
85
  fs.renameSync(tmp, file); // atomic: a torn write can't corrupt the file
86
+ fs.chmodSync(file, 0o600);
66
87
  }
67
88
 
68
89
  const lock = `${file}.lock`;
69
90
  let held = false;
70
91
  for (let i = 0; i < 60 && !held; i++) { // ~0.9s budget, then write lockless (best-effort)
71
- try { fs.closeSync(fs.openSync(lock, 'wx')); held = true; } // O_EXCL → atomic "I hold it"
92
+ try { fs.closeSync(fs.openSync(lock, 'wx', 0o600)); held = true; } // O_EXCL → atomic "I hold it"
72
93
  catch {
73
94
  try { if (Date.now() - fs.statSync(lock).mtimeMs > 3000) fs.unlinkSync(lock); } catch { /* steal a stale lock */ }
74
95
  nap(15);
@@ -0,0 +1,126 @@
1
+ {
2
+ "name": "handmux",
3
+ "version": "0.24.0",
4
+ "description": "A mobile vibe-coding cockpit — built on tmux: drive your live session, Claude Code / Codex — anything a terminal can run — from your phone.",
5
+ "type": "module",
6
+ "license": "AGPL-3.0-only",
7
+ "author": "yuanyuanzijin",
8
+ "homepage": "https://handmux.com",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/handmux/handmux.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/handmux/handmux/issues"
15
+ },
16
+ "keywords": [
17
+ "tmux",
18
+ "mobile",
19
+ "terminal",
20
+ "claude-code",
21
+ "remote",
22
+ "pwa"
23
+ ],
24
+ "engines": {
25
+ "node": ">=20"
26
+ },
27
+ "bin": {
28
+ "handmux": "bin/handmux.js"
29
+ },
30
+ "files": [
31
+ "bin/handmux.js",
32
+ "dist",
33
+ "README.md",
34
+ "README.zh-CN.md",
35
+ "LICENSE"
36
+ ],
37
+ "scripts": {
38
+ "start": "npm run build:server && node dist/bin/handmux.js start",
39
+ "dev": "npm run build:server && node dist/bin/handmux.js start --foreground",
40
+ "handmux": "npm run build:server && node dist/bin/handmux.js",
41
+ "build:server": "node scripts/build-server.mjs",
42
+ "bundle": "node scripts/bundle-web.mjs",
43
+ "prepack": "npm run bundle && npm run build:server",
44
+ "typecheck:source": "node scripts/check-typescript-product-source.mjs",
45
+ "typecheck": "tsc --project tsconfig.json",
46
+ "test": "npm run build:server && vitest run",
47
+ "test:pack": "node scripts/pack-smoke.mjs",
48
+ "test:workspace-host": "npm run build:server && HANDMUX_WORKSPACE_HOST=1 vitest run test/workspaceHost.test.js",
49
+ "test:workspace-docker": "npm run build:server && HANDMUX_WORKSPACE_DOCKER=1 vitest run test/workspaceDocker.test.js"
50
+ },
51
+ "dependencies": {
52
+ "@clack/prompts": "0.11.0",
53
+ "busboy": "^1.6.0",
54
+ "compression": "^1.8.1",
55
+ "express": "^4.19.2",
56
+ "testcafe-hammerhead": "31.7.8",
57
+ "tunlite": "^0.10.0",
58
+ "web-push": "^3.6.7",
59
+ "ws": "^8.21.0"
60
+ },
61
+ "optionalDependencies": {
62
+ "qrcode-terminal": "^0.12.0"
63
+ },
64
+ "devDependencies": {
65
+ "@types/busboy": "^1.5.4",
66
+ "@types/compression": "^1.8.1",
67
+ "@types/express": "4.17.25",
68
+ "@types/node": "20.19.43",
69
+ "@types/supertest": "7.2.1",
70
+ "@types/web-push": "^3.6.4",
71
+ "@types/ws": "^8.18.1",
72
+ "supertest": "^7.0.0",
73
+ "typescript": "6.0.3",
74
+ "vitest": "^2.0.0"
75
+ },
76
+ "whatsNew": [
77
+ {
78
+ "version": "0.24.0",
79
+ "date": "2026-08-11",
80
+ "zh": "Codex 任务进度与 Goal 原生上线",
81
+ "en": "Native Codex task progress and Goals"
82
+ },
83
+ {
84
+ "version": "0.23.0",
85
+ "date": "2026-08-09",
86
+ "zh": "Codex 托管对话正式上线",
87
+ "en": "Managed Codex chat is here"
88
+ },
89
+ {
90
+ "version": "0.22.1",
91
+ "date": "2026-08-04",
92
+ "zh": "网页宽度与网站版本分开控制",
93
+ "en": "Separate page width and site-version controls"
94
+ },
95
+ {
96
+ "version": "0.22.0",
97
+ "date": "2026-08-03",
98
+ "zh": "全屏设置 · 窗口与分屏管理更清晰",
99
+ "en": "Full-screen Settings · clearer window and pane management"
100
+ },
101
+ {
102
+ "version": "0.21.0",
103
+ "date": "2026-08-03",
104
+ "zh": "网页预览:手机直连、电脑代理与静态目录",
105
+ "en": "Web preview: direct, computer proxy, and static folders"
106
+ },
107
+ {
108
+ "version": "0.20.3",
109
+ "date": "2026-08-01",
110
+ "zh": "实时终端显示更稳定流畅",
111
+ "en": "More stable, smoother live terminals"
112
+ },
113
+ {
114
+ "version": "0.20.2",
115
+ "date": "2026-07-27",
116
+ "zh": "实时终端长时间运行更流畅",
117
+ "en": "Smoother long-running live terminals"
118
+ },
119
+ {
120
+ "version": "0.20.1",
121
+ "date": "2026-07-26",
122
+ "zh": "聊天发送修复 · 电脑键盘不中断",
123
+ "en": "Reliable chat sending · uninterrupted desktop input"
124
+ }
125
+ ]
126
+ }