@rulemetric/hooks 0.5.2 → 0.5.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/package.json +1 -1
- package/scripts/_config.sh +1 -1
- package/scripts/_post.sh +10 -4
- package/scripts/post-retry.test.sh +16 -1
- package/scripts/session-end.sh +6 -0
package/package.json
CHANGED
package/scripts/_config.sh
CHANGED
|
@@ -75,7 +75,7 @@ _rulemetric_load_config() {
|
|
|
75
75
|
if [ -n "$token" ]; then
|
|
76
76
|
export RULEMETRIC_ACCESS_TOKEN="$token"
|
|
77
77
|
# Default API URL if not set
|
|
78
|
-
export RULEMETRIC_API_URL="${RULEMETRIC_API_URL:-https://
|
|
78
|
+
export RULEMETRIC_API_URL="${RULEMETRIC_API_URL:-https://rulemetric.com}"
|
|
79
79
|
_rulemetric_load_active_org
|
|
80
80
|
return 0
|
|
81
81
|
fi
|
package/scripts/_post.sh
CHANGED
|
@@ -39,10 +39,13 @@ _rulemetric_post_event() {
|
|
|
39
39
|
http_code=$(printf '%s' "$body" | tail -1)
|
|
40
40
|
body=$(printf '%s' "$body" | sed '$d')
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
# Any 2xx is success: the batched events route (Phase 2 admission control)
|
|
43
|
+
# returns 202 "buffered", the legacy path 200, session create 201.
|
|
44
|
+
case "$http_code" in 2??)
|
|
43
45
|
[ -n "$hdr_file" ] && rm -f "$hdr_file"
|
|
44
46
|
return 0
|
|
45
|
-
|
|
47
|
+
;;
|
|
48
|
+
esac
|
|
46
49
|
|
|
47
50
|
# Transient (service busy / rate-limited / network) → retry unless this was
|
|
48
51
|
# the last allowed attempt. Everything else (4xx) is permanent.
|
|
@@ -103,10 +106,13 @@ _rulemetric_send() {
|
|
|
103
106
|
http_code=$(printf '%s' "$body" | tail -1)
|
|
104
107
|
body=$(printf '%s' "$body" | sed '$d')
|
|
105
108
|
|
|
106
|
-
|
|
109
|
+
# Any 2xx is success: the batched events route (Phase 2 admission control)
|
|
110
|
+
# returns 202 "buffered", the legacy path 200, session create 201.
|
|
111
|
+
case "$http_code" in 2??)
|
|
107
112
|
[ -n "$hdr_file" ] && rm -f "$hdr_file"
|
|
108
113
|
return 0
|
|
109
|
-
|
|
114
|
+
;;
|
|
115
|
+
esac
|
|
110
116
|
|
|
111
117
|
if [ "$http_code" = "503" ] || [ "$http_code" = "429" ] || [ "$http_code" = "000" ]; then
|
|
112
118
|
if [ "$attempt" -lt "$max_attempts" ]; then
|
|
@@ -32,7 +32,7 @@ trap cleanup EXIT
|
|
|
32
32
|
# --- Stub server: counts requests, programmed by $MODE in a control file --------
|
|
33
33
|
cat > "$WORK_ROOT/stub.py" <<'PY'
|
|
34
34
|
import http.server, json, os, sys, threading
|
|
35
|
-
mode = sys.argv[1] # fail-once | always-503 | always-400
|
|
35
|
+
mode = sys.argv[1] # fail-once | always-503 | always-400 | always-202
|
|
36
36
|
count_file = sys.argv[2]
|
|
37
37
|
n = 0
|
|
38
38
|
lock = threading.Lock()
|
|
@@ -50,6 +50,9 @@ class H(http.server.BaseHTTPRequestHandler):
|
|
|
50
50
|
if mode == "always-503":
|
|
51
51
|
self.send_response(503); self.send_header("Retry-After", "1"); self.end_headers()
|
|
52
52
|
self.wfile.write(b'{"error":"busy"}'); return
|
|
53
|
+
if mode == "always-202":
|
|
54
|
+
self.send_response(202); self.end_headers()
|
|
55
|
+
self.wfile.write(b'{"buffered":true,"count":1,"durable":false}'); return
|
|
53
56
|
# fail-once: 503 on first request, 200 thereafter
|
|
54
57
|
if cur == 1:
|
|
55
58
|
self.send_response(503); self.send_header("Retry-After", "1"); self.end_headers()
|
|
@@ -112,6 +115,18 @@ assert_eq "always-503: server saw exactly MAX_ATTEMPTS (3) requests" "3" "$(req_
|
|
|
112
115
|
grep -q "http_503" "$LOG_FILE" && ok "always-503: logged an http_503 error" || fail "always-503: expected http_503 in log"
|
|
113
116
|
stop_server
|
|
114
117
|
|
|
118
|
+
# --- Test 4: batched-ingest 202 is SUCCESS (no retry, no error log) -----------
|
|
119
|
+
# The Phase 2 batched events route returns 202 { buffered: true } instead of
|
|
120
|
+
# 200 { recorded: true }; the helper must treat ANY 2xx as delivered.
|
|
121
|
+
start_server always-202
|
|
122
|
+
: > "$LOG_FILE"
|
|
123
|
+
_rulemetric_post_event "http://127.0.0.1:$SERVER_PORT/events" "$AUTH" "$PAYLOAD" "test" 4
|
|
124
|
+
rc=$?
|
|
125
|
+
assert_eq "always-202: returns success (0) — buffered counts as delivered" "0" "$rc"
|
|
126
|
+
assert_eq "always-202: server saw exactly 1 request (no retry)" "1" "$(req_count)"
|
|
127
|
+
assert_eq "always-202: no error logged" "0" "$(wc -l < "$LOG_FILE" | tr -d ' ')"
|
|
128
|
+
stop_server
|
|
129
|
+
|
|
115
130
|
# --- Summary -----------------------------------------------------------------
|
|
116
131
|
echo
|
|
117
132
|
echo "Passed: $PASSES Failed: $FAILURES"
|
package/scripts/session-end.sh
CHANGED
|
@@ -51,6 +51,12 @@ if [ -n "$HOOK_TRANSCRIPT_PATH" ] && [ -f "$HOOK_TRANSCRIPT_PATH" ]; then
|
|
|
51
51
|
# blocks the hook, so cap at 2 attempts (worst-case tail ~18s on a hard API hang,
|
|
52
52
|
# vs ~32s at 3) — the common fast-503 wedge still recovers in <1s. Fall back to
|
|
53
53
|
# close if it still fails after retries — a dropped reimport leaves the session EMPTY.
|
|
54
|
+
# NOTE: under headless `claude -p`, Claude Code's print-mode teardown cancels this
|
|
55
|
+
# hook ("SessionEnd hook … failed: Hook cancelled") regardless of whether this runs
|
|
56
|
+
# in the foreground or is backgrounded — verified 2026-07-05. It is a Claude Code
|
|
57
|
+
# print-mode behavior, not a capture failure (per-turn hooks still land events); do
|
|
58
|
+
# NOT try to "fix" it by detaching this call (adds no benefit, and headless is a
|
|
59
|
+
# deliberately-excluded capture vehicle).
|
|
54
60
|
_rulemetric_send POST "${RULEMETRIC_API_URL}/api/sessions/${RW_SESSION_ID}/reimport" \
|
|
55
61
|
"$AUTH_HEADER" "$REIMPORT_PAYLOAD" "session-end-reimport" 8 2 \
|
|
56
62
|
> /dev/null 2>&1 || {
|