remote-claude 0.1.5 → 0.1.6
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/package.json +1 -1
- package/remote_claude.py +11 -1
- package/server/server.py +25 -1
- package/utils/session.py +6 -0
package/package.json
CHANGED
package/remote_claude.py
CHANGED
|
@@ -30,7 +30,8 @@ from utils.session import (
|
|
|
30
30
|
list_active_sessions, is_session_active, cleanup_session,
|
|
31
31
|
is_lark_running, get_lark_pid, get_lark_status, get_lark_pid_file,
|
|
32
32
|
save_lark_status, cleanup_lark,
|
|
33
|
-
USER_DATA_DIR, ensure_user_data_dir, get_lark_log_file
|
|
33
|
+
USER_DATA_DIR, ensure_user_data_dir, get_lark_log_file,
|
|
34
|
+
get_env_snapshot_path
|
|
34
35
|
)
|
|
35
36
|
|
|
36
37
|
|
|
@@ -56,6 +57,15 @@ def cmd_start(args):
|
|
|
56
57
|
|
|
57
58
|
ensure_socket_dir()
|
|
58
59
|
|
|
60
|
+
# 将当前 shell 的完整环境变量保存到快照文件(权限 0600 防止密钥泄露)
|
|
61
|
+
# tmux new-session 继承的是 tmux 服务器的全局环境,而非调用方 shell 的环境,
|
|
62
|
+
# 通过快照文件将完整环境传递给 server.py 的 _start_pty()
|
|
63
|
+
import json
|
|
64
|
+
env_snapshot_path = get_env_snapshot_path(session_name)
|
|
65
|
+
env_fd = os.open(str(env_snapshot_path), os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
|
|
66
|
+
with os.fdopen(env_fd, 'w') as f:
|
|
67
|
+
json.dump(dict(os.environ), f)
|
|
68
|
+
|
|
59
69
|
# 构建 server 命令
|
|
60
70
|
server_script = SCRIPT_DIR / "server" / "server.py"
|
|
61
71
|
claude_args = args.claude_args if args.claude_args else []
|
package/server/server.py
CHANGED
|
@@ -606,9 +606,32 @@ class ProxyServer:
|
|
|
606
606
|
|
|
607
607
|
def _start_pty(self):
|
|
608
608
|
"""启动 PTY 并运行 Claude"""
|
|
609
|
-
|
|
609
|
+
# 加载环境变量快照(从 cmd_start 保存的快照文件恢复调用方 shell 的完整环境)
|
|
610
|
+
from utils.session import get_env_snapshot_path
|
|
611
|
+
import json as _json
|
|
612
|
+
env_snapshot_path = get_env_snapshot_path(self.session_name)
|
|
613
|
+
_extra_env = {}
|
|
614
|
+
try:
|
|
615
|
+
with open(env_snapshot_path) as _f:
|
|
616
|
+
_extra_env = _json.load(_f)
|
|
617
|
+
except Exception:
|
|
618
|
+
pass
|
|
619
|
+
|
|
620
|
+
try:
|
|
621
|
+
pid, fd = pty.fork()
|
|
622
|
+
except OSError:
|
|
623
|
+
env_snapshot_path.unlink(missing_ok=True)
|
|
624
|
+
raise
|
|
610
625
|
|
|
611
626
|
if pid == 0:
|
|
627
|
+
# 子进程:恢复调用方 shell 的完整环境变量
|
|
628
|
+
for k, v in _extra_env.items():
|
|
629
|
+
os.environ[k] = v
|
|
630
|
+
# 环境已加载到内存,立即删除快照文件(execvp 前销毁)
|
|
631
|
+
try:
|
|
632
|
+
env_snapshot_path.unlink()
|
|
633
|
+
except Exception:
|
|
634
|
+
pass
|
|
612
635
|
# 恢复 TERM 以支持 kitty keyboard protocol(Shift+Enter 等扩展键)
|
|
613
636
|
# tmux 会将 TERM 改为 tmux-256color,导致 Claude CLI 不启用 kitty protocol
|
|
614
637
|
os.environ['TERM'] = 'xterm-256color'
|
|
@@ -616,6 +639,7 @@ class ProxyServer:
|
|
|
616
639
|
for key in ('TMUX', 'TMUX_PANE'):
|
|
617
640
|
os.environ.pop(key, None)
|
|
618
641
|
os.execvp("claude", ["claude"] + self.claude_args)
|
|
642
|
+
os._exit(1) # execvp 失败时兜底退出
|
|
619
643
|
else:
|
|
620
644
|
# 父进程
|
|
621
645
|
self.master_fd = fd
|
package/utils/session.py
CHANGED
|
@@ -60,6 +60,11 @@ def get_mq_path(session_name: str) -> Path:
|
|
|
60
60
|
return SOCKET_DIR / f"{_safe_filename(session_name)}.mq"
|
|
61
61
|
|
|
62
62
|
|
|
63
|
+
def get_env_snapshot_path(session_name: str) -> Path:
|
|
64
|
+
"""获取环境变量快照文件路径(cmd_start 写入,_start_pty 读取后删除)"""
|
|
65
|
+
return SOCKET_DIR / f"{_safe_filename(session_name)}_env.json"
|
|
66
|
+
|
|
67
|
+
|
|
63
68
|
def ensure_socket_dir():
|
|
64
69
|
"""确保 socket 目录存在"""
|
|
65
70
|
SOCKET_DIR.mkdir(parents=True, exist_ok=True)
|
|
@@ -248,6 +253,7 @@ def cleanup_session(session_name: str):
|
|
|
248
253
|
sock_path.unlink(missing_ok=True)
|
|
249
254
|
pid_file.unlink(missing_ok=True)
|
|
250
255
|
get_mq_path(session_name).unlink(missing_ok=True)
|
|
256
|
+
get_env_snapshot_path(session_name).unlink(missing_ok=True)
|
|
251
257
|
|
|
252
258
|
|
|
253
259
|
def is_session_active(session_name: str) -> bool:
|