remote-claude 1.0.0 → 1.0.2

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.
@@ -1202,7 +1202,8 @@ def build_session_closed_card(session_name: str) -> Dict[str, Any]:
1202
1202
 
1203
1203
  def build_menu_card(sessions: List[Dict], current_session: Optional[str] = None,
1204
1204
  session_groups: Optional[Dict[str, str]] = None, page: int = 0,
1205
- notify_enabled: bool = True, urgent_enabled: bool = False) -> Dict[str, Any]:
1205
+ notify_enabled: bool = True, urgent_enabled: bool = False,
1206
+ bypass_enabled: bool = False) -> Dict[str, Any]:
1206
1207
  """构建快捷操作菜单卡片(/menu 和 /list 共用):内嵌会话列表 + 快捷操作"""
1207
1208
  elements = []
1208
1209
 
@@ -1301,6 +1302,14 @@ def build_menu_card(sessions: List[Dict], current_session: Optional[str] = None,
1301
1302
  ]
1302
1303
  })
1303
1304
 
1305
+ bypass_label = "🔓 新会话bypass: 开" if bypass_enabled else "🔒 新会话bypass: 关"
1306
+ elements.append({
1307
+ "tag": "button",
1308
+ "text": {"tag": "plain_text", "content": bypass_label},
1309
+ "type": "default",
1310
+ "behaviors": [{"type": "callback", "value": {"action": "menu_toggle_bypass"}}]
1311
+ })
1312
+
1304
1313
  return {
1305
1314
  "schema": "2.0",
1306
1315
  "config": {"wide_screen_mode": True},
@@ -319,6 +319,8 @@ class LarkHandler:
319
319
  script_dir = Path(__file__).parent.parent.absolute()
320
320
  server_script = script_dir / "server" / "server.py"
321
321
  cmd = [sys.executable, str(server_script), session_name]
322
+ if self._poller.get_bypass_enabled():
323
+ cmd += ["--", "--dangerously-skip-permissions", "--permission-mode=dontAsk"]
322
324
 
323
325
  logger.info(f"启动会话: {session_name}, 工作目录: {work_dir}, 命令: {' '.join(cmd)}")
324
326
  _track_stats('lark', 'cmd_start', session_name=session_name, chat_id=chat_id)
@@ -408,6 +410,8 @@ class LarkHandler:
408
410
  script_dir = Path(__file__).parent.parent.absolute()
409
411
  server_script = script_dir / "server" / "server.py"
410
412
  cmd = [sys.executable, str(server_script), session_name]
413
+ if self._poller.get_bypass_enabled():
414
+ cmd += ["--", "--dangerously-skip-permissions", "--permission-mode=dontAsk"]
411
415
 
412
416
  try:
413
417
  import os as _os
@@ -607,7 +611,8 @@ class LarkHandler:
607
611
  }
608
612
  card = build_menu_card(sessions, current_session=current, session_groups=session_groups, page=page,
609
613
  notify_enabled=self._poller.get_notify_enabled(),
610
- urgent_enabled=self._poller.get_urgent_enabled())
614
+ urgent_enabled=self._poller.get_urgent_enabled(),
615
+ bypass_enabled=self._poller.get_bypass_enabled())
611
616
  await self._send_or_update_card(chat_id, card, message_id)
612
617
 
613
618
  async def _cmd_toggle_notify(self, user_id: str, chat_id: str,
@@ -624,6 +629,13 @@ class LarkHandler:
624
629
  self._poller.set_urgent_enabled(new_value)
625
630
  await self._cmd_menu(user_id, chat_id, message_id=message_id)
626
631
 
632
+ async def _cmd_toggle_bypass(self, user_id: str, chat_id: str,
633
+ message_id: Optional[str] = None):
634
+ """切换新会话 bypass 开关并刷新菜单卡片"""
635
+ new_value = not self._poller.get_bypass_enabled()
636
+ self._poller.set_bypass_enabled(new_value)
637
+ await self._cmd_menu(user_id, chat_id, message_id=message_id)
638
+
627
639
  async def _cmd_ls(self, user_id: str, chat_id: str, args: str,
628
640
  tree: bool = False, message_id: Optional[str] = None, page: int = 0):
629
641
  """查看目录文件结构"""
@@ -314,6 +314,10 @@ def handle_card_action(event: P2CardActionTrigger) -> P2CardActionTriggerRespons
314
314
  asyncio.create_task(handler._cmd_toggle_urgent(user_id, chat_id, message_id=message_id))
315
315
  return None
316
316
 
317
+ if action_type == "menu_toggle_bypass":
318
+ asyncio.create_task(handler._cmd_toggle_bypass(user_id, chat_id, message_id=message_id))
319
+ return None
320
+
317
321
  # 各卡片底部菜单按钮:辅助卡片就地→菜单,流式卡片降级新卡
318
322
  if action_type == "menu_open":
319
323
  asyncio.create_task(handler._cmd_menu(user_id, chat_id, message_id=message_id))
@@ -504,6 +504,17 @@ class SharedMemoryPoller:
504
504
  _save_urgent_enabled(enabled)
505
505
  logger.info(f"加急通知开关已{'开启' if enabled else '关闭'}")
506
506
 
507
+ def get_bypass_enabled(self) -> bool:
508
+ """获取新会话 bypass 开关状态"""
509
+ return _bypass_enabled
510
+
511
+ def set_bypass_enabled(self, enabled: bool) -> None:
512
+ """更新新会话 bypass 开关状态并持久化"""
513
+ global _bypass_enabled
514
+ _bypass_enabled = enabled
515
+ _save_bypass_enabled(enabled)
516
+ logger.info(f"新会话 bypass 开关已{'开启' if enabled else '关闭'}")
517
+
507
518
  @staticmethod
508
519
  def _compute_hash(
509
520
  blocks: list, status_line: Optional[dict],
@@ -534,6 +545,7 @@ def _is_ready(blocks: list, status_line: Optional[dict], option_block: Optional[
534
545
  _READY_COUNT_FILE = USER_DATA_DIR / "ready_notify_count"
535
546
  _NOTIFY_ENABLED_FILE = USER_DATA_DIR / "ready_notify_enabled"
536
547
  _URGENT_ENABLED_FILE = USER_DATA_DIR / "urgent_notify_enabled"
548
+ _BYPASS_ENABLED_FILE = USER_DATA_DIR / "bypass_enabled"
537
549
 
538
550
 
539
551
  def _load_notify_enabled() -> bool:
@@ -570,9 +582,27 @@ def _save_urgent_enabled(enabled: bool) -> None:
570
582
  logger.warning(f"_save_urgent_enabled 失败: {e}")
571
583
 
572
584
 
585
+ def _load_bypass_enabled() -> bool:
586
+ """读取新会话 bypass 开关状态,不存在或解析失败返回 False(默认关闭)"""
587
+ try:
588
+ return _BYPASS_ENABLED_FILE.read_text().strip() == "1"
589
+ except Exception:
590
+ return False
591
+
592
+
593
+ def _save_bypass_enabled(enabled: bool) -> None:
594
+ """持久化新会话 bypass 开关状态"""
595
+ try:
596
+ ensure_user_data_dir()
597
+ _BYPASS_ENABLED_FILE.write_text("1" if enabled else "0")
598
+ except Exception as e:
599
+ logger.warning(f"_save_bypass_enabled 失败: {e}")
600
+
601
+
573
602
  # 模块级开关状态:启动时加载一次
574
603
  _notify_enabled: bool = _load_notify_enabled()
575
604
  _urgent_enabled: bool = _load_urgent_enabled()
605
+ _bypass_enabled: bool = _load_bypass_enabled()
576
606
 
577
607
 
578
608
  def _increment_ready_count() -> int:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remote-claude",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "双端共享 Claude CLI 工具",
5
5
  "bin": {
6
6
  "remote-claude": "bin/remote-claude",
@@ -8,6 +8,7 @@
8
8
  "cl": "bin/cl"
9
9
  },
10
10
  "scripts": {
11
+ "preinstall": "bash scripts/preinstall.sh",
11
12
  "postinstall": "bash scripts/postinstall.sh"
12
13
  },
13
14
  "files": [
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+ # 升级前尝试停止旧的 lark 客户端(失败不报错)
3
+ remote-claude lark stop 2>/dev/null || true
4
+ exit 0
package/utils/session.py CHANGED
@@ -6,6 +6,7 @@
6
6
  - 通用工具
7
7
  """
8
8
 
9
+ import hashlib
9
10
  import os
10
11
  import subprocess
11
12
  import tempfile
@@ -19,6 +20,11 @@ SOCKET_DIR = Path("/tmp/remote-claude")
19
20
  USER_DATA_DIR = Path.home() / ".remote-claude"
20
21
  TMUX_SESSION_PREFIX = "rc-"
21
22
 
23
+ # macOS AF_UNIX sun_path 限制 104 字节
24
+ # /tmp/remote-claude/ = 19 字节, .sock = 5 字节
25
+ _MAX_SOCKET_PATH = 104
26
+ _MAX_FILENAME = _MAX_SOCKET_PATH - len(str(SOCKET_DIR)) - 1 - len(".sock")
27
+
22
28
 
23
29
  def get_env_file() -> Path:
24
30
  """获取 .env 配置文件路径"""
@@ -41,8 +47,12 @@ def ensure_user_data_dir():
41
47
 
42
48
 
43
49
  def _safe_filename(session_name: str) -> str:
44
- """将会话名转为安全文件名(/ 和 . 替换为 _"""
45
- return session_name.replace('/', '_').replace('.', '_')
50
+ """将会话名转为安全文件名(/ 和 . 替换为 _),超长时用完整 MD5"""
51
+ name = session_name.replace('/', '_').replace('.', '_')
52
+ if len(name) <= _MAX_FILENAME:
53
+ return name
54
+ # 超长:直接用完整 32 字符 MD5,彻底避免路径超限
55
+ return hashlib.md5(session_name.encode()).hexdigest()
46
56
 
47
57
 
48
58
  def get_socket_path(session_name: str) -> Path: