@synkro-sh/cli 1.6.69 → 1.6.70

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
@@ -6425,6 +6425,50 @@ function loadMcpJwt(): string {
6425
6425
  try { return readFileSync(join(HOME, '.synkro', '.mcp-jwt'), 'utf-8').trim(); } catch { return ''; }
6426
6426
  }
6427
6427
 
6428
+ let _cfgCache: Record<string, string> | null = null;
6429
+ function cfgVal(key: string): string {
6430
+ if (process.env[key]) return process.env[key] as string;
6431
+ if (!_cfgCache) {
6432
+ _cfgCache = {};
6433
+ try {
6434
+ const txt = readFileSync(join(HOME, '.synkro', 'config.env'), 'utf-8');
6435
+ for (const line of txt.split('\n')) {
6436
+ const mm = line.match(/^([A-Z0-9_]+)=['"]?(.*?)['"]?\s*$/);
6437
+ if (mm) _cfgCache[mm[1]] = mm[2];
6438
+ }
6439
+ } catch {}
6440
+ }
6441
+ return _cfgCache[key] || '';
6442
+ }
6443
+ function deployIsCloud(): boolean { return cfgVal('SYNKRO_DEPLOY_LOCATION') === 'cloud'; }
6444
+
6445
+ // Cloud MCP gate: in cloud mode the local server (:18931) isn't running, so the
6446
+ // gate asks the org policy directly (api /api/mcp/gate, authed by .mcp-jwt).
6447
+ // Fail-open by contract — any error or non-block verdict allows the call.
6448
+ async function cloudMcpGate(payload: any, harness: string): Promise<string> {
6449
+ try {
6450
+ const toolName = String(payload.tool_name || payload.tool || '');
6451
+ const parts = toolName.split('__');
6452
+ const server = (parts.length >= 3 && parts[0] === 'mcp') ? parts[1] : '';
6453
+ if (!server) return failOpen(harness);
6454
+ const base = (cfgVal('SYNKRO_GATEWAY_URL') || 'https://api.synkro.sh').replace(/\/+$/, '');
6455
+ const resp = await fetch(base + '/api/mcp/gate', {
6456
+ method: 'POST',
6457
+ headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + loadMcpJwt() },
6458
+ body: JSON.stringify({ server: server, tool: parts.slice(2).join('__') }),
6459
+ signal: AbortSignal.timeout(5000),
6460
+ });
6461
+ if (!resp.ok) return failOpen(harness);
6462
+ const d = await resp.json() as any;
6463
+ if (d && d.decision === 'block') {
6464
+ const reason = String(d.reason || ('MCP server "' + server + '" blocked by policy'));
6465
+ if (harness === 'cursor') return JSON.stringify({ permission: 'deny', user_message: reason });
6466
+ return JSON.stringify({ systemMessage: '[synkro:cloud:mcp] ' + reason, hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason, additionalContext: reason } });
6467
+ }
6468
+ return failOpen(harness);
6469
+ } catch { return failOpen(harness); }
6470
+ }
6471
+
6428
6472
  async function readStdin(): Promise<string> {
6429
6473
  const chunks: Buffer[] = [];
6430
6474
  for await (const chunk of process.stdin) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
@@ -6484,6 +6528,10 @@ export async function runStub(surface: string, opts: StubOpts = {}): Promise<voi
6484
6528
  if (!input.trim()) { out(failOpen(harness)); return; }
6485
6529
  const payload = JSON.parse(input);
6486
6530
 
6531
+ // Cloud MCP enforcement: the gate can't reach the local server, so decide
6532
+ // against the org policy directly. Fail-open inside cloudMcpGate.
6533
+ if (surface === 'mcp-gate' && deployIsCloud()) { out(await cloudMcpGate(payload, harness)); return; }
6534
+
6487
6535
  const workspaceRoots = Array.isArray(payload.workspace_roots) ? payload.workspace_roots : [];
6488
6536
  const cwd = (typeof payload.cwd === 'string' && payload.cwd) || workspaceRoots[0] || '';
6489
6537
  const sessionId = String(payload.session_id || payload.conversation_id || '');
@@ -10336,7 +10384,7 @@ function writeConfigEnv(opts) {
10336
10384
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
10337
10385
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
10338
10386
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
10339
- `SYNKRO_VERSION=${shellQuoteSingle("1.6.69")}`
10387
+ `SYNKRO_VERSION=${shellQuoteSingle("1.6.70")}`
10340
10388
  ];
10341
10389
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
10342
10390
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -13930,7 +13978,7 @@ var args = process.argv.slice(2);
13930
13978
  var cmd = args[0] || "";
13931
13979
  var subArgs = args.slice(1);
13932
13980
  function printVersion() {
13933
- console.log("1.6.69");
13981
+ console.log("1.6.70");
13934
13982
  }
13935
13983
  function printHelp2() {
13936
13984
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents