@synkro-sh/cli 1.6.71 → 1.6.73
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 +65 -29
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -145,7 +145,7 @@ function installCCHooks(settingsPath, config) {
|
|
|
145
145
|
{
|
|
146
146
|
type: "command",
|
|
147
147
|
command: config.bashJudgeScriptPath,
|
|
148
|
-
timeout:
|
|
148
|
+
timeout: 50
|
|
149
149
|
}
|
|
150
150
|
],
|
|
151
151
|
[SYNKRO_MARKER]: true
|
|
@@ -156,12 +156,12 @@ function installCCHooks(settingsPath, config) {
|
|
|
156
156
|
{
|
|
157
157
|
type: "command",
|
|
158
158
|
command: config.editPrecheckScriptPath,
|
|
159
|
-
timeout:
|
|
159
|
+
timeout: 50
|
|
160
160
|
},
|
|
161
161
|
{
|
|
162
162
|
type: "command",
|
|
163
163
|
command: config.cwePrecheckScriptPath,
|
|
164
|
-
timeout:
|
|
164
|
+
timeout: 50
|
|
165
165
|
},
|
|
166
166
|
{
|
|
167
167
|
type: "command",
|
|
@@ -177,7 +177,7 @@ function installCCHooks(settingsPath, config) {
|
|
|
177
177
|
{
|
|
178
178
|
type: "command",
|
|
179
179
|
command: config.agentJudgeScriptPath,
|
|
180
|
-
timeout:
|
|
180
|
+
timeout: 50
|
|
181
181
|
}
|
|
182
182
|
],
|
|
183
183
|
[SYNKRO_MARKER]: true
|
|
@@ -188,7 +188,7 @@ function installCCHooks(settingsPath, config) {
|
|
|
188
188
|
{
|
|
189
189
|
type: "command",
|
|
190
190
|
command: config.planJudgeScriptPath,
|
|
191
|
-
timeout:
|
|
191
|
+
timeout: 50
|
|
192
192
|
}
|
|
193
193
|
],
|
|
194
194
|
[SYNKRO_MARKER]: true
|
|
@@ -200,7 +200,7 @@ function installCCHooks(settingsPath, config) {
|
|
|
200
200
|
{
|
|
201
201
|
type: "command",
|
|
202
202
|
command: config.mcpGateScriptPath,
|
|
203
|
-
timeout:
|
|
203
|
+
timeout: 50
|
|
204
204
|
}
|
|
205
205
|
],
|
|
206
206
|
[SYNKRO_MARKER]: true
|
|
@@ -1689,7 +1689,12 @@ async function cloudGrade(surface: string, prompt: string, jwt: string, timeoutM
|
|
|
1689
1689
|
// JWT (the Worker derives the org from the token and routes to that org's
|
|
1690
1690
|
// container). Any non-2xx throws so the caller's catch fails open.
|
|
1691
1691
|
const CONTAINERS_URL = process.env.SYNKRO_CONTAINERS_URL || 'https://containers.synkro.sh';
|
|
1692
|
-
|
|
1692
|
+
// Cloud grades cross the network and can hit a cold/queued worker, so they get a
|
|
1693
|
+
// longer leash than on-device channel grades. The hook chain is sized around it:
|
|
1694
|
+
// grade abort (45s) < watchdog (48s) < CC hook budget (50s) \u2014 the grade aborts
|
|
1695
|
+
// FIRST so the caller's catch fails open cleanly before CC ever force-kills us.
|
|
1696
|
+
const CLOUD_GRADE_TIMEOUT_MS = 45000;
|
|
1697
|
+
async function hostedGrade(role: GradeRole, prompt: string, jwt: string, timeoutMs = CLOUD_GRADE_TIMEOUT_MS, agentKind: AgentKind = 'claude_code'): Promise<string> {
|
|
1693
1698
|
const resp = await fetch(CONTAINERS_URL + '/grade/submit', {
|
|
1694
1699
|
method: 'POST',
|
|
1695
1700
|
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },
|
|
@@ -1705,18 +1710,20 @@ async function hostedGrade(role: GradeRole, prompt: string, jwt: string, timeout
|
|
|
1705
1710
|
return String(data.result || '');
|
|
1706
1711
|
}
|
|
1707
1712
|
|
|
1708
|
-
//
|
|
1709
|
-
// ccHookConfig.ts) so the AbortSignal fires and the caller's
|
|
1710
|
-
//
|
|
1711
|
-
//
|
|
1712
|
-
// prompt as a hook error instead of a clean pass.
|
|
1713
|
+
// Local-channel default 24s \u2014 MUST stay below the CC hook timeout (50s for
|
|
1714
|
+
// edit/bash/cwe in ccHookConfig.ts) so the AbortSignal fires and the caller's
|
|
1715
|
+
// catch fails open cleanly. A hung on-device worker should fail open fast; only
|
|
1716
|
+
// cloud grades (network + cold workers) get the longer 45s leash below.
|
|
1713
1717
|
export async function localGrade(surface: string, prompt: string, timeoutMs = 24000, agentKind: AgentKind = 'claude_code'): Promise<string> {
|
|
1714
1718
|
const jwt = loadJwt();
|
|
1715
1719
|
if (!jwt) throw new Error('NO_JWT');
|
|
1716
1720
|
// Cloud-container: the grader runs on Cloudflare Containers \u2014 route to the
|
|
1717
1721
|
// hosted ingress instead of the local channel. Same prompt + parseVerdict.
|
|
1722
|
+
// Cloud grades get the full 45s (not the local 24s): the round-trip + a cold
|
|
1723
|
+
// worker routinely needs >24s, which is exactly what was timing out and
|
|
1724
|
+
// failing open on big bash prompts.
|
|
1718
1725
|
if (process.env.SYNKRO_DEPLOY_LOCATION === 'cloud') {
|
|
1719
|
-
return hostedGrade(ROLE_MAP[surface] || 'grade-edit', prompt, jwt,
|
|
1726
|
+
return hostedGrade(ROLE_MAP[surface] || 'grade-edit', prompt, jwt, CLOUD_GRADE_TIMEOUT_MS, agentKind);
|
|
1720
1727
|
}
|
|
1721
1728
|
// BYOK grading mode routes the grade through an LLM API instead of the
|
|
1722
1729
|
// on-device channel worker pool. The grader prompt + parseVerdict are shared.
|
|
@@ -1727,14 +1734,14 @@ export async function localGrade(surface: string, prompt: string, timeoutMs = 24
|
|
|
1727
1734
|
return channelGrade(ROLE_MAP[surface] || 'grade-edit', prompt, jwt, 18929, timeoutMs, agentKind);
|
|
1728
1735
|
}
|
|
1729
1736
|
|
|
1730
|
-
//
|
|
1731
|
-
//
|
|
1732
|
-
//
|
|
1737
|
+
// Local-channel default 24s. Two cwe chunks grade in parallel within the same CC
|
|
1738
|
+
// budget (now 50s), so each must finish under it. Cloud cwe grades take the 45s
|
|
1739
|
+
// hosted leash (parallel chunks \u2192 wall-clock ~45s, still inside the 50s budget).
|
|
1733
1740
|
export async function localGradeCwe(prompt: string, agentKind: AgentKind = 'claude_code', timeoutMs = 24000): Promise<string> {
|
|
1734
1741
|
const jwt = loadJwt();
|
|
1735
1742
|
if (!jwt) throw new Error('NO_JWT');
|
|
1736
1743
|
if (process.env.SYNKRO_DEPLOY_LOCATION === 'cloud') {
|
|
1737
|
-
return hostedGrade('grade-cwe', prompt, jwt,
|
|
1744
|
+
return hostedGrade('grade-cwe', prompt, jwt, CLOUD_GRADE_TIMEOUT_MS, agentKind);
|
|
1738
1745
|
}
|
|
1739
1746
|
return channelGrade('grade-cwe', prompt, jwt, 18930, timeoutMs, agentKind);
|
|
1740
1747
|
}
|
|
@@ -3540,6 +3547,16 @@ export function graderUnavailableMessage(hook: string, target: string, errorMess
|
|
|
3540
3547
|
const agent = agentKind === 'cursor' ? 'Cursor' : 'Claude Code';
|
|
3541
3548
|
return hook + ' ' + target + ' \u2192 local grader not configured for ' + agent + '. Add this agent to your synkro.toml file and run \`synkro install\`.';
|
|
3542
3549
|
}
|
|
3550
|
+
// In cloud-container mode the grade ran against the hosted ingress, NOT a local
|
|
3551
|
+
// worker \u2014 say so, and distinguish a timeout (slow/cold grade) from a true
|
|
3552
|
+
// outage so "local grader unavailable" stops masquerading as a dead grader.
|
|
3553
|
+
if (process.env.SYNKRO_DEPLOY_LOCATION === 'cloud') {
|
|
3554
|
+
const isTimeout = /tim(e|ed)\\s*out|aborted|the operation was aborted/i.test(errorMessage);
|
|
3555
|
+
if (isTimeout) {
|
|
3556
|
+
return hook + ' ' + target + ' \u2192 cloud grade timed out (45s), skipped';
|
|
3557
|
+
}
|
|
3558
|
+
return hook + ' ' + target + ' \u2192 cloud grader unavailable, skipped';
|
|
3559
|
+
}
|
|
3543
3560
|
return hook + ' ' + target + ' \u2192 local grader unavailable, skipped';
|
|
3544
3561
|
}
|
|
3545
3562
|
|
|
@@ -3635,7 +3652,7 @@ const agentKind = (process.env.SYNKRO_HOOK_FORMAT === 'cursor' || process.argv.i
|
|
|
3635
3652
|
|
|
3636
3653
|
async function main() {
|
|
3637
3654
|
setupCursorHookSignals();
|
|
3638
|
-
installHookWatchdog(
|
|
3655
|
+
installHookWatchdog(48000);
|
|
3639
3656
|
try {
|
|
3640
3657
|
const input = await readStdin();
|
|
3641
3658
|
if (!input.trim()) { outputEmpty(); return; }
|
|
@@ -4029,7 +4046,7 @@ function scanPackageCapabilities(pkgName: string, cwd: string): PackageCapabilit
|
|
|
4029
4046
|
|
|
4030
4047
|
async function main() {
|
|
4031
4048
|
setupCursorHookSignals();
|
|
4032
|
-
installHookWatchdog(
|
|
4049
|
+
installHookWatchdog(48000);
|
|
4033
4050
|
try {
|
|
4034
4051
|
const input = await readStdin();
|
|
4035
4052
|
if (!input.trim()) { outputEmpty(); return; }
|
|
@@ -4942,7 +4959,7 @@ function isDuplicate(command: string, sessionId: string): boolean {
|
|
|
4942
4959
|
|
|
4943
4960
|
async function main() {
|
|
4944
4961
|
setupCursorHookSignals();
|
|
4945
|
-
installHookWatchdog(
|
|
4962
|
+
installHookWatchdog(48000);
|
|
4946
4963
|
try {
|
|
4947
4964
|
const input = await readStdin();
|
|
4948
4965
|
if (!input.trim()) { outputEmpty(); return; }
|
|
@@ -5214,7 +5231,7 @@ const agentKind = (process.env.SYNKRO_HOOK_FORMAT === 'cursor' || process.argv.i
|
|
|
5214
5231
|
|
|
5215
5232
|
async function main() {
|
|
5216
5233
|
setupCursorHookSignals();
|
|
5217
|
-
installHookWatchdog(
|
|
5234
|
+
installHookWatchdog(48000);
|
|
5218
5235
|
try {
|
|
5219
5236
|
const input = await readStdin();
|
|
5220
5237
|
if (!input.trim()) { outputEmpty(); return; }
|
|
@@ -5451,7 +5468,7 @@ function appendReviewToPlan(planFile: string, verdict: string): void {
|
|
|
5451
5468
|
|
|
5452
5469
|
async function main() {
|
|
5453
5470
|
setupCursorHookSignals();
|
|
5454
|
-
installHookWatchdog(
|
|
5471
|
+
installHookWatchdog(48000);
|
|
5455
5472
|
try {
|
|
5456
5473
|
const input = await readStdin();
|
|
5457
5474
|
if (!input.trim()) { outputEmpty(); return; }
|
|
@@ -10386,7 +10403,7 @@ function writeConfigEnv(opts) {
|
|
|
10386
10403
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
10387
10404
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
10388
10405
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
10389
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.6.
|
|
10406
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.6.73")}`
|
|
10390
10407
|
];
|
|
10391
10408
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
10392
10409
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
@@ -10560,6 +10577,16 @@ async function verifyCloudGrader(jwt2) {
|
|
|
10560
10577
|
return false;
|
|
10561
10578
|
}
|
|
10562
10579
|
console.log(` \u2713 ${healthy} Claude worker(s) healthy`);
|
|
10580
|
+
console.log(" Running smoke grade (a worker scores a known-risky edit; up to 120s)...");
|
|
10581
|
+
const started = Date.now();
|
|
10582
|
+
const ticker = setInterval(() => {
|
|
10583
|
+
const secs = Math.round((Date.now() - started) / 1e3);
|
|
10584
|
+
process.stdout.write(`\r \u2026grading (${secs}s elapsed) `);
|
|
10585
|
+
}, 1e3);
|
|
10586
|
+
const stopTicker = () => {
|
|
10587
|
+
clearInterval(ticker);
|
|
10588
|
+
process.stdout.write("\r\x1B[2K");
|
|
10589
|
+
};
|
|
10563
10590
|
try {
|
|
10564
10591
|
const probe = 'Grade this edit. File: t.py. Change: os.system(request.args["cmd"])';
|
|
10565
10592
|
const r = await fetch(`${base}/grade/submit`, {
|
|
@@ -10568,6 +10595,7 @@ async function verifyCloudGrader(jwt2) {
|
|
|
10568
10595
|
body: JSON.stringify({ role: "grade-edit", payload: probe, content: probe, agent_kind: "claude_code" }),
|
|
10569
10596
|
signal: AbortSignal.timeout(12e4)
|
|
10570
10597
|
});
|
|
10598
|
+
stopTicker();
|
|
10571
10599
|
const raw = await r.text();
|
|
10572
10600
|
let body = {};
|
|
10573
10601
|
try {
|
|
@@ -10591,6 +10619,7 @@ async function verifyCloudGrader(jwt2) {
|
|
|
10591
10619
|
await printWorkerDebug(base, jwt2);
|
|
10592
10620
|
return false;
|
|
10593
10621
|
} catch (e) {
|
|
10622
|
+
stopTicker();
|
|
10594
10623
|
const reason = e?.name === "TimeoutError" ? "the grade never returned within 120s (worker hung \u2014 usually claude auth)" : `the request failed (${e?.message || "network"})`;
|
|
10595
10624
|
console.warn(` \u2717 smoke grade FAILED: ${reason}.`);
|
|
10596
10625
|
console.warn(" The container is provisioned but a grade does not complete; re-run `synkro install` \u2192 cloud.\n");
|
|
@@ -12063,7 +12092,14 @@ function writePluginFiles() {
|
|
|
12063
12092
|
c.pluginSettingsPath,
|
|
12064
12093
|
JSON.stringify({
|
|
12065
12094
|
fastMode: true,
|
|
12066
|
-
enabledMcpjsonServers: ["synkro-local"]
|
|
12095
|
+
enabledMcpjsonServers: ["synkro-local"],
|
|
12096
|
+
// Extended thinking is pure latency tax on a structured verdict grade and
|
|
12097
|
+
// emits reasoning the verdict parser isn't tuned for. effortLevel only
|
|
12098
|
+
// SOFTENS it; CLAUDE_CODE_DISABLE_THINKING=1 is the authoritative off
|
|
12099
|
+
// switch. Mirror the cloud pool (pool-config.sh) so local + cloud graders
|
|
12100
|
+
// behave identically.
|
|
12101
|
+
effortLevel: "low",
|
|
12102
|
+
env: { CLAUDE_CODE_DISABLE_THINKING: "1" }
|
|
12067
12103
|
}, null, 2) + "\n",
|
|
12068
12104
|
"utf-8"
|
|
12069
12105
|
);
|
|
@@ -12304,7 +12340,7 @@ tmux kill-session -t "=$SESSION" 2>/dev/null || true
|
|
|
12304
12340
|
# Start claude inside a detached tmux session so it has a real pty.
|
|
12305
12341
|
# Redirect stderr to the log so we can see why it dies.
|
|
12306
12342
|
tmux new-session -d -s "$SESSION" \\
|
|
12307
|
-
"SYNKRO_CHANNEL_PORT=${CHANNEL_1_PORT} claude --dangerously-load-development-channels server:synkro-local --dangerously-skip-permissions --setting-sources project,local --model claude-sonnet-4-6 2>>$LOG; echo 'claude exited with code '$'?' >> $LOG"
|
|
12343
|
+
"SYNKRO_CHANNEL_PORT=${CHANNEL_1_PORT} CLAUDE_CODE_DISABLE_THINKING=1 claude --dangerously-load-development-channels server:synkro-local --dangerously-skip-permissions --setting-sources project,local --model claude-sonnet-4-6 2>>$LOG; echo 'claude exited with code '$'?' >> $LOG"
|
|
12308
12344
|
|
|
12309
12345
|
# Claude's --dangerously-load-development-channels shows a confirmation
|
|
12310
12346
|
# prompt: option 1 = "I am using this for local development" (accept),
|
|
@@ -12366,7 +12402,7 @@ log "claude version: $(claude --version 2>&1 | head -1)"
|
|
|
12366
12402
|
tmux kill-session -t "=$SESSION" 2>/dev/null || true
|
|
12367
12403
|
|
|
12368
12404
|
tmux new-session -d -s "$SESSION" \\
|
|
12369
|
-
"SYNKRO_CHANNEL_PORT=${CHANNEL_2_PORT} claude --dangerously-load-development-channels server:synkro-local --dangerously-skip-permissions --setting-sources project,local --model claude-sonnet-4-6 2>>$LOG; echo 'claude exited with code '$'?' >> $LOG"
|
|
12405
|
+
"SYNKRO_CHANNEL_PORT=${CHANNEL_2_PORT} CLAUDE_CODE_DISABLE_THINKING=1 claude --dangerously-load-development-channels server:synkro-local --dangerously-skip-permissions --setting-sources project,local --model claude-sonnet-4-6 2>>$LOG; echo 'claude exited with code '$'?' >> $LOG"
|
|
12370
12406
|
|
|
12371
12407
|
sleep 3
|
|
12372
12408
|
if tmux has-session -t "=$SESSION" 2>/dev/null; then
|
|
@@ -12422,7 +12458,7 @@ log "claude version: $(claude --version 2>&1 | head -1)"
|
|
|
12422
12458
|
tmux kill-session -t "=$SESSION" 2>/dev/null || true
|
|
12423
12459
|
|
|
12424
12460
|
tmux new-session -d -s "$SESSION" \\
|
|
12425
|
-
"SYNKRO_CHANNEL_PORT=${CHANNEL_3_PORT} claude --dangerously-load-development-channels server:synkro-local --dangerously-skip-permissions --setting-sources project,local --model claude-sonnet-4-6 2>>$LOG; echo 'claude exited with code '$'?' >> $LOG"
|
|
12461
|
+
"SYNKRO_CHANNEL_PORT=${CHANNEL_3_PORT} CLAUDE_CODE_DISABLE_THINKING=1 claude --dangerously-load-development-channels server:synkro-local --dangerously-skip-permissions --setting-sources project,local --model claude-sonnet-4-6 2>>$LOG; echo 'claude exited with code '$'?' >> $LOG"
|
|
12426
12462
|
|
|
12427
12463
|
sleep 3
|
|
12428
12464
|
if tmux has-session -t "=$SESSION" 2>/dev/null; then
|
|
@@ -12478,7 +12514,7 @@ log "claude version: $(claude --version 2>&1 | head -1)"
|
|
|
12478
12514
|
tmux kill-session -t "=$SESSION" 2>/dev/null || true
|
|
12479
12515
|
|
|
12480
12516
|
tmux new-session -d -s "$SESSION" \\
|
|
12481
|
-
"SYNKRO_CHANNEL_PORT=${CHANNEL_4_PORT} claude --dangerously-load-development-channels server:synkro-local --dangerously-skip-permissions --setting-sources project,local --model claude-sonnet-4-6 2>>$LOG; echo 'claude exited with code '$'?' >> $LOG"
|
|
12517
|
+
"SYNKRO_CHANNEL_PORT=${CHANNEL_4_PORT} CLAUDE_CODE_DISABLE_THINKING=1 claude --dangerously-load-development-channels server:synkro-local --dangerously-skip-permissions --setting-sources project,local --model claude-sonnet-4-6 2>>$LOG; echo 'claude exited with code '$'?' >> $LOG"
|
|
12482
12518
|
|
|
12483
12519
|
sleep 3
|
|
12484
12520
|
if tmux has-session -t "=$SESSION" 2>/dev/null; then
|
|
@@ -14019,7 +14055,7 @@ var args = process.argv.slice(2);
|
|
|
14019
14055
|
var cmd = args[0] || "";
|
|
14020
14056
|
var subArgs = args.slice(1);
|
|
14021
14057
|
function printVersion() {
|
|
14022
|
-
console.log("1.6.
|
|
14058
|
+
console.log("1.6.73");
|
|
14023
14059
|
}
|
|
14024
14060
|
function printHelp2() {
|
|
14025
14061
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|