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
|
|
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
|
"""查看目录文件结构"""
|
package/lark_client/main.py
CHANGED
|
@@ -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.
|
|
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": [
|
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
|
-
|
|
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:
|