@synkro-sh/cli 1.3.41 → 1.3.42
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 +63 -26
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -1988,7 +1988,7 @@ Commands:
|
|
|
1988
1988
|
status - print "running"/"stopped"
|
|
1989
1989
|
"""
|
|
1990
1990
|
|
|
1991
|
-
import os, sys, json, socket, time, signal, fcntl, re, select
|
|
1991
|
+
import os, sys, json, socket, time, signal, fcntl, re, select, queue
|
|
1992
1992
|
import subprocess, threading, urllib.request, urllib.error
|
|
1993
1993
|
from pathlib import Path
|
|
1994
1994
|
|
|
@@ -2119,13 +2119,18 @@ def log(msg):
|
|
|
2119
2119
|
|
|
2120
2120
|
STALL_TIMEOUT_SEC = int(os.environ.get("SYNKRO_DAEMON_STALL_TIMEOUT", "10"))
|
|
2121
2121
|
|
|
2122
|
-
def _read_response(proc, timeout=45):
|
|
2122
|
+
def _read_response(proc, timeout=45, stop_event=None):
|
|
2123
|
+
"""Read stream-json from proc.stdout until a 'result' message arrives.
|
|
2124
|
+
If stop_event is set externally, returns immediately with empty string \u2014
|
|
2125
|
+
used by the parallel-race grade path to abort the loser."""
|
|
2123
2126
|
acc = []
|
|
2124
2127
|
deadline = time.time() + timeout
|
|
2125
2128
|
last_data = time.time()
|
|
2126
2129
|
fd = proc.stdout.fileno()
|
|
2127
2130
|
buf = ""
|
|
2128
2131
|
while True:
|
|
2132
|
+
if stop_event is not None and stop_event.is_set():
|
|
2133
|
+
return ""
|
|
2129
2134
|
remaining = deadline - time.time()
|
|
2130
2135
|
if remaining <= 0:
|
|
2131
2136
|
log("read timeout")
|
|
@@ -2133,7 +2138,7 @@ def _read_response(proc, timeout=45):
|
|
|
2133
2138
|
if time.time() - last_data > STALL_TIMEOUT_SEC:
|
|
2134
2139
|
log(f"stall timeout: no data for {STALL_TIMEOUT_SEC}s")
|
|
2135
2140
|
return ""
|
|
2136
|
-
ready, _, _ = select.select([fd], [], [], min(remaining,
|
|
2141
|
+
ready, _, _ = select.select([fd], [], [], min(remaining, 1.0))
|
|
2137
2142
|
if not ready:
|
|
2138
2143
|
if proc.poll() is not None:
|
|
2139
2144
|
log("process exited during read")
|
|
@@ -2262,39 +2267,71 @@ class WarmGrader:
|
|
|
2262
2267
|
self._warm_proc = None
|
|
2263
2268
|
self._warm_ready_at = 0.0
|
|
2264
2269
|
|
|
2265
|
-
# Discard warm processes that have been idle too long.
|
|
2266
2270
|
WARM_TTL_SEC = int(os.environ.get("SYNKRO_DAEMON_WARM_TTL", "15"))
|
|
2267
|
-
warm
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
proc = self._make_proc()
|
|
2271
|
-
warm = False
|
|
2272
|
-
elif proc.stdin.closed:
|
|
2273
|
-
log("warm process stdin closed, cold fallback")
|
|
2271
|
+
# Decide if the warm process is usable for the race.
|
|
2272
|
+
warm_usable = bool(proc) and proc.poll() is None and not proc.stdin.closed
|
|
2273
|
+
if warm_usable and ready_at and (time.time() - ready_at) > WARM_TTL_SEC:
|
|
2274
2274
|
self._kill_proc(proc)
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
elif ready_at and (time.time() - ready_at) > WARM_TTL_SEC:
|
|
2278
|
-
age = time.time() - ready_at
|
|
2279
|
-
log(f"warm process stale ({age:.0f}s > {WARM_TTL_SEC}s), cold fallback")
|
|
2275
|
+
warm_usable = False
|
|
2276
|
+
if not warm_usable and proc:
|
|
2280
2277
|
self._kill_proc(proc)
|
|
2281
|
-
proc =
|
|
2282
|
-
warm = False
|
|
2278
|
+
proc = None
|
|
2283
2279
|
|
|
2284
2280
|
wall_limit = int(os.environ.get("SYNKRO_DAEMON_WALL_TIMEOUT", "12"))
|
|
2281
|
+
race_timeout = min(GRADE_TIMEOUT_SEC, wall_limit)
|
|
2282
|
+
|
|
2283
|
+
# Race a warm and a fresh cold process. Whichever returns a non-empty
|
|
2284
|
+
# response first wins; the other is killed. If we have no warm, just
|
|
2285
|
+
# run cold solo (no benefit to racing two cold spawns of the same age).
|
|
2286
|
+
cold_proc = self._make_proc() if warm_usable else proc or self._make_proc()
|
|
2287
|
+
warm_proc = proc if warm_usable else None
|
|
2288
|
+
|
|
2289
|
+
result_q = queue.Queue()
|
|
2290
|
+
stop_event = threading.Event()
|
|
2291
|
+
|
|
2292
|
+
def grade_worker(p, label):
|
|
2293
|
+
try:
|
|
2294
|
+
_send_msg(p, prompt)
|
|
2295
|
+
r = _read_response(p, timeout=race_timeout, stop_event=stop_event)
|
|
2296
|
+
if r:
|
|
2297
|
+
result_q.put((label, r))
|
|
2298
|
+
except Exception as e:
|
|
2299
|
+
log(f"grade {label} error: {e}")
|
|
2300
|
+
|
|
2301
|
+
threads = []
|
|
2302
|
+
if warm_proc is not None:
|
|
2303
|
+
t = threading.Thread(target=grade_worker, args=(warm_proc, "warm"), daemon=True)
|
|
2304
|
+
t.start()
|
|
2305
|
+
threads.append(t)
|
|
2306
|
+
t = threading.Thread(target=grade_worker, args=(cold_proc, "cold"), daemon=True)
|
|
2307
|
+
t.start()
|
|
2308
|
+
threads.append(t)
|
|
2309
|
+
|
|
2285
2310
|
t0 = time.time()
|
|
2311
|
+
winner_label = None
|
|
2312
|
+
resp = ""
|
|
2286
2313
|
try:
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2314
|
+
deadline = time.time() + race_timeout + 2
|
|
2315
|
+
while time.time() < deadline:
|
|
2316
|
+
try:
|
|
2317
|
+
label, r = result_q.get(timeout=0.5)
|
|
2318
|
+
if r:
|
|
2319
|
+
resp = r
|
|
2320
|
+
winner_label = label
|
|
2321
|
+
break
|
|
2322
|
+
except queue.Empty:
|
|
2323
|
+
if all(not th.is_alive() for th in threads):
|
|
2324
|
+
break
|
|
2292
2325
|
finally:
|
|
2293
|
-
|
|
2326
|
+
stop_event.set()
|
|
2327
|
+
if warm_proc is not None:
|
|
2328
|
+
self._kill_proc(warm_proc)
|
|
2329
|
+
self._kill_proc(cold_proc)
|
|
2294
2330
|
|
|
2295
2331
|
elapsed = (time.time() - t0) * 1000
|
|
2296
2332
|
self._total_grades += 1
|
|
2297
|
-
|
|
2333
|
+
winner = winner_label or "none"
|
|
2334
|
+
log(f"grade #{self._total_grades} race={'warm+cold' if warm_proc else 'cold'} won={winner} elapsed={elapsed:.0f}ms resp={len(resp)}ch")
|
|
2298
2335
|
|
|
2299
2336
|
self._start_prewarm()
|
|
2300
2337
|
return resp
|
|
@@ -3746,7 +3783,7 @@ function writeConfigEnv(opts) {
|
|
|
3746
3783
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
3747
3784
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
3748
3785
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
3749
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.3.
|
|
3786
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.3.42")}`
|
|
3750
3787
|
];
|
|
3751
3788
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
3752
3789
|
if (safeOrgId) lines.push(`SYNKRO_ORG_ID=${shellQuoteSingle(safeOrgId)}`);
|