remote-claude 0.1.0 → 0.1.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.
- package/.env.example +0 -3
- package/init.sh +534 -0
- package/lark_client/config.py +15 -3
- package/lark_client/lark_handler.py +12 -3
- package/lark_client/main.py +1 -1
- package/package.json +2 -1
- package/remote_claude.py +5 -3
- package/scripts/check-env.sh +2 -1
- package/scripts/postinstall.sh +2 -64
- package/stats/collector.py +12 -1
- package/stats/machine.py +15 -3
- package/stats/query.py +1 -1
- package/utils/session.py +21 -0
package/.env.example
CHANGED
package/init.sh
ADDED
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
# 颜色定义
|
|
6
|
+
RED=$'\033[0;31m'
|
|
7
|
+
GREEN=$'\033[0;32m'
|
|
8
|
+
YELLOW=$'\033[1;33m'
|
|
9
|
+
NC=$'\033[0m' # No Color
|
|
10
|
+
|
|
11
|
+
# 末尾汇总警告
|
|
12
|
+
WARNINGS=()
|
|
13
|
+
|
|
14
|
+
# 打印函数
|
|
15
|
+
print_info() {
|
|
16
|
+
echo -e "${GREEN}ℹ${NC} $1"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
print_success() {
|
|
20
|
+
echo -e "${GREEN}✓${NC} $1"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
print_warning() {
|
|
24
|
+
echo -e "${YELLOW}⚠${NC} $1"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
print_error() {
|
|
28
|
+
echo -e "${RED}✗${NC} $1"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
print_header() {
|
|
32
|
+
echo ""
|
|
33
|
+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
34
|
+
echo -e "${GREEN}$1${NC}"
|
|
35
|
+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
36
|
+
echo ""
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# 检查操作系统
|
|
40
|
+
check_os() {
|
|
41
|
+
print_header "检查系统环境"
|
|
42
|
+
|
|
43
|
+
OS=$(uname -s)
|
|
44
|
+
if [[ "$OS" != "Darwin" && "$OS" != "Linux" ]]; then
|
|
45
|
+
print_error "不支持的操作系统: $OS"
|
|
46
|
+
print_error "Remote Claude 仅支持 macOS 和 Linux"
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
print_success "操作系统: $OS"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# 检查 uv
|
|
54
|
+
check_uv() {
|
|
55
|
+
print_header "检查 uv"
|
|
56
|
+
|
|
57
|
+
if command -v uv &> /dev/null; then
|
|
58
|
+
UV_VERSION=$(uv --version)
|
|
59
|
+
print_success "$UV_VERSION 已安装"
|
|
60
|
+
return
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
print_warning "未找到 uv,正在安装..."
|
|
64
|
+
|
|
65
|
+
# 方式一:官方安装脚本
|
|
66
|
+
if curl -LsSf https://astral.sh/uv/install.sh | sh 2>/dev/null; then
|
|
67
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# 方式二:brew(macOS 备用)
|
|
71
|
+
if ! command -v uv &> /dev/null && [[ "$OS" == "Darwin" ]] && command -v brew &> /dev/null; then
|
|
72
|
+
print_warning "官方脚本安装失败,尝试 brew install uv..."
|
|
73
|
+
brew install uv
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# 方式三:pip/pip3 备用
|
|
77
|
+
if ! command -v uv &> /dev/null; then
|
|
78
|
+
print_warning "尝试 pip 安装 uv..."
|
|
79
|
+
if command -v pip3 &> /dev/null; then
|
|
80
|
+
pip3 install uv --quiet
|
|
81
|
+
elif command -v pip &> /dev/null; then
|
|
82
|
+
pip install uv --quiet
|
|
83
|
+
fi
|
|
84
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
if command -v uv &> /dev/null; then
|
|
88
|
+
print_success "uv 安装成功"
|
|
89
|
+
|
|
90
|
+
# 确保 ~/.local/bin 写入 shell rc(uv 官方脚本可能写 .zprofile 而非 .zshrc)
|
|
91
|
+
local _RC
|
|
92
|
+
if [[ -n "$ZSH_VERSION" ]] || [[ "$(basename "$SHELL")" == "zsh" ]]; then
|
|
93
|
+
_RC="$HOME/.zshrc"
|
|
94
|
+
else
|
|
95
|
+
_RC="$HOME/.bashrc"
|
|
96
|
+
fi
|
|
97
|
+
if ! grep -qF "$HOME/.local/bin" "$_RC" 2>/dev/null; then
|
|
98
|
+
echo "" >> "$_RC"
|
|
99
|
+
echo "# uv" >> "$_RC"
|
|
100
|
+
echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$_RC"
|
|
101
|
+
print_success "已将 \$HOME/.local/bin 写入 $_RC"
|
|
102
|
+
fi
|
|
103
|
+
else
|
|
104
|
+
print_error "uv 安装失败,请手动安装: https://docs.astral.sh/uv/"
|
|
105
|
+
exit 1
|
|
106
|
+
fi
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
# 检查并安装 tmux(要求 3.6+)
|
|
110
|
+
check_tmux() {
|
|
111
|
+
print_header "检查 tmux"
|
|
112
|
+
|
|
113
|
+
REQUIRED_MAJOR=3
|
|
114
|
+
REQUIRED_MINOR=6
|
|
115
|
+
|
|
116
|
+
install_tmux() {
|
|
117
|
+
if [[ "$OS" == "Darwin" ]]; then
|
|
118
|
+
if ! command -v brew &> /dev/null; then
|
|
119
|
+
print_warning "未找到 Homebrew,正在自动安装..."
|
|
120
|
+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
|
121
|
+
# 将 Homebrew 加入 PATH(Apple Silicon / Intel 路径不同)
|
|
122
|
+
if [[ -x "/opt/homebrew/bin/brew" ]]; then
|
|
123
|
+
eval "$(/opt/homebrew/bin/brew shellenv)"
|
|
124
|
+
elif [[ -x "/usr/local/bin/brew" ]]; then
|
|
125
|
+
eval "$(/usr/local/bin/brew shellenv)"
|
|
126
|
+
fi
|
|
127
|
+
if ! command -v brew &> /dev/null; then
|
|
128
|
+
print_error "Homebrew 安装失败,请手动安装后重试: https://brew.sh"
|
|
129
|
+
exit 1
|
|
130
|
+
fi
|
|
131
|
+
print_success "Homebrew 安装成功"
|
|
132
|
+
fi
|
|
133
|
+
brew install tmux
|
|
134
|
+
elif [[ "$OS" == "Linux" ]]; then
|
|
135
|
+
if command -v apt-get &> /dev/null; then
|
|
136
|
+
sudo apt-get update && sudo apt-get install -y tmux
|
|
137
|
+
elif command -v yum &> /dev/null; then
|
|
138
|
+
sudo yum install -y tmux
|
|
139
|
+
elif command -v pacman &> /dev/null; then
|
|
140
|
+
sudo pacman -Sy --noconfirm tmux
|
|
141
|
+
elif command -v apk &> /dev/null; then
|
|
142
|
+
sudo apk add --no-cache tmux
|
|
143
|
+
elif command -v zypper &> /dev/null; then
|
|
144
|
+
sudo zypper install -y tmux
|
|
145
|
+
else
|
|
146
|
+
print_warning "无法识别包管理器,尝试从源码编译 tmux..."
|
|
147
|
+
install_tmux_from_source
|
|
148
|
+
return
|
|
149
|
+
fi
|
|
150
|
+
fi
|
|
151
|
+
print_success "tmux 安装成功"
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
install_tmux_from_source() {
|
|
155
|
+
local TMUX_VERSION_TAG="3.6a"
|
|
156
|
+
local TMUX_URL="https://github.com/tmux/tmux/releases/download/${TMUX_VERSION_TAG}/tmux-${TMUX_VERSION_TAG}.tar.gz"
|
|
157
|
+
|
|
158
|
+
print_warning "包管理器版本不满足要求,尝试从源码编译 tmux ${TMUX_VERSION_TAG}..."
|
|
159
|
+
|
|
160
|
+
# 安装编译依赖
|
|
161
|
+
if [[ "$OS" == "Darwin" ]]; then
|
|
162
|
+
brew install libevent ncurses pkg-config bison 2>/dev/null || true
|
|
163
|
+
elif command -v apt-get &> /dev/null; then
|
|
164
|
+
sudo apt-get install -y build-essential libevent-dev libncurses5-dev libncursesw5-dev bison pkg-config
|
|
165
|
+
elif command -v yum &> /dev/null; then
|
|
166
|
+
sudo yum groupinstall -y "Development Tools"
|
|
167
|
+
sudo yum install -y libevent-devel ncurses-devel bison
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
# 确定安装前缀
|
|
171
|
+
local PREFIX="/usr/local"
|
|
172
|
+
if ! sudo -n true 2>/dev/null; then
|
|
173
|
+
print_warning "无 sudo 权限,将安装到 \$HOME/.local"
|
|
174
|
+
PREFIX="$HOME/.local"
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
# 创建临时目录,编译完成后清理
|
|
178
|
+
local TMPDIR
|
|
179
|
+
TMPDIR=$(mktemp -d)
|
|
180
|
+
trap "rm -rf '$TMPDIR'" RETURN
|
|
181
|
+
|
|
182
|
+
print_warning "下载 tmux-${TMUX_VERSION_TAG}.tar.gz..."
|
|
183
|
+
if ! curl -fsSL "$TMUX_URL" -o "$TMPDIR/tmux.tar.gz"; then
|
|
184
|
+
print_warning "下载失败,请检查网络或手动安装 tmux ${REQUIRED_MAJOR}.${REQUIRED_MINOR}+"
|
|
185
|
+
WARNINGS+=("tmux 源码下载失败,请手动安装 tmux ${REQUIRED_MAJOR}.${REQUIRED_MINOR}+")
|
|
186
|
+
return
|
|
187
|
+
fi
|
|
188
|
+
|
|
189
|
+
tar -xzf "$TMPDIR/tmux.tar.gz" -C "$TMPDIR"
|
|
190
|
+
local SRC_DIR
|
|
191
|
+
SRC_DIR=$(find "$TMPDIR" -maxdepth 1 -type d -name "tmux-*" | head -1)
|
|
192
|
+
|
|
193
|
+
print_warning "编译 tmux(可能需要几分钟)..."
|
|
194
|
+
if ! (cd "$SRC_DIR" && ./configure --prefix="$PREFIX" && make -j"$(nproc 2>/dev/null || echo 2)"); then
|
|
195
|
+
print_warning "编译失败,请手动安装 tmux ${REQUIRED_MAJOR}.${REQUIRED_MINOR}+"
|
|
196
|
+
WARNINGS+=("tmux 源码编译失败,请手动安装 tmux ${REQUIRED_MAJOR}.${REQUIRED_MINOR}+")
|
|
197
|
+
return
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
if [[ "$PREFIX" == "/usr/local" ]]; then
|
|
201
|
+
sudo make -C "$SRC_DIR" install
|
|
202
|
+
else
|
|
203
|
+
make -C "$SRC_DIR" install
|
|
204
|
+
# 若 $HOME/.local/bin 不在 PATH 中,自动写入 shell 配置
|
|
205
|
+
if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then
|
|
206
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
207
|
+
local _RC
|
|
208
|
+
if [[ "$(basename "$SHELL")" == "zsh" ]]; then
|
|
209
|
+
_RC="$HOME/.zshrc"
|
|
210
|
+
else
|
|
211
|
+
_RC="$HOME/.bashrc"
|
|
212
|
+
fi
|
|
213
|
+
local _PATH_LINE='export PATH="$HOME/.local/bin:$PATH"'
|
|
214
|
+
if ! grep -qF "$HOME/.local/bin" "$_RC" 2>/dev/null; then
|
|
215
|
+
echo "" >> "$_RC"
|
|
216
|
+
echo "# remote-claude: tmux 路径" >> "$_RC"
|
|
217
|
+
echo "$_PATH_LINE" >> "$_RC"
|
|
218
|
+
print_success "已自动将 \$HOME/.local/bin 加入 PATH(写入 $_RC)"
|
|
219
|
+
fi
|
|
220
|
+
fi
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
print_success "tmux ${TMUX_VERSION_TAG} 源码编译安装完成(前缀:${PREFIX})"
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
check_version() {
|
|
227
|
+
# tmux -V 输出格式:tmux 3.6 或 tmux 3.4a
|
|
228
|
+
local ver_str
|
|
229
|
+
ver_str=$(tmux -V | awk '{print $2}')
|
|
230
|
+
local major minor
|
|
231
|
+
major=$(echo "$ver_str" | cut -d. -f1)
|
|
232
|
+
minor=$(echo "$ver_str" | cut -d. -f2 | tr -dc '0-9')
|
|
233
|
+
if [[ "$major" -gt "$REQUIRED_MAJOR" ]] || \
|
|
234
|
+
[[ "$major" -eq "$REQUIRED_MAJOR" && "${minor:-0}" -ge "$REQUIRED_MINOR" ]]; then
|
|
235
|
+
return 0
|
|
236
|
+
fi
|
|
237
|
+
return 1
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if command -v tmux &> /dev/null; then
|
|
241
|
+
TMUX_VERSION=$(tmux -V)
|
|
242
|
+
if check_version; then
|
|
243
|
+
print_success "$TMUX_VERSION 已安装(满足 >= ${REQUIRED_MAJOR}.${REQUIRED_MINOR})"
|
|
244
|
+
return
|
|
245
|
+
else
|
|
246
|
+
print_warning "$TMUX_VERSION 版本过低,需要 ${REQUIRED_MAJOR}.${REQUIRED_MINOR} 或更高,正在升级..."
|
|
247
|
+
install_tmux
|
|
248
|
+
# 升级后再次验证,版本仍不满足则走源码编译(跨平台)
|
|
249
|
+
if ! check_version; then
|
|
250
|
+
install_tmux_from_source
|
|
251
|
+
if check_version; then
|
|
252
|
+
print_success "tmux 已升级至 $(tmux -V)"
|
|
253
|
+
else
|
|
254
|
+
print_warning "源码编译后版本仍不满足要求($(tmux -V)),需要 ${REQUIRED_MAJOR}.${REQUIRED_MINOR}+"
|
|
255
|
+
WARNINGS+=("tmux 版本不满足要求($(tmux -V)),需要 ${REQUIRED_MAJOR}.${REQUIRED_MINOR}+,请手动升级")
|
|
256
|
+
fi
|
|
257
|
+
else
|
|
258
|
+
print_success "tmux 已升级至 $(tmux -V)"
|
|
259
|
+
fi
|
|
260
|
+
fi
|
|
261
|
+
else
|
|
262
|
+
print_warning "未找到 tmux,正在安装..."
|
|
263
|
+
install_tmux
|
|
264
|
+
if ! check_version; then
|
|
265
|
+
install_tmux_from_source
|
|
266
|
+
if ! check_version; then
|
|
267
|
+
print_warning "源码编译后版本仍不满足要求($(tmux -V)),需要 ${REQUIRED_MAJOR}.${REQUIRED_MINOR}+"
|
|
268
|
+
WARNINGS+=("tmux 版本不满足要求($(tmux -V)),需要 ${REQUIRED_MAJOR}.${REQUIRED_MINOR}+,请手动升级")
|
|
269
|
+
fi
|
|
270
|
+
fi
|
|
271
|
+
fi
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
# 检查 Claude CLI
|
|
275
|
+
check_claude() {
|
|
276
|
+
print_header "检查 Claude CLI"
|
|
277
|
+
|
|
278
|
+
if command -v claude &> /dev/null; then
|
|
279
|
+
print_success "Claude CLI 已安装"
|
|
280
|
+
return
|
|
281
|
+
fi
|
|
282
|
+
|
|
283
|
+
print_warning "未找到 Claude CLI"
|
|
284
|
+
print_info "请访问 https://claude.ai/code 安装 Claude CLI"
|
|
285
|
+
|
|
286
|
+
if $NPM_MODE; then
|
|
287
|
+
print_info "(npm 模式:跳过交互,请安装后重新运行)"
|
|
288
|
+
return
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
read -p "$(echo -e ${YELLOW}是否已安装 Claude CLI?${NC} [y/N]: )" -n 1 -r
|
|
292
|
+
echo
|
|
293
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
294
|
+
print_error "请先安装 Claude CLI 后再运行此脚本"
|
|
295
|
+
exit 1
|
|
296
|
+
fi
|
|
297
|
+
|
|
298
|
+
if ! command -v claude &> /dev/null; then
|
|
299
|
+
print_error "仍未找到 claude 命令,请检查安装或 PATH 配置"
|
|
300
|
+
exit 1
|
|
301
|
+
fi
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
# 安装 Python 依赖
|
|
305
|
+
install_dependencies() {
|
|
306
|
+
print_header "安装 Python 依赖"
|
|
307
|
+
|
|
308
|
+
if [ ! -f "pyproject.toml" ]; then
|
|
309
|
+
print_error "未找到 pyproject.toml 文件"
|
|
310
|
+
exit 1
|
|
311
|
+
fi
|
|
312
|
+
|
|
313
|
+
print_info "正在通过 uv 同步依赖..."
|
|
314
|
+
if $NPM_MODE; then
|
|
315
|
+
uv sync --frozen
|
|
316
|
+
else
|
|
317
|
+
uv sync
|
|
318
|
+
fi
|
|
319
|
+
|
|
320
|
+
print_success "依赖安装完成"
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
# 配置飞书环境
|
|
324
|
+
configure_lark() {
|
|
325
|
+
print_header "配置飞书客户端"
|
|
326
|
+
|
|
327
|
+
ENV_FILE="$HOME/.remote-claude/.env"
|
|
328
|
+
mkdir -p "$HOME/.remote-claude"
|
|
329
|
+
|
|
330
|
+
# 迁移旧 .env(项目根目录)到新位置
|
|
331
|
+
if [ -f ".env" ] && [ ! -f "$ENV_FILE" ]; then
|
|
332
|
+
mv ".env" "$ENV_FILE"
|
|
333
|
+
print_success "已将 .env 迁移到 $ENV_FILE"
|
|
334
|
+
fi
|
|
335
|
+
|
|
336
|
+
if [ -f "$ENV_FILE" ]; then
|
|
337
|
+
print_warning ".env 文件已存在($ENV_FILE),跳过配置"
|
|
338
|
+
return
|
|
339
|
+
fi
|
|
340
|
+
|
|
341
|
+
if [ ! -f ".env.example" ]; then
|
|
342
|
+
print_error "未找到 .env.example 文件"
|
|
343
|
+
exit 1
|
|
344
|
+
fi
|
|
345
|
+
|
|
346
|
+
read -p "$(echo -e ${YELLOW}是否需要配置飞书客户端?${NC} [y/N]: )" -n 1 -r
|
|
347
|
+
echo
|
|
348
|
+
|
|
349
|
+
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
350
|
+
cp .env.example "$ENV_FILE"
|
|
351
|
+
print_success ".env 文件已创建于 $ENV_FILE"
|
|
352
|
+
print_warning "请编辑 $ENV_FILE,填写以下信息:"
|
|
353
|
+
print_info " - FEISHU_APP_ID: 飞书应用的 App ID"
|
|
354
|
+
print_info " - FEISHU_APP_SECRET: 飞书应用的 App Secret"
|
|
355
|
+
print_info ""
|
|
356
|
+
print_info "获取方式: 登录飞书开放平台 -> 创建应用 -> 凭证与基础信息"
|
|
357
|
+
else
|
|
358
|
+
print_info "跳过飞书配置(可稍后手动配置)"
|
|
359
|
+
fi
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
# 创建必要目录
|
|
363
|
+
create_directories() {
|
|
364
|
+
print_header "创建运行目录"
|
|
365
|
+
|
|
366
|
+
SOCKET_DIR="/tmp/remote-claude"
|
|
367
|
+
USER_DATA_DIR="$HOME/.remote-claude"
|
|
368
|
+
|
|
369
|
+
if [ ! -d "$SOCKET_DIR" ]; then
|
|
370
|
+
mkdir -p "$SOCKET_DIR"
|
|
371
|
+
print_success "创建目录: $SOCKET_DIR"
|
|
372
|
+
else
|
|
373
|
+
print_info "目录已存在: $SOCKET_DIR"
|
|
374
|
+
fi
|
|
375
|
+
|
|
376
|
+
if [ ! -d "$USER_DATA_DIR" ]; then
|
|
377
|
+
mkdir -p "$USER_DATA_DIR"
|
|
378
|
+
print_success "创建目录: $USER_DATA_DIR"
|
|
379
|
+
else
|
|
380
|
+
print_info "目录已存在: $USER_DATA_DIR"
|
|
381
|
+
fi
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
# 设置可执行权限
|
|
385
|
+
set_permissions() {
|
|
386
|
+
print_header "设置执行权限"
|
|
387
|
+
|
|
388
|
+
chmod +x remote_claude.py
|
|
389
|
+
chmod +x server/server.py
|
|
390
|
+
chmod +x client/client.py
|
|
391
|
+
|
|
392
|
+
print_success "已设置执行权限"
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
# 安装快捷命令(符号链接到 bin 目录)
|
|
396
|
+
configure_shell() {
|
|
397
|
+
print_header "安装快捷命令"
|
|
398
|
+
|
|
399
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
400
|
+
chmod +x "$SCRIPT_DIR/bin/cla" "$SCRIPT_DIR/bin/cl" "$SCRIPT_DIR/bin/remote-claude"
|
|
401
|
+
|
|
402
|
+
# 优先 /usr/local/bin,权限不够则选 ~/bin 或 ~/.local/bin 中已在 PATH 里的
|
|
403
|
+
BIN_DIR="/usr/local/bin"
|
|
404
|
+
if ! ln -sf "$SCRIPT_DIR/bin/cla" "$BIN_DIR/cla" 2>/dev/null; then
|
|
405
|
+
if [[ ":$PATH:" == *":$HOME/bin:"* ]]; then
|
|
406
|
+
BIN_DIR="$HOME/bin"
|
|
407
|
+
elif [[ ":$PATH:" == *":$HOME/.local/bin:"* ]]; then
|
|
408
|
+
BIN_DIR="$HOME/.local/bin"
|
|
409
|
+
else
|
|
410
|
+
BIN_DIR="$HOME/.local/bin"
|
|
411
|
+
# 自动写入 PATH 到 shell 配置文件
|
|
412
|
+
export PATH="$BIN_DIR:$PATH"
|
|
413
|
+
local _RC
|
|
414
|
+
if [[ -n "$ZSH_VERSION" ]] || [[ "$(basename "$SHELL")" == "zsh" ]]; then
|
|
415
|
+
_RC="$HOME/.zshrc"
|
|
416
|
+
else
|
|
417
|
+
_RC="$HOME/.bashrc"
|
|
418
|
+
fi
|
|
419
|
+
local _PATH_LINE='export PATH="$HOME/.local/bin:$PATH"'
|
|
420
|
+
if ! grep -qF "$HOME/.local/bin" "$_RC" 2>/dev/null; then
|
|
421
|
+
echo "" >> "$_RC"
|
|
422
|
+
echo "# remote-claude: 快捷命令路径" >> "$_RC"
|
|
423
|
+
echo "$_PATH_LINE" >> "$_RC"
|
|
424
|
+
print_success "已自动将 \$HOME/.local/bin 加入 PATH(写入 $_RC)"
|
|
425
|
+
fi
|
|
426
|
+
fi
|
|
427
|
+
mkdir -p "$BIN_DIR"
|
|
428
|
+
ln -sf "$SCRIPT_DIR/bin/cla" "$BIN_DIR/cla"
|
|
429
|
+
ln -sf "$SCRIPT_DIR/bin/cl" "$BIN_DIR/cl"
|
|
430
|
+
ln -sf "$SCRIPT_DIR/bin/remote-claude" "$BIN_DIR/remote-claude"
|
|
431
|
+
else
|
|
432
|
+
ln -sf "$SCRIPT_DIR/bin/cl" "$BIN_DIR/cl"
|
|
433
|
+
ln -sf "$SCRIPT_DIR/bin/remote-claude" "$BIN_DIR/remote-claude"
|
|
434
|
+
fi
|
|
435
|
+
|
|
436
|
+
print_success "已安装 cla、cl 和 remote-claude 到 $BIN_DIR"
|
|
437
|
+
print_info " cla - 启动飞书客户端 + 以当前目录路径+时间戳为会话名启动 Claude"
|
|
438
|
+
print_info " cl - 同 cla,但跳过权限确认"
|
|
439
|
+
print_info " remote-claude - Remote Claude 主命令(start/attach/list/kill/lark)"
|
|
440
|
+
|
|
441
|
+
# 安装 shell 自动补全
|
|
442
|
+
local COMPLETION_LINE="source \"$SCRIPT_DIR/scripts/completion.sh\""
|
|
443
|
+
local SHELL_RC=""
|
|
444
|
+
if [[ -n "$ZSH_VERSION" ]] || [[ "$(basename "$SHELL")" == "zsh" ]]; then
|
|
445
|
+
SHELL_RC="$HOME/.zshrc"
|
|
446
|
+
else
|
|
447
|
+
SHELL_RC="$HOME/.bashrc"
|
|
448
|
+
fi
|
|
449
|
+
|
|
450
|
+
if [[ -f "$SHELL_RC" ]] && grep -qF "$SCRIPT_DIR/scripts/completion.sh" "$SHELL_RC" 2>/dev/null; then
|
|
451
|
+
print_info "自动补全已配置($SHELL_RC)"
|
|
452
|
+
else
|
|
453
|
+
echo "" >> "$SHELL_RC"
|
|
454
|
+
echo "# remote-claude 自动补全" >> "$SHELL_RC"
|
|
455
|
+
echo "$COMPLETION_LINE" >> "$SHELL_RC"
|
|
456
|
+
print_success "已添加自动补全到 $SHELL_RC(重新打开终端后生效)"
|
|
457
|
+
fi
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
# 重启飞书客户端
|
|
461
|
+
restart_lark_client() {
|
|
462
|
+
print_header "重启飞书客户端"
|
|
463
|
+
|
|
464
|
+
LARK_PID_FILE="/tmp/remote-claude/lark.pid"
|
|
465
|
+
|
|
466
|
+
if [ ! -f "$LARK_PID_FILE" ] && ! pgrep -f "lark_client/main.py" &>/dev/null; then
|
|
467
|
+
print_info "飞书客户端未运行,跳过重启"
|
|
468
|
+
return
|
|
469
|
+
fi
|
|
470
|
+
|
|
471
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
472
|
+
print_info "正在重启飞书客户端..."
|
|
473
|
+
cd "$SCRIPT_DIR"
|
|
474
|
+
uv run python3 remote_claude.py lark restart
|
|
475
|
+
print_success "飞书客户端已重启"
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
# 显示使用说明
|
|
479
|
+
show_usage() {
|
|
480
|
+
print_header "安装完成!"
|
|
481
|
+
|
|
482
|
+
cat << EOF
|
|
483
|
+
${YELLOW}快捷命令:${NC}
|
|
484
|
+
|
|
485
|
+
${GREEN}cla${NC} - 启动飞书客户端 + 以当前目录+时间戳为会话名启动 Claude
|
|
486
|
+
${GREEN}cl${NC} - 同 cla,但跳过权限确认
|
|
487
|
+
|
|
488
|
+
详细使用说明请阅读 README.md
|
|
489
|
+
|
|
490
|
+
EOF
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
# 主流程
|
|
494
|
+
main() {
|
|
495
|
+
# 解析参数
|
|
496
|
+
NPM_MODE=false
|
|
497
|
+
for arg in "$@"; do
|
|
498
|
+
[[ "$arg" == "--npm" ]] && NPM_MODE=true
|
|
499
|
+
done
|
|
500
|
+
|
|
501
|
+
echo ""
|
|
502
|
+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
503
|
+
echo -e "${GREEN} Remote Claude 初始化脚本${NC}"
|
|
504
|
+
echo -e "${GREEN} 双端共享 Claude CLI 工具${NC}"
|
|
505
|
+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
506
|
+
echo ""
|
|
507
|
+
|
|
508
|
+
check_os
|
|
509
|
+
check_uv
|
|
510
|
+
check_tmux
|
|
511
|
+
check_claude
|
|
512
|
+
install_dependencies
|
|
513
|
+
if ! $NPM_MODE; then
|
|
514
|
+
configure_lark
|
|
515
|
+
fi
|
|
516
|
+
create_directories
|
|
517
|
+
set_permissions
|
|
518
|
+
configure_shell
|
|
519
|
+
restart_lark_client
|
|
520
|
+
show_usage
|
|
521
|
+
|
|
522
|
+
if [ ${#WARNINGS[@]} -gt 0 ]; then
|
|
523
|
+
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
524
|
+
echo -e "${YELLOW}⚠ 注意事项${NC}"
|
|
525
|
+
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
526
|
+
for w in "${WARNINGS[@]}"; do
|
|
527
|
+
echo -e "${YELLOW}⚠${NC} $w"
|
|
528
|
+
done
|
|
529
|
+
echo ""
|
|
530
|
+
fi
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
# 运行主流程
|
|
534
|
+
main "$@"
|
package/lark_client/config.py
CHANGED
|
@@ -6,9 +6,21 @@ import os
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from dotenv import load_dotenv
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
import sys
|
|
10
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
11
|
+
from utils.session import USER_DATA_DIR, get_env_file
|
|
12
|
+
|
|
13
|
+
# 加载 .env 文件,优先从 ~/.remote-claude/.env 读取
|
|
14
|
+
_env_file = get_env_file()
|
|
15
|
+
_old_env_file = Path(__file__).resolve().parent.parent / ".env"
|
|
16
|
+
|
|
17
|
+
if not _env_file.exists() and _old_env_file.exists():
|
|
18
|
+
import shutil
|
|
19
|
+
USER_DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
20
|
+
shutil.move(str(_old_env_file), str(_env_file))
|
|
21
|
+
print(f"[config] 已将 .env 迁移到 {_env_file}")
|
|
22
|
+
|
|
23
|
+
load_dotenv(_env_file)
|
|
12
24
|
|
|
13
25
|
# 飞书应用配置
|
|
14
26
|
FEISHU_APP_ID = os.getenv("FEISHU_APP_ID", "")
|
|
@@ -31,7 +31,7 @@ from .card_builder import (
|
|
|
31
31
|
from .shared_memory_poller import SharedMemoryPoller, CardSlice
|
|
32
32
|
|
|
33
33
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
34
|
-
from utils.session import list_active_sessions, get_socket_path
|
|
34
|
+
from utils.session import list_active_sessions, get_socket_path, get_chat_bindings_file, ensure_user_data_dir
|
|
35
35
|
|
|
36
36
|
try:
|
|
37
37
|
from stats import track as _track_stats
|
|
@@ -42,9 +42,18 @@ except Exception:
|
|
|
42
42
|
class LarkHandler:
|
|
43
43
|
"""飞书消息处理器(群聊/私聊统一逻辑)"""
|
|
44
44
|
|
|
45
|
-
_CHAT_BINDINGS_FILE =
|
|
45
|
+
_CHAT_BINDINGS_FILE = get_chat_bindings_file()
|
|
46
|
+
_OLD_CHAT_BINDINGS_FILE = Path("/tmp/remote-claude/lark_chat_bindings.json")
|
|
46
47
|
|
|
47
48
|
def __init__(self):
|
|
49
|
+
# 兼容迁移:旧绑定文件存在而新路径不存在时,自动迁移
|
|
50
|
+
if not self._CHAT_BINDINGS_FILE.exists() and self._OLD_CHAT_BINDINGS_FILE.exists():
|
|
51
|
+
try:
|
|
52
|
+
import shutil
|
|
53
|
+
ensure_user_data_dir()
|
|
54
|
+
shutil.move(str(self._OLD_CHAT_BINDINGS_FILE), str(self._CHAT_BINDINGS_FILE))
|
|
55
|
+
except Exception as e:
|
|
56
|
+
logger.warning(f"迁移旧绑定文件失败: {e}")
|
|
48
57
|
# chat_id → SessionBridge(活跃连接)
|
|
49
58
|
self._bridges: Dict[str, SessionBridge] = {}
|
|
50
59
|
# chat_id → session_name(当前连接状态)
|
|
@@ -68,7 +77,7 @@ class LarkHandler:
|
|
|
68
77
|
|
|
69
78
|
def _save_chat_bindings(self):
|
|
70
79
|
try:
|
|
71
|
-
|
|
80
|
+
ensure_user_data_dir()
|
|
72
81
|
self._CHAT_BINDINGS_FILE.write_text(
|
|
73
82
|
json.dumps(self._chat_bindings, ensure_ascii=False)
|
|
74
83
|
)
|
package/lark_client/main.py
CHANGED
|
@@ -250,7 +250,7 @@ class LarkBot:
|
|
|
250
250
|
# 检查配置
|
|
251
251
|
if not config.FEISHU_APP_ID or not config.FEISHU_APP_SECRET:
|
|
252
252
|
print("错误: 请配置 FEISHU_APP_ID 和 FEISHU_APP_SECRET")
|
|
253
|
-
print("在
|
|
253
|
+
print("在 ~/.remote-claude/.env 文件中添加:")
|
|
254
254
|
print(" FEISHU_APP_ID=your_app_id")
|
|
255
255
|
print(" FEISHU_APP_SECRET=your_app_secret")
|
|
256
256
|
return
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "remote-claude",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "双端共享 Claude CLI 工具",
|
|
5
5
|
"bin": {
|
|
6
6
|
"remote-claude": "bin/remote-claude",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"files": [
|
|
14
14
|
"bin/",
|
|
15
15
|
"scripts/",
|
|
16
|
+
"init.sh",
|
|
16
17
|
"remote_claude.py",
|
|
17
18
|
"server/*.py",
|
|
18
19
|
"client/*.py",
|
package/remote_claude.py
CHANGED
|
@@ -29,7 +29,8 @@ from utils.session import (
|
|
|
29
29
|
tmux_kill_session,
|
|
30
30
|
list_active_sessions, is_session_active, cleanup_session,
|
|
31
31
|
is_lark_running, get_lark_pid, get_lark_status, get_lark_pid_file,
|
|
32
|
-
save_lark_status, cleanup_lark
|
|
32
|
+
save_lark_status, cleanup_lark,
|
|
33
|
+
USER_DATA_DIR, ensure_user_data_dir, get_lark_log_file
|
|
33
34
|
)
|
|
34
35
|
|
|
35
36
|
|
|
@@ -196,9 +197,10 @@ def cmd_lark_start(args):
|
|
|
196
197
|
print("正在启动飞书客户端...")
|
|
197
198
|
|
|
198
199
|
ensure_socket_dir()
|
|
200
|
+
ensure_user_data_dir()
|
|
199
201
|
|
|
200
202
|
# 启动守护进程(使用 -m 模块方式运行,确保相对导入正常工作)
|
|
201
|
-
log_file =
|
|
203
|
+
log_file = get_lark_log_file()
|
|
202
204
|
|
|
203
205
|
try:
|
|
204
206
|
# 启动进程
|
|
@@ -319,7 +321,7 @@ def cmd_lark_status(args):
|
|
|
319
321
|
print(f"运行时长: {status['uptime']}")
|
|
320
322
|
|
|
321
323
|
# 检查日志文件
|
|
322
|
-
log_file =
|
|
324
|
+
log_file = get_lark_log_file()
|
|
323
325
|
if log_file.exists():
|
|
324
326
|
print(f"日志文件: {log_file}")
|
|
325
327
|
print(f"日志大小: {log_file.stat().st_size / 1024:.1f} KB")
|
package/scripts/check-env.sh
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
# 用法: source scripts/check-env.sh "$INSTALL_DIR"
|
|
4
4
|
|
|
5
5
|
INSTALL_DIR="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
|
6
|
-
ENV_FILE="$
|
|
6
|
+
ENV_FILE="$HOME/.remote-claude/.env"
|
|
7
|
+
mkdir -p "$HOME/.remote-claude"
|
|
7
8
|
ENV_OK=false
|
|
8
9
|
|
|
9
10
|
if [ -f "$ENV_FILE" ]; then
|
package/scripts/postinstall.sh
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
set -e
|
|
3
3
|
|
|
4
|
-
#
|
|
4
|
+
# 解析安装目录(兼容符号链接)
|
|
5
5
|
SOURCE="${BASH_SOURCE[0]}"
|
|
6
6
|
while [ -L "$SOURCE" ]; do
|
|
7
7
|
DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
|
|
@@ -10,67 +10,5 @@ while [ -L "$SOURCE" ]; do
|
|
|
10
10
|
done
|
|
11
11
|
INSTALL_DIR="$(cd -P "$(dirname "$SOURCE")" && cd .. && pwd)"
|
|
12
12
|
|
|
13
|
-
echo ""
|
|
14
|
-
echo "=== Remote Claude 安装后初始化 ==="
|
|
15
|
-
echo ""
|
|
16
|
-
|
|
17
|
-
# 1. 检查/安装 uv
|
|
18
|
-
if ! command -v uv &>/dev/null; then
|
|
19
|
-
if [ -f "$HOME/.local/bin/uv" ]; then
|
|
20
|
-
export PATH="$HOME/.local/bin:$PATH"
|
|
21
|
-
else
|
|
22
|
-
echo "正在安装 uv..."
|
|
23
|
-
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
24
|
-
export PATH="$HOME/.local/bin:$PATH"
|
|
25
|
-
fi
|
|
26
|
-
fi
|
|
27
|
-
|
|
28
|
-
if ! command -v uv &>/dev/null; then
|
|
29
|
-
echo "错误: uv 安装失败,请手动安装: https://docs.astral.sh/uv/getting-started/installation/"
|
|
30
|
-
exit 1
|
|
31
|
-
fi
|
|
32
|
-
|
|
33
|
-
echo "✓ uv $(uv --version)"
|
|
34
|
-
|
|
35
|
-
# 2. 安装 Python 依赖
|
|
36
|
-
echo "正在安装 Python 依赖..."
|
|
37
13
|
cd "$INSTALL_DIR"
|
|
38
|
-
|
|
39
|
-
echo "✓ Python 依赖安装完成"
|
|
40
|
-
|
|
41
|
-
# 3. 设置文件可执行权限
|
|
42
|
-
chmod +x "$INSTALL_DIR/bin/remote-claude"
|
|
43
|
-
chmod +x "$INSTALL_DIR/bin/cla"
|
|
44
|
-
chmod +x "$INSTALL_DIR/bin/cl"
|
|
45
|
-
chmod +x "$INSTALL_DIR/scripts/check-env.sh"
|
|
46
|
-
echo "✓ 文件权限设置完成"
|
|
47
|
-
|
|
48
|
-
# 4. 检查可选依赖
|
|
49
|
-
echo ""
|
|
50
|
-
echo "=== 依赖检查 ==="
|
|
51
|
-
|
|
52
|
-
if ! command -v tmux &>/dev/null; then
|
|
53
|
-
echo "⚠ tmux 未安装(需要 >= 3.6)"
|
|
54
|
-
echo " macOS: brew install tmux"
|
|
55
|
-
echo " Linux: sudo apt install tmux 或 sudo yum install tmux"
|
|
56
|
-
else
|
|
57
|
-
TMUX_VER=$(tmux -V | grep -oE '[0-9]+\.[0-9]+' | head -1)
|
|
58
|
-
echo "✓ tmux $TMUX_VER"
|
|
59
|
-
fi
|
|
60
|
-
|
|
61
|
-
if ! command -v claude &>/dev/null; then
|
|
62
|
-
echo "⚠ claude CLI 未安装"
|
|
63
|
-
echo " 安装方法: npm install -g @anthropic-ai/claude-code"
|
|
64
|
-
else
|
|
65
|
-
echo "✓ claude CLI 已安装"
|
|
66
|
-
fi
|
|
67
|
-
|
|
68
|
-
echo ""
|
|
69
|
-
echo "=== 安装完成 ==="
|
|
70
|
-
echo ""
|
|
71
|
-
echo "使用方法:"
|
|
72
|
-
echo " remote-claude start <会话名> 启动 Claude 会话"
|
|
73
|
-
echo " remote-claude list 列出所有会话"
|
|
74
|
-
echo " remote-claude attach <会话名> 连接到会话"
|
|
75
|
-
echo " cla 启动飞书 + Claude(会引导配置飞书凭证)"
|
|
76
|
-
echo ""
|
|
14
|
+
bash init.sh --npm
|
package/stats/collector.py
CHANGED
|
@@ -18,8 +18,19 @@ from typing import Optional
|
|
|
18
18
|
from .machine import get_machine_id, get_machine_info
|
|
19
19
|
|
|
20
20
|
# SQLite 数据库路径(持久化,不受 /tmp 清理影响)
|
|
21
|
-
_DB_DIR = Path.home() / ".
|
|
21
|
+
_DB_DIR = Path.home() / ".remote-claude"
|
|
22
22
|
_DB_PATH = _DB_DIR / "stats.db"
|
|
23
|
+
_OLD_DB_DIR = Path.home() / ".local" / "share" / "remote-claude"
|
|
24
|
+
_OLD_DB_PATH = _OLD_DB_DIR / "stats.db"
|
|
25
|
+
|
|
26
|
+
# 兼容迁移:旧 DB 存在而新路径不存在时,自动迁移
|
|
27
|
+
if not _DB_PATH.exists() and _OLD_DB_PATH.exists():
|
|
28
|
+
try:
|
|
29
|
+
import shutil as _shutil
|
|
30
|
+
_DB_DIR.mkdir(parents=True, exist_ok=True)
|
|
31
|
+
_shutil.move(str(_OLD_DB_PATH), str(_DB_PATH))
|
|
32
|
+
except Exception:
|
|
33
|
+
pass
|
|
23
34
|
|
|
24
35
|
# 批量写入阈值
|
|
25
36
|
_FLUSH_INTERVAL = 10.0 # 秒
|
package/stats/machine.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
机器标识模块
|
|
3
3
|
|
|
4
|
-
持久化 UUID(~/.remote-claude-id),用于 Mixpanel distinct_id 和跨机器去重。
|
|
4
|
+
持久化 UUID(~/.remote-claude/machine-id),用于 Mixpanel distinct_id 和跨机器去重。
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import os
|
|
@@ -10,16 +10,27 @@ import uuid
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
_USER_DIR = Path.home() / ".remote-claude"
|
|
14
|
+
_ID_FILE = _USER_DIR / "machine-id"
|
|
15
|
+
_OLD_ID_FILE = Path.home() / ".remote-claude-id"
|
|
14
16
|
_machine_id: str | None = None
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
def get_machine_id() -> str:
|
|
18
|
-
"""获取(或生成)机器 UUID,持久化到 ~/.remote-claude-id"""
|
|
20
|
+
"""获取(或生成)机器 UUID,持久化到 ~/.remote-claude/machine-id"""
|
|
19
21
|
global _machine_id
|
|
20
22
|
if _machine_id:
|
|
21
23
|
return _machine_id
|
|
22
24
|
|
|
25
|
+
# 兼容迁移:旧文件存在而新文件不存在时,自动迁移
|
|
26
|
+
if not _ID_FILE.exists() and _OLD_ID_FILE.exists():
|
|
27
|
+
try:
|
|
28
|
+
import shutil
|
|
29
|
+
_USER_DIR.mkdir(parents=True, exist_ok=True)
|
|
30
|
+
shutil.move(str(_OLD_ID_FILE), str(_ID_FILE))
|
|
31
|
+
except Exception:
|
|
32
|
+
pass
|
|
33
|
+
|
|
23
34
|
if _ID_FILE.exists():
|
|
24
35
|
try:
|
|
25
36
|
_machine_id = _ID_FILE.read_text().strip()
|
|
@@ -31,6 +42,7 @@ def get_machine_id() -> str:
|
|
|
31
42
|
# 首次生成
|
|
32
43
|
_machine_id = str(uuid.uuid4())
|
|
33
44
|
try:
|
|
45
|
+
_USER_DIR.mkdir(parents=True, exist_ok=True)
|
|
34
46
|
_ID_FILE.write_text(_machine_id)
|
|
35
47
|
except Exception:
|
|
36
48
|
pass # 写失败也继续,只是无法持久化
|
package/stats/query.py
CHANGED
package/utils/session.py
CHANGED
|
@@ -16,9 +16,30 @@ import uuid
|
|
|
16
16
|
|
|
17
17
|
# 常量
|
|
18
18
|
SOCKET_DIR = Path("/tmp/remote-claude")
|
|
19
|
+
USER_DATA_DIR = Path.home() / ".remote-claude"
|
|
19
20
|
TMUX_SESSION_PREFIX = "rc-"
|
|
20
21
|
|
|
21
22
|
|
|
23
|
+
def get_env_file() -> Path:
|
|
24
|
+
"""获取 .env 配置文件路径"""
|
|
25
|
+
return USER_DATA_DIR / ".env"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_chat_bindings_file() -> Path:
|
|
29
|
+
"""获取飞书聊天绑定持久化文件路径"""
|
|
30
|
+
return USER_DATA_DIR / "lark_chat_bindings.json"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_lark_log_file() -> Path:
|
|
34
|
+
"""获取飞书客户端日志文件路径"""
|
|
35
|
+
return USER_DATA_DIR / "lark_client.log"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def ensure_user_data_dir():
|
|
39
|
+
"""确保用户数据目录存在"""
|
|
40
|
+
USER_DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
41
|
+
|
|
42
|
+
|
|
22
43
|
def _safe_filename(session_name: str) -> str:
|
|
23
44
|
"""将会话名转为安全文件名(/ 和 . 替换为 _)"""
|
|
24
45
|
return session_name.replace('/', '_').replace('.', '_')
|