remote-claude 0.2.3 → 0.2.5
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/.env.example +4 -0
- package/lark_client/lark_handler.py +34 -2
- package/package.json +1 -1
- package/server/server.py +19 -6
package/.env.example
CHANGED
|
@@ -43,6 +43,7 @@ class LarkHandler:
|
|
|
43
43
|
|
|
44
44
|
_CHAT_BINDINGS_FILE = get_chat_bindings_file()
|
|
45
45
|
_OLD_CHAT_BINDINGS_FILE = Path("/tmp/remote-claude/lark_chat_bindings.json")
|
|
46
|
+
_LARK_GROUP_IDS_FILE = Path(get_chat_bindings_file()).parent / "lark_group_ids.json"
|
|
46
47
|
|
|
47
48
|
def __init__(self):
|
|
48
49
|
# 兼容迁移:旧绑定文件存在而新路径不存在时,自动迁移
|
|
@@ -61,6 +62,8 @@ class LarkHandler:
|
|
|
61
62
|
self._poller = SharedMemoryPoller(card_service)
|
|
62
63
|
# chat_id → session_name 持久化绑定(重启后自动恢复)
|
|
63
64
|
self._chat_bindings: Dict[str, str] = self._load_chat_bindings()
|
|
65
|
+
# 专属群聊 chat_id 集合(仅包含通过 /new-group 创建的群)
|
|
66
|
+
self._group_chat_ids: set = self._load_group_chat_ids()
|
|
64
67
|
# chat_id → CardSlice(用户主动断开后保留,供重连时冻结旧卡片)
|
|
65
68
|
self._detached_slices: Dict[str, CardSlice] = {}
|
|
66
69
|
|
|
@@ -83,6 +86,23 @@ class LarkHandler:
|
|
|
83
86
|
except Exception as e:
|
|
84
87
|
logger.warning(f"保存绑定失败: {e}")
|
|
85
88
|
|
|
89
|
+
def _load_group_chat_ids(self) -> set:
|
|
90
|
+
try:
|
|
91
|
+
if self._LARK_GROUP_IDS_FILE.exists():
|
|
92
|
+
return set(json.loads(self._LARK_GROUP_IDS_FILE.read_text()))
|
|
93
|
+
except Exception:
|
|
94
|
+
pass
|
|
95
|
+
return set()
|
|
96
|
+
|
|
97
|
+
def _save_group_chat_ids(self):
|
|
98
|
+
try:
|
|
99
|
+
ensure_user_data_dir()
|
|
100
|
+
self._LARK_GROUP_IDS_FILE.write_text(
|
|
101
|
+
json.dumps(list(self._group_chat_ids), ensure_ascii=False)
|
|
102
|
+
)
|
|
103
|
+
except Exception as e:
|
|
104
|
+
logger.warning(f"保存群聊 ID 失败: {e}")
|
|
105
|
+
|
|
86
106
|
def _remove_binding_by_chat(self, chat_id: str):
|
|
87
107
|
self._chat_bindings.pop(chat_id, None)
|
|
88
108
|
self._save_chat_bindings()
|
|
@@ -526,7 +546,11 @@ class LarkHandler:
|
|
|
526
546
|
"""显示快捷操作菜单(内嵌会话列表)"""
|
|
527
547
|
sessions = list_active_sessions()
|
|
528
548
|
current = self._chat_sessions.get(chat_id)
|
|
529
|
-
session_groups = {
|
|
549
|
+
session_groups = {
|
|
550
|
+
self._chat_bindings[cid]: cid
|
|
551
|
+
for cid in self._group_chat_ids
|
|
552
|
+
if cid in self._chat_bindings
|
|
553
|
+
}
|
|
530
554
|
card = build_menu_card(sessions, current_session=current, session_groups=session_groups)
|
|
531
555
|
await self._send_or_update_card(chat_id, card, message_id)
|
|
532
556
|
|
|
@@ -570,7 +594,11 @@ class LarkHandler:
|
|
|
570
594
|
await card_service.send_text(chat_id, f"读取目录失败:{e}")
|
|
571
595
|
return
|
|
572
596
|
|
|
573
|
-
session_groups = {
|
|
597
|
+
session_groups = {
|
|
598
|
+
self._chat_bindings[cid]: cid
|
|
599
|
+
for cid in self._group_chat_ids
|
|
600
|
+
if cid in self._chat_bindings
|
|
601
|
+
}
|
|
574
602
|
card = build_dir_card(target, entries, sessions_info, tree=tree, session_groups=session_groups, page=page)
|
|
575
603
|
await self._send_or_update_card(chat_id, card, message_id)
|
|
576
604
|
|
|
@@ -630,6 +658,8 @@ class LarkHandler:
|
|
|
630
658
|
group_chat_id = create_data["data"]["chat_id"]
|
|
631
659
|
self._chat_bindings[group_chat_id] = session_name
|
|
632
660
|
self._save_chat_bindings()
|
|
661
|
+
self._group_chat_ids.add(group_chat_id)
|
|
662
|
+
self._save_group_chat_ids()
|
|
633
663
|
# 立即 attach,让新群即刻开始接收 Claude 输出
|
|
634
664
|
await self._attach(group_chat_id, session_name)
|
|
635
665
|
|
|
@@ -696,6 +726,8 @@ class LarkHandler:
|
|
|
696
726
|
|
|
697
727
|
# 无论 Feishu delete 是否成功,都清理本地绑定
|
|
698
728
|
self._remove_binding_by_chat(group_chat_id)
|
|
729
|
+
self._group_chat_ids.discard(group_chat_id)
|
|
730
|
+
self._save_group_chat_ids()
|
|
699
731
|
await self._detach(group_chat_id)
|
|
700
732
|
|
|
701
733
|
if feishu_ok:
|
package/package.json
CHANGED
package/server/server.py
CHANGED
|
@@ -35,9 +35,16 @@ from utils.protocol import (
|
|
|
35
35
|
)
|
|
36
36
|
from utils.session import (
|
|
37
37
|
get_socket_path, get_pid_file, ensure_socket_dir,
|
|
38
|
-
generate_client_id, cleanup_session, _safe_filename
|
|
38
|
+
generate_client_id, cleanup_session, _safe_filename, get_env_file
|
|
39
39
|
)
|
|
40
40
|
|
|
41
|
+
# 加载用户 .env 配置(支持 CLAUDE_COMMAND 等)
|
|
42
|
+
try:
|
|
43
|
+
from dotenv import load_dotenv
|
|
44
|
+
load_dotenv(get_env_file())
|
|
45
|
+
except Exception:
|
|
46
|
+
pass
|
|
47
|
+
|
|
41
48
|
try:
|
|
42
49
|
from stats import track as _track_stats
|
|
43
50
|
except Exception:
|
|
@@ -533,9 +540,11 @@ class ProxyServer:
|
|
|
533
540
|
"""Proxy Server"""
|
|
534
541
|
|
|
535
542
|
def __init__(self, session_name: str, claude_args: list = None,
|
|
543
|
+
claude_cmd: str = "claude",
|
|
536
544
|
debug_screen: bool = False, debug_verbose: bool = False):
|
|
537
545
|
self.session_name = session_name
|
|
538
546
|
self.claude_args = claude_args or []
|
|
547
|
+
self.claude_cmd = claude_cmd
|
|
539
548
|
self.debug_screen = debug_screen
|
|
540
549
|
self.debug_verbose = debug_verbose
|
|
541
550
|
self.socket_path = get_socket_path(session_name)
|
|
@@ -638,7 +647,9 @@ class ProxyServer:
|
|
|
638
647
|
# 清除 tmux 标识变量(PTY 数据不经过 tmux,不应让 Claude CLI 误判终端环境)
|
|
639
648
|
child_env.pop('TMUX', None)
|
|
640
649
|
child_env.pop('TMUX_PANE', None)
|
|
641
|
-
|
|
650
|
+
import shlex as _shlex
|
|
651
|
+
_cmd_parts = _shlex.split(self.claude_cmd)
|
|
652
|
+
os.execvpe(_cmd_parts[0], _cmd_parts + self.claude_args, child_env)
|
|
642
653
|
os._exit(1) # execvpe 失败时兜底退出
|
|
643
654
|
else:
|
|
644
655
|
# 父进程
|
|
@@ -801,10 +812,11 @@ class ProxyServer:
|
|
|
801
812
|
|
|
802
813
|
|
|
803
814
|
def run_server(session_name: str, claude_args: list = None,
|
|
815
|
+
claude_cmd: str = "claude",
|
|
804
816
|
debug_screen: bool = False, debug_verbose: bool = False):
|
|
805
817
|
"""运行服务器"""
|
|
806
|
-
server = ProxyServer(session_name, claude_args,
|
|
807
|
-
debug_verbose=debug_verbose)
|
|
818
|
+
server = ProxyServer(session_name, claude_args, claude_cmd=claude_cmd,
|
|
819
|
+
debug_screen=debug_screen, debug_verbose=debug_verbose)
|
|
808
820
|
|
|
809
821
|
# 信号处理
|
|
810
822
|
def signal_handler(signum, frame):
|
|
@@ -832,5 +844,6 @@ if __name__ == "__main__":
|
|
|
832
844
|
help="debug 日志输出完整诊断信息(indicator、repr 等)")
|
|
833
845
|
args = parser.parse_args()
|
|
834
846
|
|
|
835
|
-
|
|
836
|
-
|
|
847
|
+
claude_cmd = os.environ.get("CLAUDE_COMMAND", "claude")
|
|
848
|
+
run_server(args.session_name, args.claude_args, claude_cmd=claude_cmd,
|
|
849
|
+
debug_screen=args.debug_screen, debug_verbose=args.debug_verbose)
|