remote-claude 0.2.2 → 0.2.4
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.
|
@@ -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
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# 发布 remote-claude 到 npm
|
|
3
|
+
# 用法:bash scripts/publish.sh [patch|minor|major] [--token <npm-token>]
|
|
4
|
+
# 默认 patch(0.0.x)
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
10
|
+
cd "$ROOT_DIR"
|
|
11
|
+
|
|
12
|
+
# 解析参数
|
|
13
|
+
BUMP="patch"
|
|
14
|
+
TOKEN=""
|
|
15
|
+
while [[ $# -gt 0 ]]; do
|
|
16
|
+
case "$1" in
|
|
17
|
+
--token) TOKEN="$2"; shift 2 ;;
|
|
18
|
+
patch|minor|major) BUMP="$1"; shift ;;
|
|
19
|
+
*) echo "未知参数: $1"; exit 1 ;;
|
|
20
|
+
esac
|
|
21
|
+
done
|
|
22
|
+
|
|
23
|
+
# 写入 token(如果提供)
|
|
24
|
+
if [ -n "$TOKEN" ]; then
|
|
25
|
+
npm config set //registry.npmjs.org/:_authToken "$TOKEN"
|
|
26
|
+
echo "🔑 npm token 已写入 ~/.npmrc"
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# 检查 npm 登录状态
|
|
30
|
+
if ! npm whoami --registry=https://registry.npmjs.org/ &>/dev/null; then
|
|
31
|
+
echo "❌ 未登录 npm,请通过 --token 传入 token:"
|
|
32
|
+
echo " bash scripts/publish.sh --token <npm-token>"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# 检查 git 工作区干净(package.json 之外)
|
|
37
|
+
DIRTY=$(git status --porcelain | grep -v "^.M package.json" || true)
|
|
38
|
+
if [ -n "$DIRTY" ]; then
|
|
39
|
+
echo "❌ 工作区有未提交的改动,请先 commit:"
|
|
40
|
+
echo "$DIRTY"
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
# 获取旧版本
|
|
45
|
+
OLD_VERSION=$(node -e "console.log(require('./package.json').version)")
|
|
46
|
+
|
|
47
|
+
# bump 版本号(不创建 git tag)
|
|
48
|
+
npm version "$BUMP" --no-git-tag-version
|
|
49
|
+
|
|
50
|
+
NEW_VERSION=$(node -e "console.log(require('./package.json').version)")
|
|
51
|
+
|
|
52
|
+
echo "版本: $OLD_VERSION → $NEW_VERSION"
|
|
53
|
+
|
|
54
|
+
# 提交 package.json
|
|
55
|
+
git add package.json
|
|
56
|
+
git commit -m "chore: 发布 $NEW_VERSION"
|
|
57
|
+
|
|
58
|
+
# 发布到 npm
|
|
59
|
+
echo "📦 发布中..."
|
|
60
|
+
npm publish --registry=https://registry.npmjs.org/
|
|
61
|
+
|
|
62
|
+
echo "✅ remote-claude@$NEW_VERSION 发布成功"
|
|
@@ -654,13 +654,19 @@ class ScreenParser:
|
|
|
654
654
|
|
|
655
655
|
# UserInput:❯
|
|
656
656
|
if col0 == '❯':
|
|
657
|
-
|
|
657
|
+
first_text = lines[0][1:].strip()
|
|
658
658
|
# 内容全是分割线字符(如 ❯─────...─)→ 装饰性分隔符,忽略
|
|
659
|
-
if not
|
|
659
|
+
if not first_text or all(c in DIVIDER_CHARS for c in first_text):
|
|
660
660
|
return None
|
|
661
|
+
# 收集后续续行(多行输入 / 屏幕自动换行),过滤尾部空白行
|
|
662
|
+
body_lines = [l for l in lines[1:] if l.strip()]
|
|
663
|
+
text = '\n'.join([first_text] + body_lines)
|
|
661
664
|
ind = col0
|
|
662
665
|
ansi_ind = _get_col0_ansi(screen, first_row)
|
|
663
|
-
|
|
666
|
+
ansi_first = _get_row_ansi_text(screen, first_row, start_col=1).strip()
|
|
667
|
+
ansi_body = [_get_row_ansi_text(screen, r) for r in block_rows[1:]
|
|
668
|
+
if _get_row_text(screen, r).strip()]
|
|
669
|
+
ansi_text = '\n'.join([ansi_first] + ansi_body)
|
|
664
670
|
return UserInput(text=text, ansi_text=ansi_text, indicator=ind, ansi_indicator=ansi_ind)
|
|
665
671
|
|
|
666
672
|
# OutputBlock:圆点字符
|
package/server/shared_state.py
CHANGED
|
@@ -57,7 +57,8 @@ def _block_id_from_dict(d: dict) -> str:
|
|
|
57
57
|
"""从已序列化的 component dict 计算 block_id(与 server._block_id 逻辑一致)"""
|
|
58
58
|
t = d.get('_type', '')
|
|
59
59
|
if t == 'UserInput':
|
|
60
|
-
|
|
60
|
+
first_line = d.get('text', '').split('\n', 1)[0][:80]
|
|
61
|
+
return f"U:{first_line}"
|
|
61
62
|
elif t == 'OutputBlock':
|
|
62
63
|
content = d.get('content', '')
|
|
63
64
|
first_line = content.split('\n', 1)[0].strip()[:80]
|