@synkro-sh/cli 1.3.35 → 1.3.37
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 +78 -11
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -790,6 +790,7 @@ if [ "$SYNKRO_CAPTURE_DEPTH" = "local_only" ] && [ -n "$VERDICT_KIND" ]; then
|
|
|
790
790
|
--arg category "$CATEGORY" \\
|
|
791
791
|
--arg model "\${CC_MODEL:-claude-sonnet-4-6}" \\
|
|
792
792
|
--arg tool_name "$TOOL_NAME" \\
|
|
793
|
+
--arg repo "\${GIT_REPO:-}" \\
|
|
793
794
|
'{
|
|
794
795
|
event_id: $event_id,
|
|
795
796
|
timestamp: $timestamp,
|
|
@@ -800,7 +801,7 @@ if [ "$SYNKRO_CAPTURE_DEPTH" = "local_only" ] && [ -n "$VERDICT_KIND" ]; then
|
|
|
800
801
|
category: $category,
|
|
801
802
|
model: $model,
|
|
802
803
|
tool_name: $tool_name
|
|
803
|
-
}')
|
|
804
|
+
} + (if $repo != "" then {repo: $repo} else {} end)')
|
|
804
805
|
curl -sS -X POST "\${GATEWAY_URL}/api/v1/events/local-verdict" \\
|
|
805
806
|
-H "Content-Type: application/json" \\
|
|
806
807
|
-H "Authorization: Bearer $JWT" \\
|
|
@@ -1267,6 +1268,7 @@ if [ "$SYNKRO_CAPTURE_DEPTH" = "local_only" ] && [ -n "$DECISION" ]; then
|
|
|
1267
1268
|
--arg category "$LOCAL_CATEGORY" \\
|
|
1268
1269
|
--arg model "\${CC_MODEL:-claude-sonnet-4-6}" \\
|
|
1269
1270
|
--arg tool_name "$TOOL_NAME" \\
|
|
1271
|
+
--arg repo "\${GIT_REPO:-}" \\
|
|
1270
1272
|
'{
|
|
1271
1273
|
event_id: $event_id,
|
|
1272
1274
|
timestamp: $timestamp,
|
|
@@ -1276,7 +1278,7 @@ if [ "$SYNKRO_CAPTURE_DEPTH" = "local_only" ] && [ -n "$DECISION" ]; then
|
|
|
1276
1278
|
category: $category,
|
|
1277
1279
|
model: $model,
|
|
1278
1280
|
tool_name: $tool_name
|
|
1279
|
-
}')
|
|
1281
|
+
} + (if $repo != "" then {repo: $repo} else {} end)')
|
|
1280
1282
|
curl -sS -X POST "\${GATEWAY_URL}/api/v1/events/local-verdict" \\
|
|
1281
1283
|
-H "Content-Type: application/json" \\
|
|
1282
1284
|
-H "Authorization: Bearer $JWT" \\
|
|
@@ -1573,11 +1575,12 @@ if [ "$SYNKRO_CAPTURE_DEPTH" = "local_only" ]; then
|
|
|
1573
1575
|
--arg category "$CATEGORY" \\
|
|
1574
1576
|
--arg model "\${CC_MODEL:-claude-sonnet-4-6}" \\
|
|
1575
1577
|
--arg tool_name "$TOOL_NAME" \\
|
|
1578
|
+
--arg repo "\${GIT_REPO:-}" \\
|
|
1576
1579
|
'{
|
|
1577
1580
|
event_id: $event_id, timestamp: $timestamp, hook_type: $hook_type,
|
|
1578
1581
|
verdict: $verdict, severity: $severity, risk_level: $risk_level,
|
|
1579
1582
|
category: $category, model: $model, tool_name: $tool_name
|
|
1580
|
-
}')
|
|
1583
|
+
} + (if $repo != "" then {repo: $repo} else {} end)')
|
|
1581
1584
|
curl -sS -X POST "\${GATEWAY_URL}/api/v1/events/local-verdict" \\
|
|
1582
1585
|
-H "Content-Type: application/json" \\
|
|
1583
1586
|
-H "Authorization: Bearer $JWT" \\
|
|
@@ -1986,25 +1989,73 @@ def _read_jwt():
|
|
|
1986
1989
|
except Exception:
|
|
1987
1990
|
return ""
|
|
1988
1991
|
|
|
1992
|
+
def _refresh_jwt():
|
|
1993
|
+
"""Refresh the access token using the saved refresh_token. Writes new
|
|
1994
|
+
tokens back to credentials.json. Returns the new access_token or ''."""
|
|
1995
|
+
try:
|
|
1996
|
+
with open(CREDS_PATH) as f:
|
|
1997
|
+
creds = json.load(f)
|
|
1998
|
+
rt = creds.get("refresh_token", "")
|
|
1999
|
+
if not rt:
|
|
2000
|
+
return ""
|
|
2001
|
+
req = urllib.request.Request(
|
|
2002
|
+
f"{GATEWAY_URL}/api/auth/refresh",
|
|
2003
|
+
data=json.dumps({"refresh_token": rt}).encode("utf-8"),
|
|
2004
|
+
headers={
|
|
2005
|
+
"Content-Type": "application/json",
|
|
2006
|
+
"User-Agent": "synkro-cli-grader-daemon/1",
|
|
2007
|
+
},
|
|
2008
|
+
)
|
|
2009
|
+
with urllib.request.urlopen(req, timeout=5) as resp:
|
|
2010
|
+
data = json.loads(resp.read().decode("utf-8"))
|
|
2011
|
+
new_at = data.get("access_token", "")
|
|
2012
|
+
new_rt = data.get("refresh_token", "") or rt
|
|
2013
|
+
if not new_at:
|
|
2014
|
+
return ""
|
|
2015
|
+
creds["access_token"] = new_at
|
|
2016
|
+
creds["refresh_token"] = new_rt
|
|
2017
|
+
tmp = str(CREDS_PATH) + ".synkro.tmp"
|
|
2018
|
+
with open(tmp, "w") as f:
|
|
2019
|
+
json.dump(creds, f)
|
|
2020
|
+
os.replace(tmp, str(CREDS_PATH))
|
|
2021
|
+
return new_at
|
|
2022
|
+
except Exception:
|
|
2023
|
+
return ""
|
|
2024
|
+
|
|
1989
2025
|
def fetch_primer(mode):
|
|
1990
|
-
"""Fetch primer text for {bash,edit} from /cli/judge-prompts. In-memory only.
|
|
2026
|
+
"""Fetch primer text for {bash,edit} from /cli/judge-prompts. In-memory only.
|
|
2027
|
+
Auto-refreshes JWT on 401 and retries once."""
|
|
1991
2028
|
_load_gateway_url()
|
|
1992
|
-
jwt = _read_jwt()
|
|
1993
|
-
if not jwt:
|
|
1994
|
-
return ""
|
|
1995
2029
|
field = "grader_primer_bash" if mode == "bash" else "grader_primer_edit"
|
|
1996
|
-
|
|
2030
|
+
|
|
2031
|
+
def _do_fetch(jwt):
|
|
1997
2032
|
req = urllib.request.Request(
|
|
1998
2033
|
f"{GATEWAY_URL}/api/v1/cli/judge-prompts",
|
|
1999
2034
|
headers={
|
|
2000
2035
|
"Authorization": f"Bearer {jwt}",
|
|
2001
|
-
# CF rejects requests without UA \u2014 without this we get 403.
|
|
2002
2036
|
"User-Agent": "synkro-cli-grader-daemon/1",
|
|
2003
2037
|
},
|
|
2004
2038
|
)
|
|
2005
2039
|
with urllib.request.urlopen(req, timeout=5) as resp:
|
|
2006
2040
|
data = json.loads(resp.read().decode("utf-8"))
|
|
2007
2041
|
return data.get(field, "") or ""
|
|
2042
|
+
|
|
2043
|
+
jwt = _read_jwt()
|
|
2044
|
+
if not jwt:
|
|
2045
|
+
return ""
|
|
2046
|
+
try:
|
|
2047
|
+
return _do_fetch(jwt)
|
|
2048
|
+
except urllib.error.HTTPError as e:
|
|
2049
|
+
if e.code != 401:
|
|
2050
|
+
return ""
|
|
2051
|
+
# Token expired \u2014 refresh and retry once.
|
|
2052
|
+
new_jwt = _refresh_jwt()
|
|
2053
|
+
if not new_jwt:
|
|
2054
|
+
return ""
|
|
2055
|
+
try:
|
|
2056
|
+
return _do_fetch(new_jwt)
|
|
2057
|
+
except Exception:
|
|
2058
|
+
return ""
|
|
2008
2059
|
except Exception:
|
|
2009
2060
|
return ""
|
|
2010
2061
|
|
|
@@ -2111,6 +2162,7 @@ class WarmGrader:
|
|
|
2111
2162
|
def __init__(self, primer):
|
|
2112
2163
|
self.primer = primer or ""
|
|
2113
2164
|
self._warm_proc = None
|
|
2165
|
+
self._warm_ready_at = 0.0
|
|
2114
2166
|
self._warm_thread = None
|
|
2115
2167
|
self._lock = threading.Lock()
|
|
2116
2168
|
self._total_grades = 0
|
|
@@ -2149,6 +2201,7 @@ class WarmGrader:
|
|
|
2149
2201
|
with self._lock:
|
|
2150
2202
|
old = self._warm_proc
|
|
2151
2203
|
self._warm_proc = proc
|
|
2204
|
+
self._warm_ready_at = time.time()
|
|
2152
2205
|
self._prewarm_ok = True
|
|
2153
2206
|
if old:
|
|
2154
2207
|
self._kill_proc(old)
|
|
@@ -2173,8 +2226,16 @@ class WarmGrader:
|
|
|
2173
2226
|
|
|
2174
2227
|
with self._lock:
|
|
2175
2228
|
proc = self._warm_proc
|
|
2229
|
+
ready_at = self._warm_ready_at
|
|
2176
2230
|
self._warm_proc = None
|
|
2177
|
-
|
|
2231
|
+
self._warm_ready_at = 0.0
|
|
2232
|
+
|
|
2233
|
+
# Warm-process TTL: claude --print holds an HTTP/2 conn to Anthropic
|
|
2234
|
+
# that goes stale after ~20s idle. The subprocess doesn't notice the
|
|
2235
|
+
# broken pipe and just hangs forever when given a prompt. Force cold
|
|
2236
|
+
# if the warm has been sitting idle too long \u2014 still faster than
|
|
2237
|
+
# eating a 10s stall timeout + cold fallback.
|
|
2238
|
+
WARM_TTL_SEC = int(os.environ.get("SYNKRO_DAEMON_WARM_TTL", "15"))
|
|
2178
2239
|
warm = True
|
|
2179
2240
|
if not proc or proc.poll() is not None:
|
|
2180
2241
|
log("no warm process, cold fallback")
|
|
@@ -2185,6 +2246,12 @@ class WarmGrader:
|
|
|
2185
2246
|
self._kill_proc(proc)
|
|
2186
2247
|
proc = self._make_proc()
|
|
2187
2248
|
warm = False
|
|
2249
|
+
elif ready_at and (time.time() - ready_at) > WARM_TTL_SEC:
|
|
2250
|
+
age = time.time() - ready_at
|
|
2251
|
+
log(f"warm process stale ({age:.0f}s > {WARM_TTL_SEC}s), cold fallback")
|
|
2252
|
+
self._kill_proc(proc)
|
|
2253
|
+
proc = self._make_proc()
|
|
2254
|
+
warm = False
|
|
2188
2255
|
|
|
2189
2256
|
wall_limit = int(os.environ.get("SYNKRO_DAEMON_WALL_TIMEOUT", "12"))
|
|
2190
2257
|
t0 = time.time()
|
|
@@ -3651,7 +3718,7 @@ function writeConfigEnv(opts) {
|
|
|
3651
3718
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
3652
3719
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
3653
3720
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
3654
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.3.
|
|
3721
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.3.37")}`
|
|
3655
3722
|
];
|
|
3656
3723
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
3657
3724
|
if (safeOrgId) lines.push(`SYNKRO_ORG_ID=${shellQuoteSingle(safeOrgId)}`);
|