@synkro-sh/cli 1.6.60 → 1.6.62
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 +91 -8
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -1593,6 +1593,13 @@ export async function loadConfig(jwt: string, query?: string): Promise<HookConfi
|
|
|
1593
1593
|
export async function route(config: HookConfig, synkroFile?: SynkroFileConfig): Promise<'local' | 'cloud'> {
|
|
1594
1594
|
const gradingMode = synkroFile?.grader?.mode || process.env.SYNKRO_GRADING_MODE || config.gradingMode || 'local';
|
|
1595
1595
|
if (gradingMode === 'byok') return 'cloud';
|
|
1596
|
+
// Cloud-container deploy ([grader] location = cloud): grade via localGrade(),
|
|
1597
|
+
// which routes to the hosted container (containers.synkro.sh) and emits the
|
|
1598
|
+
// same rich systemMessage. Return 'local' so the hook takes that branch \u2014 NOT
|
|
1599
|
+
// the legacy BYOK 'cloud' branch (which a non-BYOK org can't use, so it would
|
|
1600
|
+
// silently produce no verdict / no system message). channelUp() is false in
|
|
1601
|
+
// cloud (no local container), so without this it would wrongly fall to 'cloud'.
|
|
1602
|
+
if (process.env.SYNKRO_DEPLOY_LOCATION === 'cloud') return 'local';
|
|
1596
1603
|
if (config.captureDepth === 'local_only') return 'local';
|
|
1597
1604
|
if (await channelUp()) return 'local';
|
|
1598
1605
|
return 'cloud';
|
|
@@ -1601,6 +1608,10 @@ export async function route(config: HookConfig, synkroFile?: SynkroFileConfig):
|
|
|
1601
1608
|
export async function cweRoute(config: HookConfig, synkroFile?: SynkroFileConfig): Promise<'local' | 'byok' | 'skip'> {
|
|
1602
1609
|
const gradingMode = synkroFile?.grader?.mode || process.env.SYNKRO_GRADING_MODE || config.gradingMode || 'local';
|
|
1603
1610
|
if (gradingMode === 'byok') return 'byok';
|
|
1611
|
+
// Cloud-container: CWE scanning runs on the hosted container via localGradeCwe()
|
|
1612
|
+
// (which honors SYNKRO_DEPLOY_LOCATION). Without this, cweChannelUp() is false in
|
|
1613
|
+
// cloud and we'd fall to 'skip' \u2014 silently disabling CWE scanning in cloud mode.
|
|
1614
|
+
if (process.env.SYNKRO_DEPLOY_LOCATION === 'cloud') return 'local';
|
|
1604
1615
|
if (await cweChannelUp()) return 'local';
|
|
1605
1616
|
return 'skip';
|
|
1606
1617
|
}
|
|
@@ -5671,7 +5682,8 @@ async function main() {
|
|
|
5671
5682
|
const isChannelUp = await channelUp();
|
|
5672
5683
|
const synkroFile = loadSynkroFile(cwd);
|
|
5673
5684
|
const gradingMode = synkroFile.grader.mode || process.env.SYNKRO_GRADING_MODE || 'local';
|
|
5674
|
-
const
|
|
5685
|
+
const deployCloud = process.env.SYNKRO_DEPLOY_LOCATION === 'cloud';
|
|
5686
|
+
const rt = gradingMode === 'byok' ? 'cloud' : (isChannelUp || deployCloud ? 'local' : 'cloud');
|
|
5675
5687
|
|
|
5676
5688
|
let policyName = '';
|
|
5677
5689
|
let silent = false;
|
|
@@ -5705,7 +5717,7 @@ async function main() {
|
|
|
5705
5717
|
|
|
5706
5718
|
const fakeConfig: HookConfig = { captureDepth: 'local_only', tier: 'standard', silent, policyName, rules: [], scanExemptions: [], gradingMode, storageMode: process.env.SYNKRO_STORAGE_MODE || 'local' };
|
|
5707
5719
|
const tagStr = tag(rt, fakeConfig);
|
|
5708
|
-
const routeLine = tagStr + ' inference: ' + (gradingMode === 'byok' ? 'cloud (BYOK)' : isChannelUp ? 'local-cc (channel reachable on 127.0.0.1:18929)' : 'cloud (local-cc channel not reachable)');
|
|
5720
|
+
const routeLine = tagStr + ' inference: ' + (gradingMode === 'byok' ? 'cloud (BYOK)' : deployCloud ? 'cloud container (containers.synkro.sh)' : isChannelUp ? 'local-cc (channel reachable on 127.0.0.1:18929)' : 'cloud (local-cc channel not reachable)');
|
|
5709
5721
|
|
|
5710
5722
|
if (!jwt) {
|
|
5711
5723
|
outputJson({ systemMessage: routeLine });
|
|
@@ -9979,6 +9991,7 @@ __export(install_exports, {
|
|
|
9979
9991
|
parseArgs: () => parseArgs,
|
|
9980
9992
|
reconcileDeployLocation: () => reconcileDeployLocation,
|
|
9981
9993
|
reconcileHarness: () => reconcileHarness,
|
|
9994
|
+
recycleCloudContainer: () => recycleCloudContainer,
|
|
9982
9995
|
syncSkillFiles: () => syncSkillFiles,
|
|
9983
9996
|
writeHookScripts: () => writeHookScripts
|
|
9984
9997
|
});
|
|
@@ -10305,7 +10318,7 @@ function writeConfigEnv(opts) {
|
|
|
10305
10318
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
10306
10319
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
10307
10320
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
10308
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.6.
|
|
10321
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.6.62")}`
|
|
10309
10322
|
];
|
|
10310
10323
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
10311
10324
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
@@ -10373,6 +10386,52 @@ async function provisionCloudContainer(opts) {
|
|
|
10373
10386
|
setupToken = "";
|
|
10374
10387
|
}
|
|
10375
10388
|
}
|
|
10389
|
+
async function verifyCloudGrader(jwt2) {
|
|
10390
|
+
const base = (process.env.SYNKRO_CONTAINERS_URL || "https://containers.synkro.sh").replace(/\/$/, "");
|
|
10391
|
+
console.log(" Warming the cloud grader (booting container + checking workers)...");
|
|
10392
|
+
const deadline = Date.now() + 18e4;
|
|
10393
|
+
let healthy = 0;
|
|
10394
|
+
while (Date.now() < deadline) {
|
|
10395
|
+
try {
|
|
10396
|
+
const r = await fetch(`${base}/diag`, { headers: { Authorization: `Bearer ${jwt2}` }, signal: AbortSignal.timeout(6e4) });
|
|
10397
|
+
if (r.ok) {
|
|
10398
|
+
const j = await r.json();
|
|
10399
|
+
healthy = j.claude_code?.healthy ?? 0;
|
|
10400
|
+
if (healthy >= 1) break;
|
|
10401
|
+
}
|
|
10402
|
+
} catch {
|
|
10403
|
+
}
|
|
10404
|
+
await new Promise((res) => setTimeout(res, 5e3));
|
|
10405
|
+
}
|
|
10406
|
+
if (healthy < 1) {
|
|
10407
|
+
console.warn(" \u26A0 cloud grader did not report a healthy worker within ~3m.");
|
|
10408
|
+
console.warn(" Grading is NOT verified \u2014 check the container or re-run `synkro install`.\n");
|
|
10409
|
+
return false;
|
|
10410
|
+
}
|
|
10411
|
+
console.log(` \u2713 ${healthy} Claude worker(s) healthy`);
|
|
10412
|
+
try {
|
|
10413
|
+
const probe = 'Grade this edit. File: t.py. Change: os.system(request.args["cmd"])';
|
|
10414
|
+
const r = await fetch(`${base}/grade/submit`, {
|
|
10415
|
+
method: "POST",
|
|
10416
|
+
headers: { Authorization: `Bearer ${jwt2}`, "Content-Type": "application/json" },
|
|
10417
|
+
body: JSON.stringify({ role: "grade-edit", payload: probe, content: probe, agent_kind: "claude_code" }),
|
|
10418
|
+
signal: AbortSignal.timeout(12e4)
|
|
10419
|
+
});
|
|
10420
|
+
const body = await r.json().catch(() => ({}));
|
|
10421
|
+
if (r.ok && typeof body.result === "string" && body.result.includes("<synkro-verdict>")) {
|
|
10422
|
+
console.log(" \u2713 smoke grade passed \u2014 cloud grading is live\n");
|
|
10423
|
+
return true;
|
|
10424
|
+
}
|
|
10425
|
+
console.warn(` \u26A0 smoke grade did not return a verdict (HTTP ${r.status}).`);
|
|
10426
|
+
console.warn(" The container is provisioned but grading is not healthy \u2014 likely the Claude");
|
|
10427
|
+
console.warn(" setup-token. Re-run `synkro install` to re-authorize the hosted worker.\n");
|
|
10428
|
+
return false;
|
|
10429
|
+
} catch {
|
|
10430
|
+
console.warn(" \u26A0 smoke grade timed out / connection dropped \u2014 grading is not responding.");
|
|
10431
|
+
console.warn(" The container is provisioned but not grading; re-run `synkro install`.\n");
|
|
10432
|
+
return false;
|
|
10433
|
+
}
|
|
10434
|
+
}
|
|
10376
10435
|
function readPersistedDeployLocation() {
|
|
10377
10436
|
try {
|
|
10378
10437
|
if (existsSync11(CONFIG_PATH2)) {
|
|
@@ -10437,7 +10496,7 @@ async function reconcileDeployLocation() {
|
|
|
10437
10496
|
const sf = readFullSynkroFile();
|
|
10438
10497
|
const desired = sf?.grader.location === "cloud" ? "cloud" : "local";
|
|
10439
10498
|
const current = readPersistedDeployLocation();
|
|
10440
|
-
if (desired === current) return desired;
|
|
10499
|
+
if (desired === current) return { location: desired, changed: false };
|
|
10441
10500
|
const hasClaudeCode = sf ? sf.harness.includes("claude-code") : true;
|
|
10442
10501
|
const hasCursor = sf ? sf.harness.includes("cursor") : false;
|
|
10443
10502
|
console.log(`
|
|
@@ -10483,13 +10542,36 @@ synkro.toml: deploy location changed ${current} \u2192 ${desired}
|
|
|
10483
10542
|
await provisionCloudContainer({ gatewayUrl, jwt: token, orgId, userId, email, hasClaudeCode, hasCursor });
|
|
10484
10543
|
updateConfigEnvLocation("cloud");
|
|
10485
10544
|
await applyMcpConfig({ gatewayUrl, jwt: token, hasClaudeCode, hasCursor, local: false });
|
|
10545
|
+
await verifyCloudGrader(token);
|
|
10486
10546
|
console.log(" \u2713 grading + rules/MCP now served from Synkro Cloud\n");
|
|
10487
10547
|
} else {
|
|
10488
10548
|
updateConfigEnvLocation("local");
|
|
10489
10549
|
await applyMcpConfig({ gatewayUrl, jwt: token, hasClaudeCode, hasCursor, local: true });
|
|
10490
10550
|
console.log(" \u2713 switched to local \u2014 bringing the local grader + MCP up\n");
|
|
10491
10551
|
}
|
|
10492
|
-
return desired;
|
|
10552
|
+
return { location: desired, changed: true };
|
|
10553
|
+
}
|
|
10554
|
+
async function recycleCloudContainer() {
|
|
10555
|
+
if (!isAuthenticated()) {
|
|
10556
|
+
console.error("Not authenticated \u2014 run `synkro login`.");
|
|
10557
|
+
return;
|
|
10558
|
+
}
|
|
10559
|
+
await ensureValidToken();
|
|
10560
|
+
const token = getAccessToken();
|
|
10561
|
+
if (!token) {
|
|
10562
|
+
console.error("No access token \u2014 run `synkro login`.");
|
|
10563
|
+
return;
|
|
10564
|
+
}
|
|
10565
|
+
const base = (process.env.SYNKRO_CONTAINERS_URL || "https://containers.synkro.sh").replace(/\/$/, "");
|
|
10566
|
+
console.log("Synkro: recycling the cloud grader container (destroy \u2192 fresh boot)\u2026");
|
|
10567
|
+
try {
|
|
10568
|
+
const r = await fetch(`${base}/recycle`, { method: "POST", headers: { Authorization: `Bearer ${token}` }, signal: AbortSignal.timeout(3e4) });
|
|
10569
|
+
if (r.ok) console.log(" \u2713 old container destroyed");
|
|
10570
|
+
else console.warn(` \u26A0 recycle returned HTTP ${r.status} \u2014 continuing to warm anyway.`);
|
|
10571
|
+
} catch {
|
|
10572
|
+
console.warn(" \u26A0 recycle request failed (network) \u2014 continuing to warm anyway.");
|
|
10573
|
+
}
|
|
10574
|
+
await verifyCloudGrader(token);
|
|
10493
10575
|
}
|
|
10494
10576
|
function resolveDeploymentMode() {
|
|
10495
10577
|
const envOverride = process.env.SYNKRO_DEPLOYMENT_MODE?.toLowerCase();
|
|
@@ -10996,6 +11078,7 @@ async function installCommand(opts = {}) {
|
|
|
10996
11078
|
console.log();
|
|
10997
11079
|
} else if (target === "cloud-container") {
|
|
10998
11080
|
await provisionCloudContainer({ gatewayUrl, jwt: token, orgId, userId, email, hasClaudeCode, hasCursor });
|
|
11081
|
+
await verifyCloudGrader(token);
|
|
10999
11082
|
}
|
|
11000
11083
|
if (transcriptConsent) {
|
|
11001
11084
|
const repo = detectGitRepo2();
|
|
@@ -13539,9 +13622,9 @@ async function updateCommand() {
|
|
|
13539
13622
|
console.log("\nSynkro updated \u2014 now running the latest version.");
|
|
13540
13623
|
}
|
|
13541
13624
|
async function restartCommand(rest = []) {
|
|
13542
|
-
const location = await reconcileDeployLocation();
|
|
13625
|
+
const { location, changed } = await reconcileDeployLocation();
|
|
13543
13626
|
if (location === "cloud") {
|
|
13544
|
-
|
|
13627
|
+
if (!changed) await recycleCloudContainer();
|
|
13545
13628
|
return;
|
|
13546
13629
|
}
|
|
13547
13630
|
assertDockerAvailable();
|
|
@@ -13753,7 +13836,7 @@ var args = process.argv.slice(2);
|
|
|
13753
13836
|
var cmd = args[0] || "";
|
|
13754
13837
|
var subArgs = args.slice(1);
|
|
13755
13838
|
function printVersion() {
|
|
13756
|
-
console.log("1.6.
|
|
13839
|
+
console.log("1.6.62");
|
|
13757
13840
|
}
|
|
13758
13841
|
function printHelp2() {
|
|
13759
13842
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|