diff --git a/README.md b/README.md index 235fd46..eec1887 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ npm run daemon -- logs # 查看日志 ## 前置条件 - Node.js >= 18 -- macOS 或 Linux +- macOS、Windows 或 Linux - 个人微信账号 - 已安装 [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI 并完成认证 diff --git a/README_en.md b/README_en.md index 7032917..f714fdf 100644 --- a/README_en.md +++ b/README_en.md @@ -121,7 +121,7 @@ The daemon long-polls WeChat for new messages, forwards them to the local `claud ## Prerequisites - Node.js >= 18 -- macOS or Linux +- macOS, Windows, or Linux - A personal WeChat account - [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI installed and authenticated diff --git a/SKILL.md b/SKILL.md index 0e8cac5..e84eec9 100644 --- a/SKILL.md +++ b/SKILL.md @@ -10,7 +10,7 @@ description: 微信消息桥接 - 在微信中与 Claude Code 聊天。支持文 ## 前置条件 - Node.js >= 18 -- macOS(daemon 使用 launchd 管理) +- macOS、Windows 或 Linux - 个人微信账号(需扫码绑定) - 已安装 Claude Code(`@anthropic-ai/claude-agent-sdk`) diff --git a/scripts/daemon.sh b/scripts/daemon.sh index acb05fe..6680924 100755 --- a/scripts/daemon.sh +++ b/scripts/daemon.sh @@ -375,6 +375,114 @@ linux_logs() { fi } +# ============================================================================= +# Windows (Git Bash / MINGW) functions +# ============================================================================= + +windows_pid_file() { + echo "${DATA_DIR}/${SERVICE_NAME}.pid" +} + +windows_start() { + local pid_file="$(windows_pid_file)" + local node_bin="$(command -v node 2>/dev/null || echo 'node')" + + if [ -f "$pid_file" ]; then + local old_pid=$(cat "$pid_file" 2>/dev/null) + if [ -n "$old_pid" ] && kill -0 "$old_pid" 2>/dev/null; then + echo "Already running (PID: $old_pid)" + exit 0 + fi + rm -f "$pid_file" + fi + + mkdir -p "$DATA_DIR/logs" + + echo "Starting wechat-claude-code daemon (Windows)..." + nohup "$node_bin" "${PROJECT_DIR}/dist/main.js" start \ + >> "$DATA_DIR/logs/stdout.log" \ + 2>> "$DATA_DIR/logs/stderr.log" & + local pid=$! + echo "$pid" > "$pid_file" + echo "Started (PID: $pid)" + echo "Logs: $DATA_DIR/logs/stdout.log" +} + +windows_stop() { + local pid_file="$(windows_pid_file)" + + if [ ! -f "$pid_file" ]; then + echo "Not running (no PID file)" + exit 0 + fi + + local pid=$(cat "$pid_file" 2>/dev/null) + if [ -z "$pid" ]; then + rm -f "$pid_file" + echo "Stopped" + exit 0 + fi + + if kill -0 "$pid" 2>/dev/null; then + kill "$pid" 2>/dev/null || true + local count=0 + while kill -0 "$pid" 2>/dev/null && [ $count -lt 10 ]; do + sleep 1 + count=$((count + 1)) + done + # Fallback: use taskkill if process still alive + if kill -0 "$pid" 2>/dev/null; then + taskkill //F //PID "$pid" 2>/dev/null || true + fi + echo "Stopped (PID: $pid)" + else + echo "Process not running (cleaning up PID file)" + fi + + rm -f "$pid_file" +} + +windows_status() { + local pid_file="$(windows_pid_file)" + + if [ ! -f "$pid_file" ]; then + echo "Not running" + exit 0 + fi + + local pid=$(cat "$pid_file" 2>/dev/null) + if [ -z "$pid" ]; then + echo "Not running (invalid PID file)" + exit 0 + fi + + if kill -0 "$pid" 2>/dev/null; then + echo "Running (PID: $pid)" + else + echo "Not running (stale PID file)" + fi +} + +windows_logs() { + local log_dir="${DATA_DIR}/logs" + if [ -d "$log_dir" ]; then + local latest=$(ls -t "${log_dir}"/bridge-*.log 2>/dev/null | head -1) + if [ -n "$latest" ]; then + tail -100 "$latest" + else + for f in "${log_dir}"/stdout.log "${log_dir}"/stderr.log; do + if [ -f "$f" ]; then + echo "=== $(basename "$f") ===" + tail -50 "$f" + echo "" + fi + done + fi + else + echo "No logs found" + fi +} + # ============================================================================= # Main dispatcher # ============================================================================= @@ -411,9 +519,23 @@ main() { ;; esac ;; + MINGW*|MSYS*|CYGWIN*) + case "$command" in + start) windows_start ;; + stop) windows_stop ;; + restart) windows_stop; sleep 1; windows_start ;; + status) windows_status ;; + logs) windows_logs ;; + *) + echo "Usage: daemon.sh {start|stop|restart|status|logs}" + echo "Platform: Windows (Git Bash)" + exit 1 + ;; + esac + ;; *) echo "Error: Unsupported platform '$OS_TYPE'" - echo "Supported platforms: macOS (Darwin), Linux" + echo "Supported platforms: macOS (Darwin), Linux, Windows (MINGW/MSYS)" exit 1 ;; esac diff --git a/src/tools/visualize-logs.ts b/src/tools/visualize-logs.ts index 33b952c..79c8466 100644 --- a/src/tools/visualize-logs.ts +++ b/src/tools/visualize-logs.ts @@ -721,7 +721,12 @@ function main() { writeFileSync(output, html, 'utf-8'); console.log(`Written to: ${output}`); if (open) { - execSync(`open "${output}"`); + const cmd = process.platform === 'win32' + ? `cmd /c start "" "${output}"` + : process.platform === 'darwin' + ? `open "${output}"` + : `xdg-open "${output}"`; + execSync(cmd); } } else { process.stdout.write(html); diff --git a/src/wechat/send.ts b/src/wechat/send.ts index 215dc8e..2731528 100644 --- a/src/wechat/send.ts +++ b/src/wechat/send.ts @@ -1,4 +1,5 @@ import { existsSync } from 'node:fs'; +import { homedir } from 'node:os'; import { resolve } from 'node:path'; import { WeChatApi } from './api.js'; import { MessageItemType, MessageType, MessageState, TypingStatus, type MessageItem, type OutboundMessage } from './types.js'; @@ -115,7 +116,7 @@ export function createSender(api: WeChatApi, botAccountId: string) { } async function sendFile(toUserId: string, contextToken: string, filePath: string): Promise { - const resolved = resolve(filePath.replace(/^~/, process.env.HOME || '')); + const resolved = resolve(filePath.replace(/^~/, homedir())); if (!existsSync(resolved)) { await sendText(toUserId, contextToken, `文件不存在: ${resolved}`); return;