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.
- package/bin/handmux.js +2 -2
- package/dist/bin/handmux.js +51 -21
- package/dist/connectors/bridgeClient.js +30 -14
- package/dist/connectors/claude/index.js +78 -23
- package/dist/hooks/handmux-write.cjs +11 -0
- package/dist/package.json +13 -13
- package/dist/public/assets/index-BjnnFTLC.css +32 -0
- package/dist/public/assets/index-D0tDAqO-.js +356 -0
- package/dist/public/index.html +2 -2
- package/dist/src/agent-runtime/bridgeTransport.js +31 -4
- package/dist/src/agent-runtime/builtinRuntime.js +10 -2
- package/dist/src/agent-runtime/conversation.js +3 -3
- package/dist/src/agent-runtime/conversationStore.js +2 -2
- package/dist/src/agent-runtime/inbox.js +20 -2
- package/dist/src/agent-runtime/run.js +5 -1
- package/dist/src/agents/claudeConversation.js +69 -6
- package/dist/src/agents/claudeLocalCommand.js +54 -0
- package/dist/src/agents/claudeNativeTail.js +271 -0
- package/dist/src/agents/claudePaneInput.js +47 -0
- package/dist/src/agents/codexConversation.js +46 -0
- package/dist/src/agents/piInboxBridge.js +64 -5
- package/dist/src/agents/processIdentity.js +13 -6
- package/dist/src/apiAccountProviders.js +5 -4
- package/dist/src/browser/bootstrap.js +5 -0
- package/dist/src/browser/credentials.js +68 -0
- package/dist/src/browser/manager.js +40 -5
- package/dist/src/browser/publicProxy.js +15 -5
- package/dist/src/browser/routes.js +7 -0
- package/dist/src/browser/workerClient.js +174 -9
- package/dist/src/browser/workerServer.js +13 -1
- package/dist/src/claudeEvents.js +94 -38
- package/dist/src/cli/authCmd.js +202 -0
- package/dist/src/cli/authDefaults.js +18 -0
- package/dist/src/cli/claudeHooks.js +3 -3
- package/dist/src/cli/hookScaffold.js +5 -1
- package/dist/src/cli/i18n/en.js +66 -11
- package/dist/src/cli/i18n/zh.js +66 -11
- package/dist/src/cli/options.js +7 -13
- package/dist/src/cli/pushCmd.js +24 -11
- package/dist/src/cli/setupModel.js +28 -6
- package/dist/src/cli/setupWizard.js +47 -25
- package/dist/src/cli/shortcutEditor.js +20 -2
- package/dist/src/cli/supervisor.js +5 -7
- package/dist/src/codexAppServer.js +25 -15
- package/dist/src/codexTranscriptParse.js +40 -1
- package/dist/src/codexUsageSnapshot.js +1 -1
- package/dist/src/deviceAccess.js +134 -0
- package/dist/src/deviceAuth/control.js +154 -0
- package/dist/src/deviceAuth/http.js +321 -0
- package/dist/src/deviceAuth/service.js +626 -0
- package/dist/src/docs.js +3 -0
- package/dist/src/httpApi.js +8 -3
- package/dist/src/paneInput.js +16 -2
- package/dist/src/previewServer.js +26 -2
- package/dist/src/previews.js +37 -13
- package/dist/src/projectTask/backup.js +28 -0
- package/dist/src/projectTask/lock.js +1 -1
- package/dist/src/projectTask/migrations.js +30 -1
- package/dist/src/projectTask/runtime.js +7 -1
- package/dist/src/projectTask/schema.js +2 -2
- package/dist/src/push.js +44 -13
- package/dist/src/requestAuthority.js +8 -0
- package/dist/src/requestOrigin.js +62 -0
- package/dist/src/routes/agents.js +3 -1
- package/dist/src/routes/files.js +2 -0
- package/dist/src/routes/previews.js +9 -3
- package/dist/src/routes/push.js +55 -58
- package/dist/src/routes/system.js +4 -4
- package/dist/src/routes/terminal.js +9 -1
- package/dist/src/server.js +88 -12
- package/dist/src/terminalStream.js +90 -5
- package/dist/src/tmux/commands.js +2 -0
- package/dist/src/transcriptParse.js +4 -1
- package/dist/src/workspace/environment.js +14 -5
- package/dist/src/workspace/runtime.js +16 -1
- package/package.json +4 -4
- package/dist/public/assets/index-B4pKf3IW.js +0 -356
- 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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
}
|