@synkro-sh/cli 1.2.3 → 1.2.5
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 +54 -58
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -135,7 +135,8 @@ function installCCHooks(settingsPath, config) {
|
|
|
135
135
|
hooks: [
|
|
136
136
|
{
|
|
137
137
|
type: "command",
|
|
138
|
-
command: config.editCaptureScriptPath
|
|
138
|
+
command: config.editCaptureScriptPath,
|
|
139
|
+
timeout: 20
|
|
139
140
|
}
|
|
140
141
|
],
|
|
141
142
|
[SYNKRO_MARKER]: true
|
|
@@ -299,7 +300,7 @@ var init_hookScripts = __esm({
|
|
|
299
300
|
# Synkro PreToolUse Bash judge hook
|
|
300
301
|
# Reads CC's hook payload from stdin, judges via Synkro gateway, returns verdict.
|
|
301
302
|
# Auth: reads access_token from ~/.synkro/credentials.json, sends Authorization: Bearer.
|
|
302
|
-
set -e
|
|
303
|
+
# No set -e: hook must ALWAYS produce JSON output. Silent death = CC timeout.
|
|
303
304
|
|
|
304
305
|
synkro_log() { echo "[synkro] $1" >&2; }
|
|
305
306
|
|
|
@@ -563,24 +564,20 @@ CATEGORY=$(echo "$VERDICT" | jq -r '.category // "destructive_command"' 2>/dev/n
|
|
|
563
564
|
# need to interrupt the user \u2014 surfacing them creates alert fatigue and
|
|
564
565
|
# trains the user to click-through on warnings that turn out to be benign.
|
|
565
566
|
|
|
567
|
+
ALT_SUFFIX=""
|
|
568
|
+
if [ -n "$ALTERNATIVE" ] && [ "$ALTERNATIVE" != "null" ]; then
|
|
569
|
+
ALT_SUFFIX=" Suggested: \${ALTERNATIVE}"
|
|
570
|
+
fi
|
|
571
|
+
|
|
566
572
|
case "$SEVERITY" in
|
|
567
573
|
critical)
|
|
568
|
-
synkro_log "bashGuard $CMD_SHORT \u2192 BLOCKED ($CATEGORY)
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
SYS_MSG="\${SYS_MSG}\\n[synkro] suggested \u2192 \${ALTERNATIVE}"
|
|
572
|
-
fi
|
|
573
|
-
ADDITIONAL_CTX="Synkro safety judge flagged this command (severity: critical, category: \${CATEGORY}). Reasoning: \${REASONING}."
|
|
574
|
-
if [ -n "$ALTERNATIVE" ] && [ "$ALTERNATIVE" != "null" ]; then
|
|
575
|
-
ADDITIONAL_CTX="\${ADDITIONAL_CTX} Suggested alternative: \${ALTERNATIVE}."
|
|
576
|
-
fi
|
|
577
|
-
PERMISSION_REASON="[synkro] BLOCKED \u2014 \${REASONING}. Severity: critical. Override only if you are certain."
|
|
574
|
+
synkro_log "bashGuard $CMD_SHORT \u2192 BLOCKED ($CATEGORY)"
|
|
575
|
+
PERMISSION_REASON="[synkro] BLOCKED \u2014 \${REASONING}\${ALT_SUFFIX}"
|
|
576
|
+
ADDITIONAL_CTX="Synkro safety judge (severity: critical, category: \${CATEGORY}).\${ALT_SUFFIX}"
|
|
578
577
|
jq -n \\
|
|
579
|
-
--arg sys_msg "$SYS_MSG" \\
|
|
580
578
|
--arg ctx "$ADDITIONAL_CTX" \\
|
|
581
579
|
--arg reason "$PERMISSION_REASON" \\
|
|
582
580
|
'{
|
|
583
|
-
systemMessage: $sys_msg,
|
|
584
581
|
hookSpecificOutput: {
|
|
585
582
|
hookEventName: "PreToolUse",
|
|
586
583
|
permissionDecision: "deny",
|
|
@@ -590,28 +587,15 @@ case "$SEVERITY" in
|
|
|
590
587
|
}'
|
|
591
588
|
;;
|
|
592
589
|
high)
|
|
593
|
-
synkro_log "bashGuard $CMD_SHORT \u2192 FLAGGED ($CATEGORY)
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
SYS_MSG="\${SYS_MSG}\\n[synkro] suggested \u2192 \${ALTERNATIVE}"
|
|
597
|
-
fi
|
|
598
|
-
ADDITIONAL_CTX="Synkro safety judge flagged this command (severity: high, category: \${CATEGORY}). Reasoning: \${REASONING}."
|
|
599
|
-
if [ -n "$ALTERNATIVE" ] && [ "$ALTERNATIVE" != "null" ]; then
|
|
600
|
-
ADDITIONAL_CTX="\${ADDITIONAL_CTX} Suggested alternative: \${ALTERNATIVE}."
|
|
601
|
-
fi
|
|
602
|
-
PERMISSION_REASON="[synkro] high: \${REASONING}"
|
|
603
|
-
if [ -n "$ALTERNATIVE" ] && [ "$ALTERNATIVE" != "null" ]; then
|
|
604
|
-
PERMISSION_REASON="\${PERMISSION_REASON} Alternative: \${ALTERNATIVE}"
|
|
605
|
-
fi
|
|
606
|
-
# Headless? Upgrade ask \u2192 deny so we fail-closed.
|
|
590
|
+
synkro_log "bashGuard $CMD_SHORT \u2192 FLAGGED ($CATEGORY)"
|
|
591
|
+
PERMISSION_REASON="[synkro] \${REASONING}\${ALT_SUFFIX}"
|
|
592
|
+
ADDITIONAL_CTX="Synkro safety judge (severity: high, category: \${CATEGORY}).\${ALT_SUFFIX}"
|
|
607
593
|
if [ "$IS_HEADLESS" = "1" ]; then DECISION="deny"; else DECISION="ask"; fi
|
|
608
594
|
jq -n \\
|
|
609
|
-
--arg sys_msg "$SYS_MSG" \\
|
|
610
595
|
--arg ctx "$ADDITIONAL_CTX" \\
|
|
611
596
|
--arg reason "$PERMISSION_REASON" \\
|
|
612
597
|
--arg decision "$DECISION" \\
|
|
613
598
|
'{
|
|
614
|
-
systemMessage: $sys_msg,
|
|
615
599
|
hookSpecificOutput: {
|
|
616
600
|
hookEventName: "PreToolUse",
|
|
617
601
|
permissionDecision: $decision,
|
|
@@ -621,9 +605,15 @@ case "$SEVERITY" in
|
|
|
621
605
|
}'
|
|
622
606
|
;;
|
|
623
607
|
*)
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
608
|
+
synkro_log "bashGuard $CMD_SHORT \u2192 pass ($CATEGORY): $REASONING"
|
|
609
|
+
case "$CATEGORY" in
|
|
610
|
+
trivial_utility)
|
|
611
|
+
jq -n --arg m "[synkro] bashGuard \u2192 pass" '{systemMessage: $m}' ;;
|
|
612
|
+
judge_unavailable|judge_error|parse_error)
|
|
613
|
+
jq -n --arg m "[synkro] bashGuard \u2192 pass (grader unavailable)" '{systemMessage: $m}' ;;
|
|
614
|
+
*)
|
|
615
|
+
jq -n --arg m "[synkro] bashGuard \u2192 pass: \${REASONING}" '{systemMessage: $m}' ;;
|
|
616
|
+
esac
|
|
627
617
|
;;
|
|
628
618
|
esac
|
|
629
619
|
|
|
@@ -642,7 +632,7 @@ exit 0
|
|
|
642
632
|
# Synkro's COGS = ~$0.00001 per edit (one Gemini embedding call).
|
|
643
633
|
#
|
|
644
634
|
# Always exits 0 with valid JSON. Fails open on any error.
|
|
645
|
-
set -e
|
|
635
|
+
# No set -e: hook must ALWAYS produce JSON output. Silent death = CC timeout.
|
|
646
636
|
|
|
647
637
|
synkro_log() { echo "[synkro] $1" >&2; }
|
|
648
638
|
|
|
@@ -1014,7 +1004,7 @@ exit 0
|
|
|
1014
1004
|
# edit lands (cross-file shapes, real disk state, post-edit semantic effects).
|
|
1015
1005
|
#
|
|
1016
1006
|
# Always exits 0 with valid JSON \u2014 never breaks CC's flow.
|
|
1017
|
-
set -e
|
|
1007
|
+
# No set -e: hook must ALWAYS produce JSON output. Silent death = CC timeout.
|
|
1018
1008
|
|
|
1019
1009
|
synkro_log() { echo "[synkro] $1" >&2; }
|
|
1020
1010
|
|
|
@@ -1033,7 +1023,7 @@ if [ ! -f "$CREDS_PATH" ]; then
|
|
|
1033
1023
|
echo '{}'
|
|
1034
1024
|
exit 0
|
|
1035
1025
|
fi
|
|
1036
|
-
JWT=$(jq -r '.access_token // empty' "$CREDS_PATH" 2>/dev/null)
|
|
1026
|
+
JWT=$(jq -r '.access_token // empty' "$CREDS_PATH" 2>/dev/null || true)
|
|
1037
1027
|
if [ -z "$JWT" ]; then
|
|
1038
1028
|
echo '{}'
|
|
1039
1029
|
exit 0
|
|
@@ -1080,7 +1070,7 @@ if [ -z "$FILE_CONTENT" ]; then
|
|
|
1080
1070
|
exit 0
|
|
1081
1071
|
fi
|
|
1082
1072
|
|
|
1083
|
-
DIFF_FIELD=$(echo "$TOOL_INPUT" | jq -c '{old_string, new_string, edits} | with_entries(select(.value != null))' 2>/dev/null)
|
|
1073
|
+
DIFF_FIELD=$(echo "$TOOL_INPUT" | jq -c '{old_string, new_string, edits} | with_entries(select(.value != null))' 2>/dev/null || echo "null")
|
|
1084
1074
|
if [ -z "$DIFF_FIELD" ] || [ "$DIFF_FIELD" = "null" ] || [ "$DIFF_FIELD" = "{}" ]; then
|
|
1085
1075
|
DIFF_FIELD="null"
|
|
1086
1076
|
fi
|
|
@@ -1101,7 +1091,12 @@ BODY=$(jq -n \\
|
|
|
1101
1091
|
tool_use_id: (if ($tool_use_id | length) > 0 then $tool_use_id else null end),
|
|
1102
1092
|
cwd: (if ($cwd | length) > 0 then $cwd else null end),
|
|
1103
1093
|
repo: (if ($repo | length) > 0 then $repo else null end)
|
|
1104
|
-
}')
|
|
1094
|
+
}' 2>/dev/null || true)
|
|
1095
|
+
|
|
1096
|
+
if [ -z "$BODY" ] || ! echo "$BODY" | jq -e 'type == "object"' >/dev/null 2>&1; then
|
|
1097
|
+
jq -n --arg m "[synkro] editScan $BASENAME \u2192 error (body construction failed)" '{systemMessage: $m}'
|
|
1098
|
+
exit 0
|
|
1099
|
+
fi
|
|
1105
1100
|
|
|
1106
1101
|
refresh_jwt() {
|
|
1107
1102
|
local refresh_token
|
|
@@ -1128,31 +1123,32 @@ refresh_jwt() {
|
|
|
1128
1123
|
return 0
|
|
1129
1124
|
}
|
|
1130
1125
|
|
|
1131
|
-
# Fire-and-forget correction-followup
|
|
1132
|
-
# flip any pending precheck-correction for this tool_use_id to 'allow'.
|
|
1126
|
+
# Fire-and-forget correction-followup (backgrounded \u2014 must not block grading).
|
|
1133
1127
|
if [ -n "$SESSION_ID" ] && [ -n "$TOOL_USE_ID" ]; then
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1128
|
+
(
|
|
1129
|
+
FOLLOWUP_BODY=$(jq -n \\
|
|
1130
|
+
--arg sid "$SESSION_ID" \\
|
|
1131
|
+
--arg tid "$TOOL_USE_ID" \\
|
|
1132
|
+
'{session_id: $sid, tool_use_id: $tid, decision: "allow"}')
|
|
1133
|
+
curl -sS -X POST "\${GATEWAY_URL}/api/v1/precheck-edit/correction-followup" \\
|
|
1134
|
+
-H "Content-Type: application/json" \\
|
|
1135
|
+
-H "Authorization: Bearer $JWT" \\
|
|
1136
|
+
-d "$FOLLOWUP_BODY" \\
|
|
1137
|
+
--max-time 2 \\
|
|
1138
|
+
>/dev/null 2>&1
|
|
1139
|
+
) &
|
|
1144
1140
|
fi
|
|
1145
1141
|
|
|
1146
1142
|
# Resolve tier (cached 60 min) \u2014 same logic as the other hooks.
|
|
1147
1143
|
TIER_CACHE_FILE="$HOME/.synkro/.tier-cache-\${SYNKRO_USER_ID:-default}"
|
|
1148
1144
|
SYNKRO_INFERENCE_TIER=""
|
|
1149
1145
|
if find "$TIER_CACHE_FILE" -mmin -60 2>/dev/null | grep -q .; then
|
|
1150
|
-
SYNKRO_INFERENCE_TIER=$(cat "$TIER_CACHE_FILE" 2>/dev/null)
|
|
1146
|
+
SYNKRO_INFERENCE_TIER=$(cat "$TIER_CACHE_FILE" 2>/dev/null || true)
|
|
1151
1147
|
fi
|
|
1152
1148
|
if [ -z "$SYNKRO_INFERENCE_TIER" ]; then
|
|
1153
1149
|
ME_RESP=$(curl -sS "\${GATEWAY_URL}/api/v1/cli/me" -H "Authorization: Bearer $JWT" --max-time 2 2>/dev/null || echo "")
|
|
1154
1150
|
if [ -n "$ME_RESP" ]; then
|
|
1155
|
-
SYNKRO_INFERENCE_TIER=$(echo "$ME_RESP" | jq -r '.tier // empty' 2>/dev/null)
|
|
1151
|
+
SYNKRO_INFERENCE_TIER=$(echo "$ME_RESP" | jq -r '.tier // empty' 2>/dev/null || true)
|
|
1156
1152
|
[ -n "$SYNKRO_INFERENCE_TIER" ] && printf '%s' "$SYNKRO_INFERENCE_TIER" > "$TIER_CACHE_FILE" 2>/dev/null || true
|
|
1157
1153
|
fi
|
|
1158
1154
|
fi
|
|
@@ -1195,7 +1191,7 @@ else
|
|
|
1195
1191
|
-H "Content-Type: application/json" \\
|
|
1196
1192
|
-H "Authorization: Bearer $JWT" \\
|
|
1197
1193
|
-d "$BODY" \\
|
|
1198
|
-
--max-time
|
|
1194
|
+
--max-time 12 2>/dev/null || echo "")
|
|
1199
1195
|
|
|
1200
1196
|
if echo "$RESP" | grep -qE '"detail":"Token has expired|"detail":"Invalid or expired token'; then
|
|
1201
1197
|
if refresh_jwt; then
|
|
@@ -1203,7 +1199,7 @@ else
|
|
|
1203
1199
|
-H "Content-Type: application/json" \\
|
|
1204
1200
|
-H "Authorization: Bearer $JWT" \\
|
|
1205
1201
|
-d "$BODY" \\
|
|
1206
|
-
--max-time
|
|
1202
|
+
--max-time 12 2>/dev/null || echo "")
|
|
1207
1203
|
fi
|
|
1208
1204
|
fi
|
|
1209
1205
|
fi
|
|
@@ -1244,7 +1240,7 @@ exit 0
|
|
|
1244
1240
|
# Synkro Stop hook \u2014 emits "[synkro] stop \u2192 N findings: X auto-fixed, Y open"
|
|
1245
1241
|
# as a final summary line when the CC session ends. Reads guard_checks rows
|
|
1246
1242
|
# for the session via /api/v1/cli/session-summary.
|
|
1247
|
-
set -e
|
|
1243
|
+
# No set -e: hook must ALWAYS produce JSON output. Silent death = CC timeout.
|
|
1248
1244
|
|
|
1249
1245
|
CONFIG_FILE="$HOME/.synkro/config.env"
|
|
1250
1246
|
if [ -f "$CONFIG_FILE" ]; then
|
|
@@ -1309,7 +1305,7 @@ exit 0
|
|
|
1309
1305
|
# Synkro SessionStart hook \u2014 when the user opens a fresh CC session in a repo
|
|
1310
1306
|
# we have findings on, surface "[synkro] session start \u2192 N open finding(s) in
|
|
1311
1307
|
# this repo" so they have context. Silent when there's nothing to report.
|
|
1312
|
-
set -e
|
|
1308
|
+
# No set -e: hook must ALWAYS produce JSON output. Silent death = CC timeout.
|
|
1313
1309
|
|
|
1314
1310
|
CONFIG_FILE="$HOME/.synkro/config.env"
|
|
1315
1311
|
if [ -f "$CONFIG_FILE" ]; then
|
|
@@ -1370,7 +1366,7 @@ exit 0
|
|
|
1370
1366
|
# Synkro PostToolUse Bash hook \u2014 minimal correction-followup fire.
|
|
1371
1367
|
# No grading happens here; verdict already came from PreToolUse. This is just
|
|
1372
1368
|
# the "user approved + agent ran it" capture.
|
|
1373
|
-
set -e
|
|
1369
|
+
# No set -e: hook must ALWAYS produce JSON output. Silent death = CC timeout.
|
|
1374
1370
|
|
|
1375
1371
|
CONFIG_FILE="$HOME/.synkro/config.env"
|
|
1376
1372
|
if [ -f "$CONFIG_FILE" ]; then
|
|
@@ -2225,7 +2221,7 @@ function writeConfigEnv(opts) {
|
|
|
2225
2221
|
`SYNKRO_GATEWAY_URL=${shellQuoteSingle(safeGateway)}`,
|
|
2226
2222
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
2227
2223
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
2228
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.2.
|
|
2224
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.2.5")}`
|
|
2229
2225
|
];
|
|
2230
2226
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
2231
2227
|
if (safeOrgId) lines.push(`SYNKRO_ORG_ID=${shellQuoteSingle(safeOrgId)}`);
|