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 CHANGED
@@ -1,5 +1,9 @@
1
1
  # Remote Claude 飞书客户端配置
2
2
 
3
+ # Claude CLI 命令(可选,默认 claude)
4
+ # 支持多词命令,如:ccr code、/usr/local/bin/claude
5
+ # CLAUDE_COMMAND=claude
6
+
3
7
  # 飞书应用配置(必填)
4
8
  # 在飞书开发者后台创建应用获取: https://open.feishu.cn/app
5
9
  FEISHU_APP_ID=cli_xxxxx
@@ -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 = {sname: cid for cid, sname in self._chat_bindings.items() if cid.startswith("oc_")}
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 = {sname: cid for cid, sname in self._chat_bindings.items() if cid.startswith("oc_")}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remote-claude",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "双端共享 Claude CLI 工具",
5
5
  "bin": {
6
6
  "remote-claude": "bin/remote-claude",
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
- os.execvpe("claude", ["claude"] + self.claude_args, child_env)
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, debug_screen=debug_screen,
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
- run_server(args.session_name, args.claude_args, debug_screen=args.debug_screen,
836
- debug_verbose=args.debug_verbose)
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)