@synkro-sh/cli 1.4.15 → 1.4.17
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 +170 -25
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -495,6 +495,86 @@ synkro_detect_repo() {
|
|
|
495
495
|
echo ""
|
|
496
496
|
}
|
|
497
497
|
|
|
498
|
+
synkro_channel_up() {
|
|
499
|
+
(exec 3<>/dev/tcp/127.0.0.1/\${SYNKRO_CHANNEL_PORT:-8929}) 2>/dev/null && exec 3<&- 3>&-
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
# Fetch hook config (cached 5min). Sets SYNKRO_CAPTURE_DEPTH, SYNKRO_TIER, SYNKRO_RULES, SYNKRO_GRADER_PRIMER_BASH, SYNKRO_GRADER_PRIMER_EDIT, SYNKRO_CLASSIFICATION_PROMPT.
|
|
503
|
+
synkro_load_config() {
|
|
504
|
+
local cache="$HOME/.synkro/.hook-config-cache"
|
|
505
|
+
if [ -f "$cache" ] && find "$cache" -mmin -5 2>/dev/null | grep -q .; then
|
|
506
|
+
. "$cache" 2>/dev/null
|
|
507
|
+
return
|
|
508
|
+
fi
|
|
509
|
+
local resp
|
|
510
|
+
resp=$(curl -sS "\${GATEWAY_URL}/api/v1/hook/config\${1:+?$1}" -H "Authorization: Bearer $JWT" --max-time 4 2>/dev/null || echo "")
|
|
511
|
+
if [ -z "$resp" ]; then return; fi
|
|
512
|
+
SYNKRO_CAPTURE_DEPTH=$(echo "$resp" | jq -r '.capture_depth // "local_only"' 2>/dev/null)
|
|
513
|
+
SYNKRO_TIER=$(echo "$resp" | jq -r '.tier // "standard"' 2>/dev/null)
|
|
514
|
+
SYNKRO_RULES=$(echo "$resp" | jq -c '[.rules[]? | select(.hook_stage == "pre" or .hook_stage == "both" or .hook_stage == null) | {rule_id,text,severity,category,mode}]' 2>/dev/null || echo "[]")
|
|
515
|
+
# Cache the values
|
|
516
|
+
printf 'SYNKRO_CAPTURE_DEPTH="%s"\\nSYNKRO_TIER="%s"\\n' "$SYNKRO_CAPTURE_DEPTH" "$SYNKRO_TIER" > "$cache" 2>/dev/null || true
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
# Decide routing: "local" (grade on device) or "cloud" (POST to server)
|
|
520
|
+
synkro_route() {
|
|
521
|
+
[ "$SYNKRO_CAPTURE_DEPTH" = "local_only" ] && echo "local" && return
|
|
522
|
+
synkro_channel_up && echo "local" && return
|
|
523
|
+
echo "cloud"
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
# Grade locally via synkro CLI or claude --print. Reads prompt from stdin.
|
|
527
|
+
synkro_local_grade() {
|
|
528
|
+
local surface="$1"
|
|
529
|
+
if synkro_channel_up && [ -n "\${SYNKRO_CLI_BIN:-}" ] && [ -f "$SYNKRO_CLI_BIN" ] && command -v node >/dev/null 2>&1; then
|
|
530
|
+
node "$SYNKRO_CLI_BIN" grade "$surface" 2>/dev/null
|
|
531
|
+
elif synkro_channel_up && command -v synkro >/dev/null 2>&1; then
|
|
532
|
+
synkro grade "$surface" 2>/dev/null
|
|
533
|
+
elif command -v claude >/dev/null 2>&1; then
|
|
534
|
+
claude --print --model claude-sonnet-4-6 --no-session-persistence 2>/dev/null
|
|
535
|
+
fi
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
# Parse <synkro-verdict>...</synkro-verdict> XML from local grader output.
|
|
539
|
+
# Sets LOCAL_OK, LOCAL_REASON, LOCAL_RULE_ID, LOCAL_SEV, LOCAL_CAT.
|
|
540
|
+
synkro_parse_local_verdict() {
|
|
541
|
+
local resp="$1"
|
|
542
|
+
LOCAL_OK="true"; LOCAL_REASON=""; LOCAL_RULE_ID=""; LOCAL_SEV="low"; LOCAL_CAT="general"
|
|
543
|
+
local inner
|
|
544
|
+
inner=$(printf '%s' "$resp" | tr '\\n' ' ' | sed -nE 's|.*<synkro-verdict>(.*)</synkro-verdict>.*|\\1|p' | tail -1)
|
|
545
|
+
[ -z "$inner" ] && return
|
|
546
|
+
local ok_tag
|
|
547
|
+
ok_tag=$(printf '%s' "$inner" | sed -nE 's|.*<ok>(.*)</ok>.*|\\1|p' | head -1)
|
|
548
|
+
[ -n "$ok_tag" ] && LOCAL_OK="$ok_tag"
|
|
549
|
+
LOCAL_REASON=$(printf '%s' "$inner" | sed -nE 's|.*<reason>(.*)</reason>.*|\\1|p' | head -1)
|
|
550
|
+
if [ "$LOCAL_OK" = "false" ]; then
|
|
551
|
+
local fv
|
|
552
|
+
fv=$(printf '%s' "$inner" | awk -v RS='</violation>' '/<violation>/{print; exit}')
|
|
553
|
+
LOCAL_RULE_ID=$(printf '%s' "$fv" | sed -nE 's|.*<rule_id>(.*)</rule_id>.*|\\1|p' | head -1)
|
|
554
|
+
[ -z "$LOCAL_REASON" ] && LOCAL_REASON=$(printf '%s' "$fv" | sed -nE 's|.*<reason>(.*)</reason>.*|\\1|p' | head -1)
|
|
555
|
+
LOCAL_SEV=$(printf '%s' "$fv" | sed -nE 's|.*<severity>(.*)</severity>.*|\\1|p' | head -1)
|
|
556
|
+
LOCAL_CAT=$(printf '%s' "$fv" | sed -nE 's|.*<category>(.*)</category>.*|\\1|p' | head -1)
|
|
557
|
+
LOCAL_SEV="\${LOCAL_SEV:-high}"; LOCAL_CAT="\${LOCAL_CAT:-policy_violation}"
|
|
558
|
+
fi
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
# Fire anonymized telemetry for local verdicts. All args positional.
|
|
562
|
+
synkro_capture_local() {
|
|
563
|
+
local hook_type="$1" verdict="$2" severity="$3" category="$4" tool_name="$5" repo="$6" session_id="$7"
|
|
564
|
+
(
|
|
565
|
+
BODY=$(jq -n \\
|
|
566
|
+
--arg eid "$(uuidgen 2>/dev/null || echo "evt_$(date +%s)_$$")" \\
|
|
567
|
+
--arg ht "$hook_type" --arg v "$verdict" --arg s "$severity" --arg c "$category" \\
|
|
568
|
+
--arg tn "$tool_name" --arg r "$repo" --arg sid "$session_id" \\
|
|
569
|
+
'{capture_type:"local_verdict",event_id:$eid,hook_type:$ht,verdict:$v,severity:$s,category:$c,model:"claude-sonnet-4-6",tool_name:$tn}
|
|
570
|
+
+ (if $r != "" then {repo:$r} else {} end)
|
|
571
|
+
+ (if $sid != "" then {session_id:$sid} else {} end)')
|
|
572
|
+
curl -sS -X POST "\${GATEWAY_URL}/api/v1/hook/capture" \\
|
|
573
|
+
-H "Content-Type: application/json" -H "Authorization: Bearer $JWT" \\
|
|
574
|
+
-d "$BODY" --max-time 2 >/dev/null 2>&1
|
|
575
|
+
) &
|
|
576
|
+
}
|
|
577
|
+
|
|
498
578
|
synkro_post_with_retry() {
|
|
499
579
|
local url="$1" body="$2" timeout="\${3:-8}"
|
|
500
580
|
local resp
|
|
@@ -545,44 +625,57 @@ if [ -z "$COMMAND" ]; then echo '{}'; exit 0; fi
|
|
|
545
625
|
CMD_SHORT=$(printf '%s' "$COMMAND" | head -c 80)
|
|
546
626
|
synkro_log "bashGuard checking: $CMD_SHORT"
|
|
547
627
|
|
|
548
|
-
# Extract transcript context
|
|
549
628
|
TRANSCRIPT_PATH=$(echo "$PAYLOAD" | jq -r '.transcript_path // empty' 2>/dev/null)
|
|
550
629
|
USER_INTENT=""
|
|
551
630
|
RECENT_USER_MESSAGES="[]"
|
|
552
|
-
RECENT_MESSAGES="[]"
|
|
553
|
-
RECENT_ACTIONS="[]"
|
|
554
631
|
if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
|
|
555
|
-
RECENT_USER_MESSAGES=$(tail -400 "$TRANSCRIPT_PATH" | jq -c -s '
|
|
556
|
-
[.[] | select(.type == "user") | (.message.content
|
|
557
|
-
| if type == "string" then . else (map(.text? // "") | join(" ")) end)
|
|
558
|
-
| select(. != null and . != "")
|
|
559
|
-
] | .[-5:]' 2>/dev/null || echo "[]")
|
|
632
|
+
RECENT_USER_MESSAGES=$(tail -400 "$TRANSCRIPT_PATH" | jq -c -s '[.[] | select(.type == "user") | (.message.content | if type == "string" then . else (map(.text? // "") | join(" ")) end) | select(. != null and . != "")] | .[-5:]' 2>/dev/null || echo "[]")
|
|
560
633
|
USER_INTENT=$(echo "$RECENT_USER_MESSAGES" | jq -r '.[-1] // ""' 2>/dev/null || echo "")
|
|
561
|
-
RECENT_MESSAGES=$(tail -400 "$TRANSCRIPT_PATH" | jq -c -s '
|
|
562
|
-
[.[] | select(.type == "user" or .type == "assistant")
|
|
563
|
-
| {type, text: (.message.content | if type == "string" then .[0:500]
|
|
564
|
-
else ([.[]? | (.text? // "") | .[0:300]] | join(" ")) end)}
|
|
565
|
-
] | .[-10:]' 2>/dev/null || echo "[]")
|
|
566
|
-
RECENT_ACTIONS=$(tail -400 "$TRANSCRIPT_PATH" | jq -c -s '
|
|
567
|
-
[.[] | select(.type == "assistant") | .message.content[]?
|
|
568
|
-
| select(.type == "tool_use") | {tool: .name, input: (.input // {} | tostring | .[0:200])}
|
|
569
|
-
] | .[-5:]' 2>/dev/null || echo "[]")
|
|
570
634
|
fi
|
|
571
635
|
|
|
572
|
-
#
|
|
636
|
+
# Headless detection
|
|
637
|
+
IS_HEADLESS="\${SYNKRO_HEADLESS:-0}"
|
|
638
|
+
case "$PERMISSION_MODE" in acceptEdits|bypassPermissions|plan|auto) IS_HEADLESS="1" ;; esac
|
|
639
|
+
|
|
640
|
+
synkro_load_config
|
|
641
|
+
ROUTE=$(synkro_route)
|
|
642
|
+
|
|
643
|
+
if [ "$ROUTE" = "local" ]; then
|
|
644
|
+
# \u2500\u2500\u2500 Local grading (local_only privacy or local-cc channel) \u2500\u2500\u2500
|
|
645
|
+
GRADER_FILE=$(mktemp -t synkro-bash.XXXXXX)
|
|
646
|
+
trap "rm -f \\"$GRADER_FILE\\"" EXIT
|
|
647
|
+
printf 'Command: %s\\nUser intent: %s\\nOrg rules: %s\\n' "$COMMAND" "\${USER_INTENT:-none stated}" "\${SYNKRO_RULES:-[]}" > "$GRADER_FILE"
|
|
648
|
+
|
|
649
|
+
CC_RESP=$(synkro_local_grade bash < "$GRADER_FILE" || echo "")
|
|
650
|
+
synkro_parse_local_verdict "$CC_RESP"
|
|
651
|
+
|
|
652
|
+
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"
|
|
658
|
+
else
|
|
659
|
+
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"
|
|
661
|
+
fi
|
|
662
|
+
exit 0
|
|
663
|
+
fi
|
|
664
|
+
|
|
665
|
+
# \u2500\u2500\u2500 Cloud grading \u2500\u2500\u2500
|
|
573
666
|
CC_MODEL=""
|
|
574
667
|
CC_USAGE="{}"
|
|
668
|
+
RECENT_MESSAGES="[]"
|
|
669
|
+
RECENT_ACTIONS="[]"
|
|
670
|
+
SESSION_SUMMARY=""
|
|
575
671
|
if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
|
|
576
672
|
_LAST=$(grep '"type":"assistant"' "$TRANSCRIPT_PATH" 2>/dev/null | tail -1)
|
|
577
673
|
if [ -n "$_LAST" ]; then
|
|
578
674
|
CC_MODEL=$(echo "$_LAST" | jq -r '.message.model // empty' 2>/dev/null)
|
|
579
675
|
CC_USAGE=$(echo "$_LAST" | jq -c '{input_tokens:.message.usage.input_tokens,output_tokens:.message.usage.output_tokens,cache_creation_input_tokens:.message.usage.cache_creation_input_tokens,cache_read_input_tokens:.message.usage.cache_read_input_tokens}' 2>/dev/null || echo "{}")
|
|
580
676
|
fi
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
# Session summary from last summary entry
|
|
584
|
-
SESSION_SUMMARY=""
|
|
585
|
-
if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
|
|
677
|
+
RECENT_MESSAGES=$(tail -400 "$TRANSCRIPT_PATH" | jq -c -s '[.[] | select(.type == "user" or .type == "assistant") | {type, text: (.message.content | if type == "string" then .[0:500] else ([.[]? | (.text? // "") | .[0:300]] | join(" ")) end)}] | .[-10:]' 2>/dev/null || echo "[]")
|
|
678
|
+
RECENT_ACTIONS=$(tail -400 "$TRANSCRIPT_PATH" | jq -c -s '[.[] | select(.type == "assistant") | .message.content[]? | select(.type == "tool_use") | {tool: .name, input: (.input // {} | tostring | .[0:200])}] | .[-5:]' 2>/dev/null || echo "[]")
|
|
586
679
|
SESSION_SUMMARY=$(grep '"type":"summary"' "$TRANSCRIPT_PATH" 2>/dev/null | tail -1 | jq -r '.summary // empty' 2>/dev/null || echo "")
|
|
587
680
|
fi
|
|
588
681
|
|
|
@@ -664,6 +757,9 @@ if [ -z "$FILE_PATH" ]; then echo '{}'; exit 0; fi
|
|
|
664
757
|
FILE_SHORT=$(basename "$FILE_PATH")
|
|
665
758
|
synkro_log "editGuard checking: $FILE_SHORT"
|
|
666
759
|
|
|
760
|
+
IS_HEADLESS="\${SYNKRO_HEADLESS:-0}"
|
|
761
|
+
case "$PERMISSION_MODE" in acceptEdits|bypassPermissions|plan|auto) IS_HEADLESS="1" ;; esac
|
|
762
|
+
|
|
667
763
|
# Read file before edit for reconstruction
|
|
668
764
|
FILE_BEFORE=""
|
|
669
765
|
if [ "$TOOL_NAME" != "Write" ] && [ -n "$FILE_PATH" ] && [ -f "$FILE_PATH" ]; then
|
|
@@ -713,6 +809,32 @@ if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
|
|
|
713
809
|
RECENT_ACTIONS=$(tail -200 "$TRANSCRIPT_PATH" | jq -c -s '[.[] | select(.type == "assistant") | .message.content[]? | select(.type == "tool_use") | {tool: .name, input: (.input // {} | tostring | .[0:200])}] | .[-5:]' 2>/dev/null || echo "[]")
|
|
714
810
|
fi
|
|
715
811
|
|
|
812
|
+
synkro_load_config
|
|
813
|
+
ROUTE=$(synkro_route)
|
|
814
|
+
|
|
815
|
+
if [ "$ROUTE" = "local" ]; then
|
|
816
|
+
# \u2500\u2500\u2500 Local grading (local_only privacy or local-cc channel) \u2500\u2500\u2500
|
|
817
|
+
GRADER_FILE=$(mktemp -t synkro-edit.XXXXXX)
|
|
818
|
+
trap "rm -f \\"$GRADER_FILE\\"" EXIT
|
|
819
|
+
printf 'File: %s\\nProposed content (first 4000 chars):\\n%s\\nUser intent: %s\\nOrg rules: %s\\n' "$FILE_PATH" "$(printf '%s' "$PROPOSED" | head -c 4000)" "\${USER_INTENT:-none stated}" "\${SYNKRO_RULES:-[]}" > "$GRADER_FILE"
|
|
820
|
+
|
|
821
|
+
CC_RESP=$(synkro_local_grade edit < "$GRADER_FILE" || echo "")
|
|
822
|
+
synkro_parse_local_verdict "$CC_RESP"
|
|
823
|
+
|
|
824
|
+
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"
|
|
830
|
+
else
|
|
831
|
+
jq -n --arg m "[synkro:local] editGuard $FILE_SHORT \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
|
|
832
|
+
synkro_capture_local "edit" "pass" "audit" "\${LOCAL_CAT:-trivial_edit}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
|
|
833
|
+
fi
|
|
834
|
+
exit 0
|
|
835
|
+
fi
|
|
836
|
+
|
|
837
|
+
# \u2500\u2500\u2500 Cloud grading \u2500\u2500\u2500
|
|
716
838
|
BODY=$(jq -n \\
|
|
717
839
|
--arg hook_event "PreToolUse" \\
|
|
718
840
|
--arg tool_name "$TOOL_NAME" \\
|
|
@@ -829,6 +951,30 @@ while [ "$_PKG_DIR" != "/" ]; do
|
|
|
829
951
|
_PKG_DIR=$(dirname "$_PKG_DIR")
|
|
830
952
|
done
|
|
831
953
|
|
|
954
|
+
synkro_load_config
|
|
955
|
+
ROUTE=$(synkro_route)
|
|
956
|
+
|
|
957
|
+
if [ "$ROUTE" = "local" ]; then
|
|
958
|
+
# \u2500\u2500\u2500 Local edit scan (local_only privacy or local-cc channel) \u2500\u2500\u2500
|
|
959
|
+
GRADER_FILE=$(mktemp -t synkro-escan.XXXXXX)
|
|
960
|
+
trap "rm -f \\"$GRADER_FILE\\"" EXIT
|
|
961
|
+
printf 'File: %s\\nContent (first 4000 chars):\\n%s\\nOrg rules: %s\\n' "$FILE_PATH" "$(printf '%s' "$FILE_CONTENT" | head -c 4000)" "\${SYNKRO_RULES:-[]}" > "$GRADER_FILE"
|
|
962
|
+
|
|
963
|
+
CC_RESP=$(synkro_local_grade edit < "$GRADER_FILE" || echo "")
|
|
964
|
+
synkro_parse_local_verdict "$CC_RESP"
|
|
965
|
+
|
|
966
|
+
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"
|
|
970
|
+
else
|
|
971
|
+
jq -n --arg m "[synkro:local] editScan $BASENAME \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
|
|
972
|
+
synkro_capture_local "edit_scan" "pass" "audit" "\${LOCAL_CAT:-trivial_edit}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
|
|
973
|
+
fi
|
|
974
|
+
exit 0
|
|
975
|
+
fi
|
|
976
|
+
|
|
977
|
+
# \u2500\u2500\u2500 Cloud edit scan \u2500\u2500\u2500
|
|
832
978
|
BODY=$(jq -n \\
|
|
833
979
|
--arg hook_event "PostToolUse" \\
|
|
834
980
|
--arg tool_name "$TOOL_NAME" \\
|
|
@@ -863,7 +1009,6 @@ if [ -z "$RESP" ] || ! echo "$RESP" | jq -e 'type == "object"' >/dev/null 2>&1;
|
|
|
863
1009
|
exit 0
|
|
864
1010
|
fi
|
|
865
1011
|
|
|
866
|
-
# Server returns {hook_response: {...}} \u2014 extract and echo
|
|
867
1012
|
if echo "$RESP" | jq -e '.hook_response' >/dev/null 2>&1; then
|
|
868
1013
|
echo "$RESP" | jq -c '.hook_response'
|
|
869
1014
|
else
|
|
@@ -3456,7 +3601,7 @@ function writeConfigEnv(opts) {
|
|
|
3456
3601
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
3457
3602
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
3458
3603
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
3459
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.4.
|
|
3604
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.4.17")}`
|
|
3460
3605
|
];
|
|
3461
3606
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
3462
3607
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|