@synkro-sh/cli 1.6.59 → 1.6.60

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 CHANGED
@@ -9977,6 +9977,7 @@ __export(install_exports, {
9977
9977
  detectGitRepo: () => detectGitRepo2,
9978
9978
  installCommand: () => installCommand,
9979
9979
  parseArgs: () => parseArgs,
9980
+ reconcileDeployLocation: () => reconcileDeployLocation,
9980
9981
  reconcileHarness: () => reconcileHarness,
9981
9982
  syncSkillFiles: () => syncSkillFiles,
9982
9983
  writeHookScripts: () => writeHookScripts
@@ -10304,7 +10305,7 @@ function writeConfigEnv(opts) {
10304
10305
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
10305
10306
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
10306
10307
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
10307
- `SYNKRO_VERSION=${shellQuoteSingle("1.6.59")}`
10308
+ `SYNKRO_VERSION=${shellQuoteSingle("1.6.60")}`
10308
10309
  ];
10309
10310
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
10310
10311
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -10372,6 +10373,124 @@ async function provisionCloudContainer(opts) {
10372
10373
  setupToken = "";
10373
10374
  }
10374
10375
  }
10376
+ function readPersistedDeployLocation() {
10377
+ try {
10378
+ if (existsSync11(CONFIG_PATH2)) {
10379
+ const m = readFileSync10(CONFIG_PATH2, "utf-8").match(/^SYNKRO_DEPLOY_LOCATION='([^']*)'/m);
10380
+ if (m?.[1] === "cloud") return "cloud";
10381
+ }
10382
+ } catch {
10383
+ }
10384
+ return "local";
10385
+ }
10386
+ function updateConfigEnvLocation(location) {
10387
+ if (!existsSync11(CONFIG_PATH2)) return;
10388
+ let env = readFileSync10(CONFIG_PATH2, "utf-8");
10389
+ const set = (k, v) => {
10390
+ const re = new RegExp(`^${k}=.*$`, "m");
10391
+ const line = `${k}='${v}'`;
10392
+ env = re.test(env) ? env.replace(re, line) : `${env.replace(/\n?$/, "\n")}${line}`;
10393
+ };
10394
+ set("SYNKRO_DEPLOY_LOCATION", location);
10395
+ set("SYNKRO_STORAGE_MODE", location === "cloud" ? "cloud" : "local");
10396
+ writeFileSync8(CONFIG_PATH2, env, "utf-8");
10397
+ chmodSync2(CONFIG_PATH2, 384);
10398
+ }
10399
+ async function applyMcpConfig(opts) {
10400
+ if (!opts.hasClaudeCode && !opts.hasCursor) return;
10401
+ let mcpJwt = "";
10402
+ try {
10403
+ mcpJwt = readFileSync10(join10(SYNKRO_DIR5, ".mcp-jwt"), "utf-8").trim();
10404
+ } catch {
10405
+ }
10406
+ if (!mcpJwt) {
10407
+ try {
10408
+ const r = await fetch(`${opts.gatewayUrl}/api/v1/cli/mcp-token`, {
10409
+ method: "POST",
10410
+ headers: { Authorization: `Bearer ${opts.jwt}`, "Content-Type": "application/json" },
10411
+ body: "{}"
10412
+ });
10413
+ if (r.ok) mcpJwt = (await r.json()).token;
10414
+ else console.warn(` \u26A0 MCP token mint failed (${r.status}) \u2014 run \`synkro install\` to set MCP up.`);
10415
+ } catch {
10416
+ console.warn(" \u26A0 MCP token mint failed (network) \u2014 run `synkro install` to set MCP up.");
10417
+ }
10418
+ }
10419
+ if (opts.hasClaudeCode) {
10420
+ try {
10421
+ const mcp = installMcpConfig({ gatewayUrl: opts.gatewayUrl, bearerToken: mcpJwt, local: opts.local });
10422
+ console.log(` \u2713 Claude Code MCP \u2192 ${mcp.url}`);
10423
+ } catch (err) {
10424
+ console.warn(` \u26A0 Claude Code MCP config failed: ${err.message}`);
10425
+ }
10426
+ }
10427
+ if (opts.hasCursor) {
10428
+ try {
10429
+ const mcp = installCursorMcpConfig({ gatewayUrl: opts.gatewayUrl, bearerToken: opts.local ? "" : mcpJwt, local: opts.local });
10430
+ console.log(` \u2713 Cursor MCP \u2192 ${mcp.url}`);
10431
+ } catch (err) {
10432
+ console.warn(` \u26A0 Cursor MCP config failed: ${err.message}`);
10433
+ }
10434
+ }
10435
+ }
10436
+ async function reconcileDeployLocation() {
10437
+ const sf = readFullSynkroFile();
10438
+ const desired = sf?.grader.location === "cloud" ? "cloud" : "local";
10439
+ const current = readPersistedDeployLocation();
10440
+ if (desired === current) return desired;
10441
+ const hasClaudeCode = sf ? sf.harness.includes("claude-code") : true;
10442
+ const hasCursor = sf ? sf.harness.includes("cursor") : false;
10443
+ console.log(`
10444
+ synkro.toml: deploy location changed ${current} \u2192 ${desired}
10445
+ `);
10446
+ if (!isAuthenticated()) {
10447
+ console.log(" Opening browser for Synkro auth...");
10448
+ const ok = await authenticate(() => {
10449
+ });
10450
+ if (!ok) {
10451
+ console.error(" \u2717 authentication failed");
10452
+ process.exit(1);
10453
+ }
10454
+ }
10455
+ await ensureValidToken();
10456
+ const token = getAccessToken();
10457
+ if (!token) {
10458
+ console.error(" \u2717 no access token \u2014 run `synkro login`");
10459
+ process.exit(1);
10460
+ }
10461
+ const gatewayUrl = sanitizeGatewayCandidate(process.env.SYNKRO_GATEWAY_URL) || "https://api.synkro.sh";
10462
+ try {
10463
+ assertGatewayAllowed(gatewayUrl);
10464
+ } catch (err) {
10465
+ console.error(err.message);
10466
+ process.exit(1);
10467
+ }
10468
+ if (desired === "cloud") {
10469
+ if (dockerStatus().running) {
10470
+ console.log(" Stopping local grader container (snapshotting state first)...");
10471
+ await dockerSafeStop();
10472
+ }
10473
+ dockerRemove();
10474
+ console.log(" \u2713 local container stopped + removed");
10475
+ let orgId, userId, email;
10476
+ try {
10477
+ const info = getUserInfo();
10478
+ userId = info.id;
10479
+ orgId = info.org_id;
10480
+ email = info.email;
10481
+ } catch {
10482
+ }
10483
+ await provisionCloudContainer({ gatewayUrl, jwt: token, orgId, userId, email, hasClaudeCode, hasCursor });
10484
+ updateConfigEnvLocation("cloud");
10485
+ await applyMcpConfig({ gatewayUrl, jwt: token, hasClaudeCode, hasCursor, local: false });
10486
+ console.log(" \u2713 grading + rules/MCP now served from Synkro Cloud\n");
10487
+ } else {
10488
+ updateConfigEnvLocation("local");
10489
+ await applyMcpConfig({ gatewayUrl, jwt: token, hasClaudeCode, hasCursor, local: true });
10490
+ console.log(" \u2713 switched to local \u2014 bringing the local grader + MCP up\n");
10491
+ }
10492
+ return desired;
10493
+ }
10375
10494
  function resolveDeploymentMode() {
10376
10495
  const envOverride = process.env.SYNKRO_DEPLOYMENT_MODE?.toLowerCase();
10377
10496
  if (envOverride === "bare-host" || envOverride === "docker") return envOverride;
@@ -13420,6 +13539,11 @@ async function updateCommand() {
13420
13539
  console.log("\nSynkro updated \u2014 now running the latest version.");
13421
13540
  }
13422
13541
  async function restartCommand(rest = []) {
13542
+ const location = await reconcileDeployLocation();
13543
+ if (location === "cloud") {
13544
+ console.log("Synkro: grading runs on Synkro Cloud \u2014 no local container to restart.");
13545
+ return;
13546
+ }
13423
13547
  assertDockerAvailable();
13424
13548
  const cfg = resolveWorkerConfig(rest);
13425
13549
  let claudeWorkers = cfg.claudeWorkers;
@@ -13629,7 +13753,7 @@ var args = process.argv.slice(2);
13629
13753
  var cmd = args[0] || "";
13630
13754
  var subArgs = args.slice(1);
13631
13755
  function printVersion() {
13632
- console.log("1.6.59");
13756
+ console.log("1.6.60");
13633
13757
  }
13634
13758
  function printHelp2() {
13635
13759
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents