@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulemetric/hooks",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -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://api.rulemetric.com}"
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
- if [ "$http_code" = "200" ] || [ "$http_code" = "201" ]; then
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
- fi
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
- if [ "$http_code" = "200" ] || [ "$http_code" = "201" ]; then
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
- fi
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"
@@ -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 || {