weclaude 0.0.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.
- package/LICENSE +21 -0
- package/README.md +61 -0
- package/cli/wrc.sh +168 -0
- package/commands/wrc.md +7 -0
- package/config.example.jsonc +75 -0
- package/dist/cli/init.js +216 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/sync.js +130 -0
- package/dist/cli/sync.js.map +1 -0
- package/dist/daemon/approval.js +366 -0
- package/dist/daemon/approval.js.map +1 -0
- package/dist/daemon/ask.js +97 -0
- package/dist/daemon/ask.js.map +1 -0
- package/dist/daemon/cc-bridge.js +173 -0
- package/dist/daemon/cc-bridge.js.map +1 -0
- package/dist/daemon/claim.js +76 -0
- package/dist/daemon/claim.js.map +1 -0
- package/dist/daemon/http.js +82 -0
- package/dist/daemon/http.js.map +1 -0
- package/dist/daemon/inbound.js +145 -0
- package/dist/daemon/inbound.js.map +1 -0
- package/dist/daemon/index.js +85 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/mirror-bridge.js +539 -0
- package/dist/daemon/mirror-bridge.js.map +1 -0
- package/dist/daemon/outbound.js +33 -0
- package/dist/daemon/outbound.js.map +1 -0
- package/dist/daemon/pending.js +27 -0
- package/dist/daemon/pending.js.map +1 -0
- package/dist/daemon/redact.js +27 -0
- package/dist/daemon/redact.js.map +1 -0
- package/dist/daemon/session-cache.js +66 -0
- package/dist/daemon/session-cache.js.map +1 -0
- package/dist/daemon/sessions.js +35 -0
- package/dist/daemon/sessions.js.map +1 -0
- package/dist/daemon/ws.js +67 -0
- package/dist/daemon/ws.js.map +1 -0
- package/dist/mcp/server.js +82 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/shared/config-writer.js +39 -0
- package/dist/shared/config-writer.js.map +1 -0
- package/dist/shared/config.js +139 -0
- package/dist/shared/config.js.map +1 -0
- package/dist/shared/log.js +18 -0
- package/dist/shared/log.js.map +1 -0
- package/dist/shared/paths.js +6 -0
- package/dist/shared/paths.js.map +1 -0
- package/docs/DESIGN-INIT.md +125 -0
- package/docs/ONBOARDING.md +118 -0
- package/hooks/hooks.json +16 -0
- package/hooks/pre-tool-use.sh +81 -0
- package/launchd/com.cc-wecom.daemon.plist.template +45 -0
- package/package.json +77 -0
- package/scripts/install.sh +50 -0
- package/scripts/uninstall.sh +26 -0
- package/systemd/cc-wecom.service.template +17 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 cc-wecom contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# cc-wecom
|
|
2
|
+
|
|
3
|
+
WeCom (企业微信) 远程控制 Claude Code:把 PreToolUse 授权卡片推到 IM,点按钮放行;远程下发 `/wrc` 指令驱动 Claude 干活。
|
|
4
|
+
|
|
5
|
+
## 能做什么
|
|
6
|
+
|
|
7
|
+
- **IM 侧授权转发** — Claude 触发的 Bash / Edit 等敏感工具,会以按钮卡片推到企业微信,点 ✅/❌ 直接放行或拒绝;卡片就地刷新成决策结果。
|
|
8
|
+
- **远程 `/wrc`** — 在 IM 里给机器人发指令,daemon 通过 WebSocket 转给本机 Claude 执行,结果回推。
|
|
9
|
+
- **结构化消息** — 基于 `@wecom/aibot-node-sdk` 的按钮 / markdown 卡片,非纯文本。
|
|
10
|
+
- **MCP 主动推送** — Claude 通过 `wecom__send_markdown` 工具主动汇报。
|
|
11
|
+
|
|
12
|
+
## 上手 (3 步)
|
|
13
|
+
|
|
14
|
+
前置:Node ≥ 20、已装 `claude` 或 `claude-internal`、企业微信「智能机器人」的 `botId` + `secret`。
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install -g cc-wecom
|
|
18
|
+
wrc init
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`wrc init` 会交互式问 4 个值并落盘到 `~/.cc-wecom/`:
|
|
22
|
+
|
|
23
|
+
| 字段 | 落点 |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| botId / secret | `~/.cc-wecom/secrets.json` |
|
|
26
|
+
| Claude agent (`claude` / `claude-internal` / 自定义路径) | `~/.cc-wecom/config.jsonc` |
|
|
27
|
+
| 启用 PreToolUse hook | `~/.cc-wecom/config.jsonc` |
|
|
28
|
+
|
|
29
|
+
随后自动:编译 dist → `wrc sync` 注入 hook/MCP → 装 daemon (launchd/systemd --user) → 等 WS 鉴权。
|
|
30
|
+
|
|
31
|
+
**绑定默认会话**:CLI 提示后,在企业微信里给机器人发:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
将本对话设置为默认会话
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
10 分钟 claim 窗口内的这条精确匹配消息会被吃掉,把发送方 principal 写进 `defaultChat` + `allowFrom`。这是**唯一**绕过 `allowFrom` 的入口,消费完立刻关闭。
|
|
38
|
+
|
|
39
|
+
**演示**:CLI 自动跑一条三步指令 (`echo` → `sleep` → MCP 推送),IM 应收到两张授权卡片和一条 markdown 总结。
|
|
40
|
+
|
|
41
|
+
## 常用命令
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
wrc status # daemon + WS 健康
|
|
45
|
+
wrc logs -f # 实时日志
|
|
46
|
+
wrc send <chat> <text> # 主动推消息
|
|
47
|
+
wrc reload # 重启 daemon
|
|
48
|
+
wrc unsync # 卸载 hook/MCP (保留 daemon)
|
|
49
|
+
wrc uninstall # 完整卸载 (先于 npm uninstall -g)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
> ⚠️ 卸载顺序:**先** `wrc uninstall` **再** `npm uninstall -g cc-wecom`,否则 launchd/systemd 会反复拉起已删除的二进制。`~/.cc-wecom/` 下的配置/secrets 不会被清,二次安装可复用。
|
|
53
|
+
|
|
54
|
+
## 详细文档
|
|
55
|
+
|
|
56
|
+
- [docs/ONBOARDING.md](docs/ONBOARDING.md) — 上手细节、排错、多机部署
|
|
57
|
+
- [docs/DESIGN-INIT.md](docs/DESIGN-INIT.md) — `init` 流程的设计取舍
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
MIT
|
package/cli/wrc.sh
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# /wrc CLI — talks to local cc-wecom daemon over HTTP and toggles launchd/systemd.
|
|
3
|
+
set -uo pipefail
|
|
4
|
+
|
|
5
|
+
DAEMON_BASE="${CC_WECOM_DAEMON_BASE:-http://127.0.0.1:17890}"
|
|
6
|
+
LABEL="com.cc-wecom.daemon"
|
|
7
|
+
HOME_DIR="$HOME"
|
|
8
|
+
OS="$(uname -s)"
|
|
9
|
+
|
|
10
|
+
cmd="${1:-status}"
|
|
11
|
+
shift || true
|
|
12
|
+
|
|
13
|
+
usage() {
|
|
14
|
+
cat <<'EOF'
|
|
15
|
+
wrc <subcommand>
|
|
16
|
+
init interactive onboarding (write config, install daemon, claim default chat, run demo)
|
|
17
|
+
status daemon health + connection state
|
|
18
|
+
start load resident daemon (launchd/systemd)
|
|
19
|
+
stop unload resident daemon
|
|
20
|
+
restart stop + start
|
|
21
|
+
reload quick: shutdown via HTTP + kickstart
|
|
22
|
+
mirror [chat] attach current Claude session for mirror push (target overrides config)
|
|
23
|
+
mirror-status show current mirror attachment
|
|
24
|
+
send <chat> <text> proactive markdown message
|
|
25
|
+
pending list outstanding approval req_ids
|
|
26
|
+
logs [-f] tail daemon log
|
|
27
|
+
config-path show resolved config path
|
|
28
|
+
sync write hooks/MCP/env into sync.targets settings.json
|
|
29
|
+
unsync remove our entries from sync.targets settings.json
|
|
30
|
+
uninstall full teardown: unsync + remove daemon (run before `npm uninstall -g`)
|
|
31
|
+
help
|
|
32
|
+
EOF
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
http_get() { curl -sS --max-time 5 "$DAEMON_BASE$1"; }
|
|
36
|
+
http_post() { curl -sS --max-time 5 -X POST -H 'content-type: application/json' -d "${2:-{\}}" "$DAEMON_BASE$1"; }
|
|
37
|
+
|
|
38
|
+
svc_load() {
|
|
39
|
+
case "$OS" in
|
|
40
|
+
Darwin) launchctl load -w "$HOME_DIR/Library/LaunchAgents/${LABEL}.plist" ;;
|
|
41
|
+
Linux) systemctl --user start cc-wecom.service ;;
|
|
42
|
+
esac
|
|
43
|
+
}
|
|
44
|
+
svc_unload() {
|
|
45
|
+
case "$OS" in
|
|
46
|
+
Darwin) launchctl unload "$HOME_DIR/Library/LaunchAgents/${LABEL}.plist" ;;
|
|
47
|
+
Linux) systemctl --user stop cc-wecom.service ;;
|
|
48
|
+
esac
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# Walk the parent-process chain looking for `claude-internal`. Mirror commands
|
|
52
|
+
# inject into a live Claude session; we only enable them under claude-internal
|
|
53
|
+
# (the Tencent-internal CC build) for now to avoid surprising upstream Claude.
|
|
54
|
+
require_claude_internal() {
|
|
55
|
+
local pid=$PPID
|
|
56
|
+
local i=0
|
|
57
|
+
while (( i < 8 )) && [[ "$pid" -gt 1 ]]; do
|
|
58
|
+
local args
|
|
59
|
+
args=$(ps -p "$pid" -o args= 2>/dev/null) || break
|
|
60
|
+
[[ "$args" == *claude-internal* ]] && return 0
|
|
61
|
+
pid=$(ps -p "$pid" -o ppid= 2>/dev/null | tr -d ' ')
|
|
62
|
+
[[ -z "$pid" ]] && break
|
|
63
|
+
(( i++ ))
|
|
64
|
+
done
|
|
65
|
+
echo "wrc $cmd: only available inside claude-internal" >&2
|
|
66
|
+
exit 1
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
case "$cmd" in
|
|
70
|
+
init)
|
|
71
|
+
NODE_BIN="$(command -v node)"
|
|
72
|
+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
73
|
+
SCRIPT="$REPO_ROOT/dist/cli/init.js"
|
|
74
|
+
if [[ ! -f "$SCRIPT" ]]; then
|
|
75
|
+
echo "[wrc init] building..."
|
|
76
|
+
(cd "$REPO_ROOT" && npm install --silent && npx tsc -p tsconfig.json) || { echo "build failed"; exit 1; }
|
|
77
|
+
fi
|
|
78
|
+
exec "$NODE_BIN" "$SCRIPT" "$@"
|
|
79
|
+
;;
|
|
80
|
+
status)
|
|
81
|
+
if ! out=$(http_get /status 2>/dev/null); then
|
|
82
|
+
echo "daemon: down ($DAEMON_BASE)"
|
|
83
|
+
exit 1
|
|
84
|
+
fi
|
|
85
|
+
echo "$out" | jq . 2>/dev/null || echo "$out"
|
|
86
|
+
;;
|
|
87
|
+
start) svc_load && echo "daemon: started" ;;
|
|
88
|
+
stop) svc_unload && echo "daemon: stopped" ;;
|
|
89
|
+
restart) svc_unload || true; sleep 1; svc_load && echo "daemon: restarted" ;;
|
|
90
|
+
reload)
|
|
91
|
+
# KeepAlive.SuccessfulExit=false, so /shutdown alone won't respawn — kick it.
|
|
92
|
+
http_post /shutdown >/dev/null 2>&1 || true
|
|
93
|
+
case "$OS" in
|
|
94
|
+
Darwin) launchctl kickstart -k "gui/$(id -u)/${LABEL}" >/dev/null && echo "daemon: reloaded" ;;
|
|
95
|
+
Linux) systemctl --user restart cc-wecom.service && echo "daemon: reloaded" ;;
|
|
96
|
+
esac
|
|
97
|
+
;;
|
|
98
|
+
send)
|
|
99
|
+
chat="${1:-}"; shift || true
|
|
100
|
+
text="${*:-}"
|
|
101
|
+
if [[ -z "$chat" || -z "$text" ]]; then
|
|
102
|
+
echo "usage: wrc send <chat> <text>"; exit 1
|
|
103
|
+
fi
|
|
104
|
+
body=$(jq -nc --arg c "$chat" --arg t "$text" '{chat:$c,text:$t}')
|
|
105
|
+
http_post /message "$body"
|
|
106
|
+
;;
|
|
107
|
+
pending) http_get /pending | jq . 2>/dev/null || http_get /pending ;;
|
|
108
|
+
logs)
|
|
109
|
+
log_path=$(http_get /status 2>/dev/null | jq -r '.logFile // empty' 2>/dev/null || true)
|
|
110
|
+
log_path="${log_path:-$HOME_DIR/.cc-wecom/daemon.log}"
|
|
111
|
+
if [[ "${1:-}" == "-f" ]]; then tail -f "$log_path"; else tail -n 100 "$log_path"; fi
|
|
112
|
+
;;
|
|
113
|
+
config-path) http_get /status | jq -r '.sourcePath // empty' ;;
|
|
114
|
+
uninstall)
|
|
115
|
+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
116
|
+
NODE_BIN="$(command -v node)"
|
|
117
|
+
SYNC_SCRIPT="$REPO_ROOT/dist/cli/sync.js"
|
|
118
|
+
[[ -f "$SYNC_SCRIPT" ]] && "$NODE_BIN" "$SYNC_SCRIPT" --remove || true
|
|
119
|
+
bash "$REPO_ROOT/scripts/uninstall.sh"
|
|
120
|
+
echo "[wrc] cleaned. safe to 'npm uninstall -g cc-wecom' (state at ~/.cc-wecom kept)"
|
|
121
|
+
;;
|
|
122
|
+
mirror)
|
|
123
|
+
require_claude_internal
|
|
124
|
+
# Pick the most-recently-written jsonl in this cwd's claude project dir.
|
|
125
|
+
# That's the live session writing this very turn.
|
|
126
|
+
cwd="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
127
|
+
enc="$(printf %s "$cwd" | sed 's|/|-|g')"
|
|
128
|
+
# claude-internal stores under ~/.claude-internal/projects/, official claude
|
|
129
|
+
# under ~/.claude/projects/. Prefer the internal one since this command is
|
|
130
|
+
# gated to claude-internal anyway.
|
|
131
|
+
proj=""
|
|
132
|
+
for base in "$HOME/.claude-internal/projects" "$HOME/.claude/projects"; do
|
|
133
|
+
if [[ -d "$base/$enc" ]]; then proj="$base/$enc"; break; fi
|
|
134
|
+
done
|
|
135
|
+
if [[ -z "$proj" ]]; then
|
|
136
|
+
echo "no claude project dir for cwd: $cwd"
|
|
137
|
+
echo "looked in: $HOME/.claude-internal/projects/$enc and $HOME/.claude/projects/$enc"
|
|
138
|
+
exit 1
|
|
139
|
+
fi
|
|
140
|
+
latest=$(ls -t "$proj"/*.jsonl 2>/dev/null | head -1)
|
|
141
|
+
if [[ -z "$latest" ]]; then
|
|
142
|
+
echo "no .jsonl under $proj"; exit 1
|
|
143
|
+
fi
|
|
144
|
+
sid="$(basename "$latest" .jsonl)"
|
|
145
|
+
target="${1:-}"
|
|
146
|
+
body=$(jq -nc --arg s "$sid" --arg p "$latest" --arg t "$target" \
|
|
147
|
+
'{sessionId:$s, jsonlPath:$p} + (if $t == "" then {} else {target:$t} end)')
|
|
148
|
+
http_post /mirror/attach "$body" | jq . 2>/dev/null || true
|
|
149
|
+
;;
|
|
150
|
+
mirror-status)
|
|
151
|
+
require_claude_internal
|
|
152
|
+
http_get /mirror/status | jq . 2>/dev/null || http_get /mirror/status
|
|
153
|
+
;;
|
|
154
|
+
sync)
|
|
155
|
+
NODE_BIN="$(command -v node)"
|
|
156
|
+
SCRIPT="$(cd "$(dirname "$0")/.." && pwd)/dist/cli/sync.js"
|
|
157
|
+
[[ -f "$SCRIPT" ]] || { echo "build first: npm run build"; exit 1; }
|
|
158
|
+
exec "$NODE_BIN" "$SCRIPT" "$@"
|
|
159
|
+
;;
|
|
160
|
+
unsync)
|
|
161
|
+
NODE_BIN="$(command -v node)"
|
|
162
|
+
SCRIPT="$(cd "$(dirname "$0")/.." && pwd)/dist/cli/sync.js"
|
|
163
|
+
[[ -f "$SCRIPT" ]] || { echo "build first: npm run build"; exit 1; }
|
|
164
|
+
exec "$NODE_BIN" "$SCRIPT" --remove "$@"
|
|
165
|
+
;;
|
|
166
|
+
help|-h|--help) usage ;;
|
|
167
|
+
*) echo "unknown subcommand: $cmd"; usage; exit 2 ;;
|
|
168
|
+
esac
|
package/commands/wrc.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: WeCom remote control — daemon status / start / stop / send
|
|
3
|
+
allowed-tools: Bash(${CLAUDE_PLUGIN_ROOT}/cli/wrc.sh:*)
|
|
4
|
+
argument-hint: status|start|stop|restart|reload|mirror [chat]|mirror-status|send <chat> <text>|pending|logs|config-path|help
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
!`${CLAUDE_PLUGIN_ROOT}/cli/wrc.sh $ARGUMENTS`
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Declarative config for cc-wecom. Copy to ~/.cc-wecom/config.jsonc and edit.
|
|
2
|
+
// All paths support `~`. Secrets (botId/secret) can also live in ~/.cc-wecom/secrets.json
|
|
3
|
+
// (same shape, deep-merged on top — keep secrets out of dotfile repos).
|
|
4
|
+
{
|
|
5
|
+
"$schema": "./schema.json",
|
|
6
|
+
|
|
7
|
+
// ── WeCom bot credentials ─────────────────────────────────────────
|
|
8
|
+
"bot": {
|
|
9
|
+
"botId": "", // 智能机器人 ID
|
|
10
|
+
"secret": "", // 智能机器人 secret
|
|
11
|
+
"websocketUrl": "wss://openws.work.weixin.qq.com"
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
// ── Default chat target for proactive outbound ────────────────────
|
|
15
|
+
// Format: "user:<userid>" | "chat:<chatid>" | "group:<groupid>"
|
|
16
|
+
"defaultChat": "",
|
|
17
|
+
|
|
18
|
+
// ── Daemon (resident process) ─────────────────────────────────────
|
|
19
|
+
"daemon": {
|
|
20
|
+
"host": "127.0.0.1",
|
|
21
|
+
"port": 17890,
|
|
22
|
+
"stateDir": "~/.cc-wecom/state",
|
|
23
|
+
"logFile": "~/.cc-wecom/daemon.log",
|
|
24
|
+
"logLevel": "info" // trace|debug|info|warn|error
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
// ── /wrc remote-control behavior ──────────────────────────────────
|
|
28
|
+
"wrc": {
|
|
29
|
+
// Allow which WeCom users / chats to drive CC. Empty = deny all.
|
|
30
|
+
"allowFrom": [], // ["user:zhangsan", "chat:wcXXXX"]
|
|
31
|
+
// headless: spawn `claude -p` per msg with per-principal session map.
|
|
32
|
+
// mirror : attach to an existing interactive `claude` session by injecting
|
|
33
|
+
// via `claude --resume <sid> -p` AND tailing the same session
|
|
34
|
+
// jsonl, pushing live assistant output to WeCom.
|
|
35
|
+
"mode": "headless",
|
|
36
|
+
"claudeBin": "claude",
|
|
37
|
+
"cwd": "~", // workdir (mirror: must match the running claude's cwd)
|
|
38
|
+
"sessionMapFile": "~/.cc-wecom/sessions.json",
|
|
39
|
+
"extraArgs": [], // appended to `claude -p`
|
|
40
|
+
"mirror": {
|
|
41
|
+
// Pin a session id; empty → auto-pick latest .jsonl mtime under
|
|
42
|
+
// ~/.claude/projects/<encoded(cwd)>/.
|
|
43
|
+
"sessionId": "",
|
|
44
|
+
// Where to push live assistant output. Empty → defaultChat.
|
|
45
|
+
"pushChat": "", // "user:<userid>" | "chat:<chatid>"
|
|
46
|
+
"chunkChars": 1800
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
// ── PreToolUse approval (all tools → button card; align with auth-helper) ─
|
|
51
|
+
"approval": {
|
|
52
|
+
"enabled": true,
|
|
53
|
+
"matcher": ".*", // 全量拦截;Bash 只读命令(grep/ls/cat/...)走脚本 fast-path
|
|
54
|
+
"approvers": [], // ["user:zhangsan"] empty → defaultChat
|
|
55
|
+
"hookTimeoutSec": 1810, // hook curl --max-time
|
|
56
|
+
"longPollSec": 1800, // daemon long-poll
|
|
57
|
+
"sessionCacheMinutes": 30, // (session_id, tool, cmd_prefix_hash) → decision
|
|
58
|
+
"windowMinutes": 5, // 「⏱ N 分钟」按钮:开窗口期间所有请求自动 allow
|
|
59
|
+
"windowMinutes": 5, // 「⏱ N 分钟」按钮:开窗口期间所有请求自动 allow
|
|
60
|
+
"sensitiveArgRedact": true, // mask password/token-like values before sending to IM
|
|
61
|
+
"fallbackOnError": "ask" // never "deny" — never break workflow
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
// ── claude-internal wrapper sync ──────────────────────────────────
|
|
65
|
+
// `wrc sync` writes hooks/MCP/permissions into target settings.json.
|
|
66
|
+
"sync": {
|
|
67
|
+
"targets": [
|
|
68
|
+
{
|
|
69
|
+
"kind": "claude-internal",
|
|
70
|
+
"settingsPath": "~/.claude/settings.json",
|
|
71
|
+
"scope": "user" // user | project | local
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
}
|
package/dist/cli/init.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// `wrc init` — interactive onboarding for new users.
|
|
3
|
+
// Flow:
|
|
4
|
+
// 1. Prompt creds + agent kind + hook toggle.
|
|
5
|
+
// 2. Write ~/.cc-wecom/config.jsonc + secrets.json (split secrets).
|
|
6
|
+
// 3. Build (if needed), run `wrc sync` against chosen agent settings.json,
|
|
7
|
+
// install resident daemon.
|
|
8
|
+
// 4. Arm bootstrap claim. Wait for the user to send a magic phrase in IM.
|
|
9
|
+
// That message bypasses allowFrom, sets defaultChat, and adds the sender.
|
|
10
|
+
// 5. Demo: spawn `claude -p` with a prompt that exercises Bash → triggers
|
|
11
|
+
// approval card → sleeps → sends final markdown via MCP. End-to-end smoke.
|
|
12
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
13
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
14
|
+
import { dirname, resolve as pathResolve } from "node:path";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { homedir } from "node:os";
|
|
17
|
+
import { input, password, select, confirm } from "@inquirer/prompts";
|
|
18
|
+
import { patchJsonc } from "../shared/config-writer.js";
|
|
19
|
+
import { expandHome } from "../shared/paths.js";
|
|
20
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const REPO = pathResolve(here, "..", "..");
|
|
22
|
+
const CONFIG = "~/.cc-wecom/config.jsonc";
|
|
23
|
+
const SECRETS = "~/.cc-wecom/secrets.json";
|
|
24
|
+
const CLAIM_PHRASE = "将本对话设置为默认会话";
|
|
25
|
+
// ── Pretty output (no ink — keep deps light) ─────────────────────────
|
|
26
|
+
const c = {
|
|
27
|
+
dim: (s) => `\x1b[2m${s}\x1b[0m`,
|
|
28
|
+
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
29
|
+
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
30
|
+
cyan: (s) => `\x1b[36m${s}\x1b[0m`,
|
|
31
|
+
yellow: (s) => `\x1b[33m${s}\x1b[0m`,
|
|
32
|
+
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
33
|
+
};
|
|
34
|
+
const log = (s) => console.log(s);
|
|
35
|
+
const step = (n, title) => log(`\n${c.cyan(`[${n}/3]`)} ${c.bold(title)}`);
|
|
36
|
+
const settingsPathFor = (kind, custom) => {
|
|
37
|
+
switch (kind) {
|
|
38
|
+
case "claude": return "~/.claude/settings.json";
|
|
39
|
+
case "claude-internal": return "~/.claude-internal/settings.json";
|
|
40
|
+
case "custom": return custom ?? "";
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const claudeBinFor = (kind) => kind === "claude-internal" ? "claude-internal" : "claude";
|
|
44
|
+
// ── HTTP helpers (talk to local daemon) ──────────────────────────────
|
|
45
|
+
const DAEMON = "http://127.0.0.1:17890";
|
|
46
|
+
const get = async (p) => {
|
|
47
|
+
const r = await fetch(`${DAEMON}${p}`);
|
|
48
|
+
return r.json().catch(() => ({}));
|
|
49
|
+
};
|
|
50
|
+
const post = async (p, body) => {
|
|
51
|
+
const r = await fetch(`${DAEMON}${p}`, {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: { "content-type": "application/json" },
|
|
54
|
+
body: JSON.stringify(body),
|
|
55
|
+
});
|
|
56
|
+
return r.json().catch(() => ({}));
|
|
57
|
+
};
|
|
58
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
59
|
+
// ── Build + install + reload daemon ──────────────────────────────────
|
|
60
|
+
const ensureBuild = () => {
|
|
61
|
+
if (existsSync(`${REPO}/dist/daemon/index.js`))
|
|
62
|
+
return;
|
|
63
|
+
log(c.dim(" building..."));
|
|
64
|
+
const r = spawnSync("npx", ["tsc", "-p", "tsconfig.json"], { cwd: REPO, stdio: "inherit" });
|
|
65
|
+
if (r.status !== 0)
|
|
66
|
+
throw new Error("build failed");
|
|
67
|
+
};
|
|
68
|
+
const runSync = () => {
|
|
69
|
+
log(c.dim(" syncing hooks/MCP/env into agent settings.json..."));
|
|
70
|
+
const r = spawnSync(process.execPath, [`${REPO}/dist/cli/sync.js`], { stdio: "inherit" });
|
|
71
|
+
if (r.status !== 0)
|
|
72
|
+
throw new Error("sync failed");
|
|
73
|
+
};
|
|
74
|
+
const installDaemon = () => {
|
|
75
|
+
log(c.dim(" installing resident daemon..."));
|
|
76
|
+
const r = spawnSync("bash", [`${REPO}/scripts/install.sh`], { stdio: "inherit" });
|
|
77
|
+
if (r.status !== 0)
|
|
78
|
+
throw new Error("install.sh failed");
|
|
79
|
+
};
|
|
80
|
+
const waitDaemonReady = async (timeoutMs) => {
|
|
81
|
+
const deadline = Date.now() + timeoutMs;
|
|
82
|
+
while (Date.now() < deadline) {
|
|
83
|
+
try {
|
|
84
|
+
const s = (await get("/status"));
|
|
85
|
+
if (s.wsConnected)
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
/* not up yet */
|
|
90
|
+
}
|
|
91
|
+
await sleep(500);
|
|
92
|
+
}
|
|
93
|
+
throw new Error("daemon did not become ready in time");
|
|
94
|
+
};
|
|
95
|
+
// ── Main flow ────────────────────────────────────────────────────────
|
|
96
|
+
const main = async () => {
|
|
97
|
+
log(c.bold("\ncc-wecom · 新用户引导\n"));
|
|
98
|
+
log(c.dim(" 目标:3 步内完成 → IM 授权转发 + 远程 CC 控制可用。\n"));
|
|
99
|
+
if (existsSync(expandHome(CONFIG))) {
|
|
100
|
+
const ok = await confirm({
|
|
101
|
+
message: `检测到已有 ${CONFIG},覆盖关键字段后继续?`,
|
|
102
|
+
default: false,
|
|
103
|
+
});
|
|
104
|
+
if (!ok) {
|
|
105
|
+
log(c.yellow("已取消。"));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// ── Step 1: prompts ────────────────────────────────────────────
|
|
110
|
+
step(1, "采集配置");
|
|
111
|
+
const botId = await input({ message: "智能机器人 botId:", required: true });
|
|
112
|
+
const secret = await password({ message: "机器人 secret:", mask: "•" });
|
|
113
|
+
const agentKind = (await select({
|
|
114
|
+
message: "选择 Claude agent:",
|
|
115
|
+
choices: [
|
|
116
|
+
{ name: "claude (Anthropic 官方)", value: "claude" },
|
|
117
|
+
{ name: "claude-internal (Tencent 内部)", value: "claude-internal" },
|
|
118
|
+
{ name: "自定义路径", value: "custom" },
|
|
119
|
+
],
|
|
120
|
+
default: "claude-internal",
|
|
121
|
+
}));
|
|
122
|
+
const customSettings = agentKind === "custom"
|
|
123
|
+
? await input({ message: "settings.json 绝对路径:", required: true })
|
|
124
|
+
: "";
|
|
125
|
+
const enableHook = await confirm({
|
|
126
|
+
message: "开启 PreToolUse 授权拦截 hook?(IM 按钮卡片授权)",
|
|
127
|
+
default: true,
|
|
128
|
+
});
|
|
129
|
+
const settings = settingsPathFor(agentKind, customSettings);
|
|
130
|
+
const claudeBin = claudeBinFor(agentKind);
|
|
131
|
+
// ── Step 1b: write configs ─────────────────────────────────────
|
|
132
|
+
log(c.dim(` 写入 ${CONFIG} ...`));
|
|
133
|
+
patchJsonc(CONFIG, [
|
|
134
|
+
{ path: ["bot", "websocketUrl"], value: "wss://openws.work.weixin.qq.com" },
|
|
135
|
+
{ path: ["defaultChat"], value: "" },
|
|
136
|
+
{ path: ["wrc", "mode"], value: "headless" },
|
|
137
|
+
{ path: ["wrc", "claudeBin"], value: claudeBin },
|
|
138
|
+
{ path: ["wrc", "cwd"], value: "~" },
|
|
139
|
+
{ path: ["wrc", "allowFrom"], value: [] },
|
|
140
|
+
{ path: ["approval", "enabled"], value: enableHook },
|
|
141
|
+
{ path: ["approval", "matcher"], value: ".*" },
|
|
142
|
+
{ path: ["sync", "targets"], value: [{ kind: "claude-internal", settingsPath: settings, scope: "user" }] },
|
|
143
|
+
]);
|
|
144
|
+
log(c.dim(` 写入 ${SECRETS} ...`));
|
|
145
|
+
patchJsonc(SECRETS, [
|
|
146
|
+
{ path: ["bot", "botId"], value: botId },
|
|
147
|
+
{ path: ["bot", "secret"], value: secret },
|
|
148
|
+
]);
|
|
149
|
+
ensureBuild();
|
|
150
|
+
if (enableHook)
|
|
151
|
+
runSync();
|
|
152
|
+
installDaemon();
|
|
153
|
+
log(c.dim(" 等待 daemon 上线..."));
|
|
154
|
+
await waitDaemonReady(20_000);
|
|
155
|
+
log(c.green(" ✓ daemon ready"));
|
|
156
|
+
// ── Step 2: claim default chat ─────────────────────────────────
|
|
157
|
+
step(2, "绑定默认会话");
|
|
158
|
+
await post("/claim/start", { phrase: CLAIM_PHRASE, ttlSec: 600 });
|
|
159
|
+
log(`\n ${c.bold("→ 现在打开企业微信,给该机器人发送:")}`);
|
|
160
|
+
log(` ${c.cyan(CLAIM_PHRASE)}\n`);
|
|
161
|
+
log(c.dim(" 等待中... (10 分钟超时)"));
|
|
162
|
+
const claimed = await pollClaim(10 * 60_000);
|
|
163
|
+
if (!claimed) {
|
|
164
|
+
log(c.red("\n 超时未收到消息。可稍后手动编辑 config 的 defaultChat / wrc.allowFrom。"));
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
log(c.green(`\n ✓ 已绑定: ${c.bold(claimed)}`));
|
|
168
|
+
// ── Step 3: live demo ──────────────────────────────────────────
|
|
169
|
+
step(3, "授权转发演示");
|
|
170
|
+
if (!enableHook) {
|
|
171
|
+
log(c.yellow(" hook 未启用 — 跳过授权演示。"));
|
|
172
|
+
log(c.green("\n✅ 引导完成。"));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
log(c.dim(" 即将启动 claude,触发一次 Bash 授权 → 你会在 WeCom 收到按钮卡片。"));
|
|
176
|
+
log(c.dim(" 点击「✅」放行即可观察后续 markdown 推送。\n"));
|
|
177
|
+
await runDemo(claimed, claudeBin);
|
|
178
|
+
log(c.green("\n✅ 引导完成。后续可用 `wrc status` / `wrc logs -f` 观察。"));
|
|
179
|
+
};
|
|
180
|
+
const pollClaim = async (timeoutMs) => {
|
|
181
|
+
const deadline = Date.now() + timeoutMs;
|
|
182
|
+
while (Date.now() < deadline) {
|
|
183
|
+
const s = (await get("/claim/status"));
|
|
184
|
+
if (s.claimed)
|
|
185
|
+
return s.claimed.principal;
|
|
186
|
+
if (s.armed === false)
|
|
187
|
+
break;
|
|
188
|
+
await sleep(1000);
|
|
189
|
+
}
|
|
190
|
+
return undefined;
|
|
191
|
+
};
|
|
192
|
+
const DEMO_PROMPT = (chat) => [
|
|
193
|
+
"请按顺序执行,不要输出多余解释:",
|
|
194
|
+
"1. 用 Bash 运行 `echo hello world from cc-wecom`。",
|
|
195
|
+
"2. 用 Bash 运行 `sleep 3`。",
|
|
196
|
+
`3. 用 mcp 工具 \`wecom__send_markdown\` 向 chat="${chat}" 发送 content="✅ cc-wecom 演示完成:hello world"。`,
|
|
197
|
+
].join("\n");
|
|
198
|
+
const runDemo = (chat, claudeBin) => new Promise((resolve) => {
|
|
199
|
+
const proc = spawn(claudeBin, ["-p", DEMO_PROMPT(chat)], {
|
|
200
|
+
stdio: ["ignore", "inherit", "inherit"],
|
|
201
|
+
env: { ...process.env, HOME: process.env.HOME ?? homedir() },
|
|
202
|
+
});
|
|
203
|
+
proc.on("close", () => resolve());
|
|
204
|
+
proc.on("error", (e) => {
|
|
205
|
+
log(c.red(` demo spawn 失败: ${e.message}`));
|
|
206
|
+
resolve();
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
main().catch((e) => {
|
|
210
|
+
// eslint-disable-next-line no-console
|
|
211
|
+
console.error(c.red(`\n[wrc-init] ${e.message}`));
|
|
212
|
+
process.exit(1);
|
|
213
|
+
});
|
|
214
|
+
// keep loadConfig reference reachable for tree-shaking sanity (used by sub-binaries)
|
|
215
|
+
void readFileSync;
|
|
216
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../cli/init.ts"],"names":[],"mappings":"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"}
|