handmux 0.27.2 → 0.29.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 (78) hide show
  1. package/bin/handmux.js +2 -2
  2. package/dist/bin/handmux.js +51 -21
  3. package/dist/connectors/bridgeClient.js +30 -14
  4. package/dist/connectors/claude/index.js +78 -23
  5. package/dist/hooks/handmux-write.cjs +11 -0
  6. package/dist/package.json +13 -13
  7. package/dist/public/assets/index-BjnnFTLC.css +32 -0
  8. package/dist/public/assets/index-D0tDAqO-.js +356 -0
  9. package/dist/public/index.html +2 -2
  10. package/dist/src/agent-runtime/bridgeTransport.js +31 -4
  11. package/dist/src/agent-runtime/builtinRuntime.js +10 -2
  12. package/dist/src/agent-runtime/conversation.js +3 -3
  13. package/dist/src/agent-runtime/conversationStore.js +2 -2
  14. package/dist/src/agent-runtime/inbox.js +20 -2
  15. package/dist/src/agent-runtime/run.js +5 -1
  16. package/dist/src/agents/claudeConversation.js +69 -6
  17. package/dist/src/agents/claudeLocalCommand.js +54 -0
  18. package/dist/src/agents/claudeNativeTail.js +271 -0
  19. package/dist/src/agents/claudePaneInput.js +47 -0
  20. package/dist/src/agents/codexConversation.js +46 -0
  21. package/dist/src/agents/piInboxBridge.js +64 -5
  22. package/dist/src/agents/processIdentity.js +13 -6
  23. package/dist/src/apiAccountProviders.js +5 -4
  24. package/dist/src/browser/bootstrap.js +5 -0
  25. package/dist/src/browser/credentials.js +68 -0
  26. package/dist/src/browser/manager.js +40 -5
  27. package/dist/src/browser/publicProxy.js +15 -5
  28. package/dist/src/browser/routes.js +7 -0
  29. package/dist/src/browser/workerClient.js +174 -9
  30. package/dist/src/browser/workerServer.js +13 -1
  31. package/dist/src/claudeEvents.js +94 -38
  32. package/dist/src/cli/authCmd.js +202 -0
  33. package/dist/src/cli/authDefaults.js +18 -0
  34. package/dist/src/cli/claudeHooks.js +3 -3
  35. package/dist/src/cli/hookScaffold.js +5 -1
  36. package/dist/src/cli/i18n/en.js +66 -11
  37. package/dist/src/cli/i18n/zh.js +66 -11
  38. package/dist/src/cli/options.js +7 -13
  39. package/dist/src/cli/pushCmd.js +24 -11
  40. package/dist/src/cli/setupModel.js +28 -6
  41. package/dist/src/cli/setupWizard.js +47 -25
  42. package/dist/src/cli/shortcutEditor.js +20 -2
  43. package/dist/src/cli/supervisor.js +5 -7
  44. package/dist/src/codexAppServer.js +25 -15
  45. package/dist/src/codexTranscriptParse.js +40 -1
  46. package/dist/src/codexUsageSnapshot.js +1 -1
  47. package/dist/src/deviceAccess.js +134 -0
  48. package/dist/src/deviceAuth/control.js +154 -0
  49. package/dist/src/deviceAuth/http.js +321 -0
  50. package/dist/src/deviceAuth/service.js +626 -0
  51. package/dist/src/docs.js +3 -0
  52. package/dist/src/httpApi.js +8 -3
  53. package/dist/src/paneInput.js +16 -2
  54. package/dist/src/previewServer.js +26 -2
  55. package/dist/src/previews.js +37 -13
  56. package/dist/src/projectTask/backup.js +28 -0
  57. package/dist/src/projectTask/lock.js +1 -1
  58. package/dist/src/projectTask/migrations.js +30 -1
  59. package/dist/src/projectTask/runtime.js +7 -1
  60. package/dist/src/projectTask/schema.js +2 -2
  61. package/dist/src/push.js +44 -13
  62. package/dist/src/requestAuthority.js +8 -0
  63. package/dist/src/requestOrigin.js +62 -0
  64. package/dist/src/routes/agents.js +3 -1
  65. package/dist/src/routes/files.js +2 -0
  66. package/dist/src/routes/previews.js +9 -3
  67. package/dist/src/routes/push.js +55 -58
  68. package/dist/src/routes/system.js +4 -4
  69. package/dist/src/routes/terminal.js +9 -1
  70. package/dist/src/server.js +88 -12
  71. package/dist/src/terminalStream.js +90 -5
  72. package/dist/src/tmux/commands.js +2 -0
  73. package/dist/src/transcriptParse.js +4 -1
  74. package/dist/src/workspace/environment.js +14 -5
  75. package/dist/src/workspace/runtime.js +16 -1
  76. package/package.json +4 -4
  77. package/dist/public/assets/index-B4pKf3IW.js +0 -356
  78. package/dist/public/assets/index-Cdrh2CUp.css +0 -32
@@ -3,9 +3,11 @@ import { execFile } from 'node:child_process';
3
3
  import fsp from 'node:fs/promises';
4
4
  import { promisify } from 'node:util';
5
5
  const execFileAsync = promisify(execFile);
6
- function normalizeMacBootTime(value) {
7
- const seconds = String(value).match(/\bsec\s*=\s*(\d+)\b/)?.[1];
8
- return seconds && seconds !== '0' ? seconds : null;
6
+ const UUID_PATTERN = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}';
7
+ const UUID = new RegExp(`^${UUID_PATTERN}$`, 'i');
8
+ const MAC_BOOT_IDENTITY = new RegExp(`^darwin:${UUID_PATTERN}$`, 'i');
9
+ export function isLegacyMacBootIdentity(value) {
10
+ return typeof value === 'string' && /^[1-9][0-9]*$/.test(value);
9
11
  }
10
12
  function environmentId(bootIdentity, tmuxServerId) {
11
13
  return crypto
@@ -18,8 +20,14 @@ export function detectEnvironmentChange(previous, observed) {
18
20
  return { status: 'unknown' };
19
21
  if (!previous)
20
22
  return { status: 'initial', current: observed };
21
- if (previous.bootIdentity !== observed.bootIdentity)
23
+ // Old macOS checkpoints used a wall-clock boot timestamp, which can drift during the same boot.
24
+ // It cannot be compared with a boot session UUID. During migration the tmux generation is the
25
+ // available continuity evidence; a replaced/missing tmux still produces a recoverable change below.
26
+ const migratingMacIdentity = isLegacyMacBootIdentity(previous.bootIdentity)
27
+ && MAC_BOOT_IDENTITY.test(observed.bootIdentity);
28
+ if (!migratingMacIdentity && previous.bootIdentity !== observed.bootIdentity) {
22
29
  return { status: 'changed', reason: 'boot-changed', current: observed };
30
+ }
23
31
  if (observed.status === 'absent') {
24
32
  return previous.tmuxServerId
25
33
  ? { status: 'changed', reason: 'tmux-changed', current: observed }
@@ -39,7 +47,8 @@ export function createBootIdentityProvider({ platform = process.platform, readFi
39
47
  return identity || null;
40
48
  }
41
49
  if (platform === 'darwin') {
42
- return normalizeMacBootTime((await exec('sysctl', ['-n', 'kern.boottime'])).stdout);
50
+ const identity = String((await exec('sysctl', ['-n', 'kern.bootsessionuuid'])).stdout).trim();
51
+ return UUID.test(identity) ? `darwin:${identity.toLowerCase()}` : null;
43
52
  }
44
53
  return null;
45
54
  }
@@ -1,4 +1,5 @@
1
1
  import crypto from 'node:crypto';
2
+ import { isLegacyMacBootIdentity } from './environment.js';
2
3
  import { buildRestorePlan } from './planner.js';
3
4
  import { executeRestore } from './restore.js';
4
5
  import { createOperationManager, normalizeRestoreRequest, } from './operations.js';
@@ -162,11 +163,25 @@ export function createWorkspaceRuntime({ store, tmux, lock, checkpointer, now =
162
163
  await start();
163
164
  const state = await planRestore(request);
164
165
  const serverNow = new Date(now()).toISOString();
165
- const promptEligible = Boolean(!state.request.historical
166
+ let promptEligible = Boolean(!state.request.historical
166
167
  && state.recovery
167
168
  && state.recovery.resolvedAt === null
168
169
  && state.recovery.pendingSessionIds.length > 0
169
170
  && Date.parse(state.recovery.expiresAt) > now());
171
+ // A numeric macOS boot timestamp could have archived this still-running tmux generation.
172
+ // Suppress only the automatic prompt when a fresh read proves that exact server survived.
173
+ // Keep both checkpoint and recovery records intact for explicit historical recovery.
174
+ if (promptEligible && state.plan.changeReason === 'boot-changed'
175
+ && isLegacyMacBootIdentity(state.checkpoint.environment.bootIdentity)
176
+ && state.checkpoint.environment.tmuxServerId) {
177
+ try {
178
+ const observed = await tmux.observeEnvironment?.({ readOnly: true });
179
+ if (observed?.status === 'present'
180
+ && observed.tmuxServerId === state.checkpoint.environment.tmuxServerId)
181
+ promptEligible = false;
182
+ }
183
+ catch { /* Without continuity evidence, retain the recovery prompt. */ }
184
+ }
170
185
  return Object.freeze({ ...state.plan, mapping: state.recovery?.mapping || null, serverNow, promptEligible });
171
186
  }
172
187
  async function getProtectionStatus() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "handmux",
3
- "version": "0.27.2",
3
+ "version": "0.29.0",
4
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
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -75,13 +75,13 @@
75
75
  "vitest": "^2.0.0"
76
76
  },
77
77
  "whatsNew": [
78
+ {"version":"0.29.0","date":"2026-09-16","zh":"可信设备与可信地址保护","en":"Trusted device and address protection"},
79
+ {"version":"0.28.0","date":"2026-09-11","zh":"Claude 对话更可靠 · 工具图片预览","en":"More reliable Claude chat · tool image previews"},
78
80
  {"version":"0.27.2","date":"2026-09-08","zh":"修复多标签页偶发鉴权弹窗","en":"Fix intermittent authentication prompts across tabs"},
79
81
  {"version":"0.27.1","date":"2026-09-08","zh":"腾讯语音 · 终端光标修复 · Codex 对话恢复","en":"Tencent voice · terminal cursor fixes · Codex chat recovery"},
80
82
  {"version":"0.27.0","date":"2026-09-05","zh":"腾讯语音输入 · 终端输入更准确","en":"Tencent voice input · more accurate terminal typing"},
81
83
  {"version":"0.26.0","date":"2026-09-04","zh":"对话精细复制 · Pi 上下文状态","en":"Precise chat copy · Pi context status"},
82
84
  {"version":"0.25.2","date":"2026-09-02","zh":"修复 Agent 对话断流、排队与工具路径","en":"Fixed Agent chat streams, queues, and tool paths"},
83
- {"version":"0.25.1","date":"2026-09-02","zh":"修复 Codex 幽灵任务与对话空白","en":"Fixed Codex ghost tasks and blank chats"},
84
- {"version":"0.25.0","date":"2026-09-01","zh":"Pi 原生接入 · 多 Agent 对话更可靠","en":"Native Pi support · more reliable agent chat"},
85
- {"version":"0.24.0","date":"2026-08-11","zh":"Codex 任务进度与 Goal 原生上线","en":"Native Codex task progress and Goals"}
85
+ {"version":"0.25.1","date":"2026-09-02","zh":"修复 Codex 幽灵任务与对话空白","en":"Fixed Codex ghost tasks and blank chats"}
86
86
  ]
87
87
  }