weclaude 0.0.4 → 0.1.1
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 +1 -1
- package/README.md +105 -28
- package/cli/{wrc.sh → weclaude.sh} +34 -18
- package/commands/wrc.md +4 -4
- package/config.example.jsonc +6 -6
- package/dist/cli/init.js +10 -10
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/sync.js +35 -18
- package/dist/cli/sync.js.map +1 -1
- package/dist/daemon/approval.js +487 -37
- package/dist/daemon/approval.js.map +1 -1
- package/dist/daemon/cc-bridge.js +37 -20
- package/dist/daemon/cc-bridge.js.map +1 -1
- package/dist/daemon/claim.js +20 -1
- package/dist/daemon/claim.js.map +1 -1
- package/dist/daemon/detail.js +500 -0
- package/dist/daemon/detail.js.map +1 -0
- package/dist/daemon/http.js +2 -1
- package/dist/daemon/http.js.map +1 -1
- package/dist/daemon/inbound.js +115 -21
- package/dist/daemon/inbound.js.map +1 -1
- package/dist/daemon/index.js +30 -8
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/mirror-bridge.js +1010 -153
- package/dist/daemon/mirror-bridge.js.map +1 -1
- package/dist/daemon/mirror-store.js +39 -0
- package/dist/daemon/mirror-store.js.map +1 -0
- package/dist/daemon/pending.js +46 -0
- package/dist/daemon/pending.js.map +1 -1
- package/dist/daemon/session-cache.js +71 -3
- package/dist/daemon/session-cache.js.map +1 -1
- package/dist/daemon/spawn-tmux.js +132 -0
- package/dist/daemon/spawn-tmux.js.map +1 -0
- package/dist/mcp/server.js +104 -65
- package/dist/mcp/server.js.map +1 -1
- package/dist/shared/config-writer.js +1 -1
- package/dist/shared/config.js +34 -20
- package/dist/shared/config.js.map +1 -1
- package/dist/shared/paths.js +6 -0
- package/dist/shared/paths.js.map +1 -1
- package/docs/DESIGN-INIT.md +6 -6
- package/docs/ONBOARDING.md +25 -25
- package/hooks/pre-tool-use.sh +42 -7
- package/launchd/{com.cc-wecom.daemon.plist.template → com.weclaude.daemon.plist.template} +3 -3
- package/package.json +10 -11
- package/scripts/install.sh +6 -6
- package/scripts/uninstall.sh +3 -3
- package/systemd/{cc-wecom.service.template → weclaude.service.template} +3 -3
package/hooks/pre-tool-use.sh
CHANGED
|
@@ -1,18 +1,53 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
2
|
+
# weclaude PreToolUse hook: forward to local daemon → long-poll → emit decision.
|
|
3
3
|
# Any failure → ask (never break workflow).
|
|
4
4
|
set -uo pipefail
|
|
5
5
|
|
|
6
|
-
DAEMON_URL="${
|
|
7
|
-
HOOK_TIMEOUT="${
|
|
6
|
+
DAEMON_URL="${WECLAUDE_DAEMON_URL:-http://127.0.0.1:17890/approve}"
|
|
7
|
+
HOOK_TIMEOUT="${WECLAUDE_HOOK_TIMEOUT:-1810}"
|
|
8
|
+
STATE_DIR="${WECLAUDE_STATE_DIR:-$HOME/.weclaude/state}"
|
|
9
|
+
# Fallback policy when the daemon is unreachable / replies garbage. ask|allow|deny.
|
|
10
|
+
# Default keeps the safe behavior; set to `allow` in trusted local-only setups.
|
|
11
|
+
FALLBACK="${WECLAUDE_HOOK_FALLBACK:-ask}"
|
|
8
12
|
|
|
9
13
|
emit() {
|
|
10
14
|
local decision="$1" reason="${2:-}"
|
|
11
|
-
|
|
15
|
+
# 用 jq 拼 JSON: reason 可能含字面引号(askq deny 把答案塞进 reason),
|
|
16
|
+
# printf 直接拼会漏出内层 " 把 JSON 撕烂, Claude Code 解析失败 fallback
|
|
17
|
+
# 弹原生 picker → askq 失效。
|
|
18
|
+
if command -v jq >/dev/null 2>&1; then
|
|
19
|
+
jq -cn --arg d "$decision" --arg r "weclaude: $reason" \
|
|
20
|
+
'{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:$d,permissionDecisionReason:$r}}'
|
|
21
|
+
else
|
|
22
|
+
# jq 缺失兜底: 手动转义 \ 和 " (足够覆盖 reason 里的字面引号)。
|
|
23
|
+
local esc="${reason//\\/\\\\}"; esc="${esc//\"/\\\"}"
|
|
24
|
+
printf '%s\n' "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"${decision}\",\"permissionDecisionReason\":\"weclaude: ${esc}\"}}"
|
|
25
|
+
fi
|
|
12
26
|
exit 0
|
|
13
27
|
}
|
|
14
28
|
ask() { emit "ask" "${1:-bridge unreachable}"; }
|
|
15
29
|
|
|
30
|
+
# Daemon-down fallback. Consults the persisted auto-approve window first so a
|
|
31
|
+
# session-level "allow N min" survives daemon restart / outage; otherwise falls
|
|
32
|
+
# back to FALLBACK. SESSION_ID must already be parsed.
|
|
33
|
+
bridge_down() {
|
|
34
|
+
local reason="$1"
|
|
35
|
+
local sf="$STATE_DIR/auto-windows.json"
|
|
36
|
+
if [[ -n "${SESSION_ID:-}" && -r "$sf" ]] && command -v jq >/dev/null 2>&1; then
|
|
37
|
+
local active
|
|
38
|
+
active=$(jq -r --arg s "$SESSION_ID" \
|
|
39
|
+
'if (.windows[$s].until // 0) > (now * 1000) then "1" else "0" end' \
|
|
40
|
+
"$sf" 2>/dev/null) || active=""
|
|
41
|
+
if [[ "$active" == "1" ]]; then
|
|
42
|
+
emit "allow" "auto-window (offline): $reason"
|
|
43
|
+
fi
|
|
44
|
+
fi
|
|
45
|
+
case "$FALLBACK" in
|
|
46
|
+
allow|deny|ask) emit "$FALLBACK" "$reason" ;;
|
|
47
|
+
*) emit "ask" "$reason" ;;
|
|
48
|
+
esac
|
|
49
|
+
}
|
|
50
|
+
|
|
16
51
|
PAYLOAD=$(cat) || ask "stdin read failed"
|
|
17
52
|
command -v jq >/dev/null 2>&1 || ask "jq missing"
|
|
18
53
|
|
|
@@ -70,12 +105,12 @@ BODY=$(jq -nc \
|
|
|
70
105
|
RESP=$(curl -sS --max-time "$HOOK_TIMEOUT" \
|
|
71
106
|
-H 'Content-Type: application/json' \
|
|
72
107
|
-d "$BODY" \
|
|
73
|
-
"$DAEMON_URL" 2>/dev/null) ||
|
|
108
|
+
"$DAEMON_URL" 2>/dev/null) || bridge_down "daemon curl failed"
|
|
74
109
|
|
|
75
|
-
DECISION=$(printf '%s' "$RESP" | jq -r '.decision // "ask"' 2>/dev/null) ||
|
|
110
|
+
DECISION=$(printf '%s' "$RESP" | jq -r '.decision // "ask"' 2>/dev/null) || bridge_down "bad daemon response"
|
|
76
111
|
REASON=$(printf '%s' "$RESP" | jq -r '.reason // ""' 2>/dev/null)
|
|
77
112
|
|
|
78
113
|
case "$DECISION" in
|
|
79
114
|
allow|deny|ask) emit "$DECISION" "$REASON" ;;
|
|
80
|
-
*)
|
|
115
|
+
*) bridge_down "unknown decision: $DECISION" ;;
|
|
81
116
|
esac
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<plist version="1.0">
|
|
6
6
|
<dict>
|
|
7
7
|
<key>Label</key>
|
|
8
|
-
<string>com.
|
|
8
|
+
<string>com.weclaude.daemon</string>
|
|
9
9
|
|
|
10
10
|
<key>ProgramArguments</key>
|
|
11
11
|
<array>
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
<integer>10</integer>
|
|
39
39
|
|
|
40
40
|
<key>StandardOutPath</key>
|
|
41
|
-
<string>__HOME__/.
|
|
41
|
+
<string>__HOME__/.weclaude/daemon.stdout.log</string>
|
|
42
42
|
<key>StandardErrorPath</key>
|
|
43
|
-
<string>__HOME__/.
|
|
43
|
+
<string>__HOME__/.weclaude/daemon.stderr.log</string>
|
|
44
44
|
</dict>
|
|
45
45
|
</plist>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "weclaude",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Bridge Claude Code and WeCom: PreToolUse approval cards + bidirectional tmux session mirror with image paste",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
@@ -14,14 +14,13 @@
|
|
|
14
14
|
"mcp"
|
|
15
15
|
],
|
|
16
16
|
"bin": {
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"wrc-init": "./dist/cli/init.js"
|
|
17
|
+
"weclaude": "./cli/weclaude.sh",
|
|
18
|
+
"weclaude-daemon": "./dist/daemon/index.js",
|
|
19
|
+
"weclaude-mcp": "./dist/mcp/server.js",
|
|
20
|
+
"weclaude-init": "./dist/cli/init.js"
|
|
22
21
|
},
|
|
23
22
|
"files": [
|
|
24
|
-
"cli/
|
|
23
|
+
"cli/weclaude.sh",
|
|
25
24
|
"dist/cli/init.js",
|
|
26
25
|
"dist/cli/init.js.map",
|
|
27
26
|
"dist/cli/sync.js",
|
|
@@ -36,8 +35,8 @@
|
|
|
36
35
|
"hooks/hooks.json",
|
|
37
36
|
"scripts/install.sh",
|
|
38
37
|
"scripts/uninstall.sh",
|
|
39
|
-
"launchd/com.
|
|
40
|
-
"systemd/
|
|
38
|
+
"launchd/com.weclaude.daemon.plist.template",
|
|
39
|
+
"systemd/weclaude.service.template",
|
|
41
40
|
"config.example.jsonc",
|
|
42
41
|
"commands/wrc.md",
|
|
43
42
|
"README.md",
|
|
@@ -48,7 +47,7 @@
|
|
|
48
47
|
"scripts": {
|
|
49
48
|
"build": "tsc -p tsconfig.json && chmod +x dist/cli/init.js dist/daemon/index.js dist/mcp/server.js",
|
|
50
49
|
"dev:daemon": "tsx daemon/index.ts",
|
|
51
|
-
"dev:cli": "tsx cli/
|
|
50
|
+
"dev:cli": "tsx cli/weclaude.ts",
|
|
52
51
|
"typecheck": "tsc --noEmit",
|
|
53
52
|
"prepublishOnly": "rm -rf dist && npm run build",
|
|
54
53
|
"prepack": "test -f dist/daemon/index.js || npm run build"
|
package/scripts/install.sh
CHANGED
|
@@ -5,7 +5,7 @@ set -euo pipefail
|
|
|
5
5
|
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
|
6
6
|
NODE="$(command -v node)"
|
|
7
7
|
HOME_DIR="$HOME"
|
|
8
|
-
LABEL="com.
|
|
8
|
+
LABEL="com.weclaude.daemon"
|
|
9
9
|
|
|
10
10
|
[[ -x "$NODE" ]] || { echo "node not found in PATH"; exit 1; }
|
|
11
11
|
|
|
@@ -15,7 +15,7 @@ if [[ ! -f "$REPO/dist/daemon/index.js" ]]; then
|
|
|
15
15
|
(cd "$REPO" && npm install --silent && npx tsc -p tsconfig.json)
|
|
16
16
|
fi
|
|
17
17
|
|
|
18
|
-
mkdir -p "$HOME_DIR/.
|
|
18
|
+
mkdir -p "$HOME_DIR/.weclaude"
|
|
19
19
|
|
|
20
20
|
OS="$(uname -s)"
|
|
21
21
|
case "$OS" in
|
|
@@ -33,18 +33,18 @@ case "$OS" in
|
|
|
33
33
|
Linux)
|
|
34
34
|
UNIT_DIR="$HOME_DIR/.config/systemd/user"
|
|
35
35
|
mkdir -p "$UNIT_DIR"
|
|
36
|
-
UNIT_DST="$UNIT_DIR/
|
|
36
|
+
UNIT_DST="$UNIT_DIR/weclaude.service"
|
|
37
37
|
sed \
|
|
38
38
|
-e "s|__NODE__|$NODE|g" \
|
|
39
39
|
-e "s|__REPO__|$REPO|g" \
|
|
40
40
|
-e "s|__HOME__|$HOME_DIR|g" \
|
|
41
|
-
"$REPO/systemd/
|
|
41
|
+
"$REPO/systemd/weclaude.service.template" > "$UNIT_DST"
|
|
42
42
|
systemctl --user daemon-reload
|
|
43
|
-
systemctl --user enable --now
|
|
43
|
+
systemctl --user enable --now weclaude.service
|
|
44
44
|
echo "[install] systemd unit enabled: $UNIT_DST"
|
|
45
45
|
;;
|
|
46
46
|
*)
|
|
47
47
|
echo "unsupported OS: $OS"; exit 1 ;;
|
|
48
48
|
esac
|
|
49
49
|
|
|
50
|
-
echo "[install] done. Logs at $HOME_DIR/.
|
|
50
|
+
echo "[install] done. Logs at $HOME_DIR/.weclaude/daemon.{stdout,stderr,log}"
|
package/scripts/uninstall.sh
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
|
-
LABEL="com.
|
|
3
|
+
LABEL="com.weclaude.daemon"
|
|
4
4
|
HOME_DIR="$HOME"
|
|
5
5
|
OS="$(uname -s)"
|
|
6
6
|
|
|
@@ -14,9 +14,9 @@ case "$OS" in
|
|
|
14
14
|
fi
|
|
15
15
|
;;
|
|
16
16
|
Linux)
|
|
17
|
-
UNIT="$HOME_DIR/.config/systemd/user/
|
|
17
|
+
UNIT="$HOME_DIR/.config/systemd/user/weclaude.service"
|
|
18
18
|
if [[ -f "$UNIT" ]]; then
|
|
19
|
-
systemctl --user disable --now
|
|
19
|
+
systemctl --user disable --now weclaude.service 2>/dev/null || true
|
|
20
20
|
rm -f "$UNIT"
|
|
21
21
|
systemctl --user daemon-reload
|
|
22
22
|
echo "[uninstall] removed $UNIT"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[Unit]
|
|
2
|
-
Description=
|
|
2
|
+
Description=weclaude daemon (WeCom remote control for Claude Code)
|
|
3
3
|
After=network-online.target
|
|
4
4
|
Wants=network-online.target
|
|
5
5
|
|
|
@@ -9,8 +9,8 @@ WorkingDirectory=__REPO__
|
|
|
9
9
|
ExecStart=__NODE__ __REPO__/dist/daemon/index.js
|
|
10
10
|
Restart=always
|
|
11
11
|
RestartSec=5
|
|
12
|
-
StandardOutput=append:__HOME__/.
|
|
13
|
-
StandardError=append:__HOME__/.
|
|
12
|
+
StandardOutput=append:__HOME__/.weclaude/daemon.stdout.log
|
|
13
|
+
StandardError=append:__HOME__/.weclaude/daemon.stderr.log
|
|
14
14
|
Environment=NODE_ENV=production
|
|
15
15
|
|
|
16
16
|
[Install]
|