@synkro-sh/cli 1.4.17 → 1.4.19

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/dist/bootstrap.js CHANGED
@@ -547,6 +547,7 @@ synkro_parse_local_verdict() {
547
547
  ok_tag=$(printf '%s' "$inner" | sed -nE 's|.*<ok>(.*)</ok>.*|\\1|p' | head -1)
548
548
  [ -n "$ok_tag" ] && LOCAL_OK="$ok_tag"
549
549
  LOCAL_REASON=$(printf '%s' "$inner" | sed -nE 's|.*<reason>(.*)</reason>.*|\\1|p' | head -1)
550
+ [ -z "$LOCAL_REASON" ] && LOCAL_REASON=$(printf '%s' "$inner" | sed -nE 's|.*<reasoning>(.*)</reasoning>.*|\\1|p' | head -1)
550
551
  if [ "$LOCAL_OK" = "false" ]; then
551
552
  local fv
552
553
  fv=$(printf '%s' "$inner" | awk -v RS='</violation>' '/<violation>/{print; exit}')
@@ -575,6 +576,16 @@ synkro_capture_local() {
575
576
  ) &
576
577
  }
577
578
 
579
+ # Look up a rule's mode from cached rules. Returns "blocking" or "audit".
580
+ synkro_rule_mode() {
581
+ local rid="$1"
582
+ [ -z "$rid" ] || [ -z "\${SYNKRO_RULES:-}" ] && echo "blocking" && return
583
+ local m
584
+ m=$(printf '%s' "$SYNKRO_RULES" | jq -r --arg r "$rid" '[.[] | select(.rule_id == $r) | .mode] | .[0] // "blocking"' 2>/dev/null)
585
+ [ -z "$m" ] || [ "$m" = "null" ] && m="blocking"
586
+ echo "$m"
587
+ }
588
+
578
589
  synkro_post_with_retry() {
579
590
  local url="$1" body="$2" timeout="\${3:-8}"
580
591
  local resp
@@ -650,14 +661,21 @@ if [ "$ROUTE" = "local" ]; then
650
661
  synkro_parse_local_verdict "$CC_RESP"
651
662
 
652
663
  if [ "$LOCAL_OK" = "false" ]; then
653
- if [ "$IS_HEADLESS" = "1" ]; then DEC="deny"; else DEC="ask"; fi
654
- REASON="[synkro:local] bashGuard \u2192 block\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
655
- jq -n --arg dec "$DEC" --arg reason "$REASON" --arg ctx "$REASON" \\
656
- '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:$dec,permissionDecisionReason:$reason,additionalContext:$ctx}}'
657
- synkro_capture_local "bash" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
664
+ RULE_MODE=$(synkro_rule_mode "\${LOCAL_RULE_ID}")
665
+ if [ "$RULE_MODE" = "audit" ]; then
666
+ REASON="[synkro:local] bashGuard \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
667
+ jq -n --arg m "$REASON" '{systemMessage: $m}'
668
+ synkro_capture_local "bash" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
669
+ else
670
+ if [ "$IS_HEADLESS" = "1" ]; then DEC="deny"; else DEC="ask"; fi
671
+ REASON="[synkro:local] bashGuard \u2192 block\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
672
+ jq -n --arg dec "$DEC" --arg reason "$REASON" --arg ctx "$REASON" \\
673
+ '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:$dec,permissionDecisionReason:$reason,additionalContext:$ctx}}'
674
+ synkro_capture_local "bash" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
675
+ fi
658
676
  else
659
677
  jq -n --arg m "[synkro:local] bashGuard \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
660
- synkro_capture_local "bash" "allow" "audit" "\${LOCAL_CAT:-trivial_utility}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
678
+ synkro_capture_local "bash" "pass" "audit" "\${LOCAL_CAT:-trivial_utility}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
661
679
  fi
662
680
  exit 0
663
681
  fi
@@ -822,11 +840,18 @@ if [ "$ROUTE" = "local" ]; then
822
840
  synkro_parse_local_verdict "$CC_RESP"
823
841
 
824
842
  if [ "$LOCAL_OK" = "false" ]; then
825
- if [ "$IS_HEADLESS" = "1" ]; then DEC="deny"; else DEC="ask"; fi
826
- REASON="[synkro:local] editGuard $FILE_SHORT \u2192 block\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
827
- jq -n --arg dec "$DEC" --arg reason "$REASON" --arg ctx "$REASON" \\
828
- '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:$dec,permissionDecisionReason:$reason,additionalContext:$ctx}}'
829
- synkro_capture_local "edit" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
843
+ RULE_MODE=$(synkro_rule_mode "\${LOCAL_RULE_ID}")
844
+ if [ "$RULE_MODE" = "audit" ]; then
845
+ REASON="[synkro:local] editGuard $FILE_SHORT \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
846
+ jq -n --arg m "$REASON" '{systemMessage: $m}'
847
+ synkro_capture_local "edit" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
848
+ else
849
+ if [ "$IS_HEADLESS" = "1" ]; then DEC="deny"; else DEC="ask"; fi
850
+ REASON="[synkro:local] editGuard $FILE_SHORT \u2192 block\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
851
+ jq -n --arg dec "$DEC" --arg reason "$REASON" --arg ctx "$REASON" \\
852
+ '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:$dec,permissionDecisionReason:$reason,additionalContext:$ctx}}'
853
+ synkro_capture_local "edit" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
854
+ fi
830
855
  else
831
856
  jq -n --arg m "[synkro:local] editGuard $FILE_SHORT \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
832
857
  synkro_capture_local "edit" "pass" "audit" "\${LOCAL_CAT:-trivial_edit}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
@@ -964,9 +989,16 @@ if [ "$ROUTE" = "local" ]; then
964
989
  synkro_parse_local_verdict "$CC_RESP"
965
990
 
966
991
  if [ "$LOCAL_OK" = "false" ]; then
967
- REASON="[synkro:local] editScan $BASENAME \u2192 block: \${LOCAL_REASON:-policy violation}"
968
- jq -n --arg m "$REASON" '{systemMessage: $m, additionalContext: $m}'
969
- synkro_capture_local "edit_scan" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
992
+ RULE_MODE=$(synkro_rule_mode "\${LOCAL_RULE_ID}")
993
+ if [ "$RULE_MODE" = "audit" ]; then
994
+ REASON="[synkro:local] editScan $BASENAME \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
995
+ jq -n --arg m "$REASON" '{systemMessage: $m}'
996
+ synkro_capture_local "edit_scan" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
997
+ else
998
+ REASON="[synkro:local] editScan $BASENAME \u2192 block: \${LOCAL_REASON:-policy violation}"
999
+ jq -n --arg m "$REASON" '{systemMessage: $m, additionalContext: $m}'
1000
+ synkro_capture_local "edit_scan" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
1001
+ fi
970
1002
  else
971
1003
  jq -n --arg m "[synkro:local] editScan $BASENAME \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
972
1004
  synkro_capture_local "edit_scan" "pass" "audit" "\${LOCAL_CAT:-trivial_edit}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
@@ -1145,8 +1177,17 @@ SESSION_ID=$(echo "$PAYLOAD" | jq -r '.session_id // empty' 2>/dev/null)
1145
1177
  TOOL_USE_ID=$(echo "$PAYLOAD" | jq -r '.tool_use_id // empty' 2>/dev/null)
1146
1178
  if [ -z "$SESSION_ID" ] || [ -z "$TOOL_USE_ID" ]; then echo '{}'; exit 0; fi
1147
1179
 
1180
+ # Extract is_error from tool_result and compute command hash for consent tracking
1181
+ IS_ERROR=$(echo "$PAYLOAD" | jq -r '.tool_result.is_error // false' 2>/dev/null)
1182
+ CMD=$(echo "$PAYLOAD" | jq -r '.tool_input.command // empty' 2>/dev/null)
1183
+ CMD_HASH=""
1184
+ if [ -n "$CMD" ]; then
1185
+ CMD_HASH=$(printf '%s' "$CMD" | shasum -a 256 | cut -c1-16)
1186
+ fi
1187
+
1148
1188
  BODY=$(jq -n --arg sid "$SESSION_ID" --arg tid "$TOOL_USE_ID" \\
1149
- '{capture_type:"bash_followup",session_id:$sid,tool_use_id:$tid}')
1189
+ --argjson err "$IS_ERROR" --arg ch "$CMD_HASH" \\
1190
+ '{capture_type:"bash_followup",session_id:$sid,tool_use_id:$tid,is_error:$err,command_hash:$ch}')
1150
1191
 
1151
1192
  curl -sS -X POST "\${GATEWAY_URL}/api/v1/hook/capture" \\
1152
1193
  -H "Content-Type: application/json" -H "Authorization: Bearer $JWT" \\
@@ -1398,10 +1439,18 @@ case "$TOOL_NAME" in Shell|Bash|terminal|run_terminal_cmd|execute_command) ;; *)
1398
1439
  SESSION_ID=$(echo "$PAYLOAD" | jq -r '.conversation_id // empty' 2>/dev/null)
1399
1440
  TOOL_USE_ID=$(echo "$PAYLOAD" | jq -r '.tool_use_id // empty' 2>/dev/null)
1400
1441
 
1442
+ IS_ERROR=$(echo "$PAYLOAD" | jq -r '.tool_result.is_error // false' 2>/dev/null)
1443
+ CMD=$(echo "$PAYLOAD" | jq -r '.tool_input.command // empty' 2>/dev/null)
1444
+ CMD_HASH=""
1445
+ if [ -n "$CMD" ]; then
1446
+ CMD_HASH=$(printf '%s' "$CMD" | shasum -a 256 | cut -c1-16)
1447
+ fi
1448
+
1401
1449
  if [ -n "$SESSION_ID" ] && [ -n "$TOOL_USE_ID" ]; then
1402
1450
  (
1403
1451
  BODY=$(jq -n --arg sid "$SESSION_ID" --arg tid "$TOOL_USE_ID" \\
1404
- '{capture_type:"bash_followup",session_id:$sid,tool_use_id:$tid}')
1452
+ --argjson err "$IS_ERROR" --arg ch "$CMD_HASH" \\
1453
+ '{capture_type:"bash_followup",session_id:$sid,tool_use_id:$tid,is_error:$err,command_hash:$ch}')
1405
1454
  curl -sS -X POST "\${GATEWAY_URL}/api/v1/hook/capture" \\
1406
1455
  -H "Content-Type: application/json" -H "Authorization: Bearer $JWT" \\
1407
1456
  -d "$BODY" --max-time 3 >/dev/null 2>&1 || true
@@ -3601,7 +3650,7 @@ function writeConfigEnv(opts) {
3601
3650
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
3602
3651
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
3603
3652
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
3604
- `SYNKRO_VERSION=${shellQuoteSingle("1.4.17")}`
3653
+ `SYNKRO_VERSION=${shellQuoteSingle("1.4.19")}`
3605
3654
  ];
3606
3655
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
3607
3656
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);