@synkro-sh/cli 1.6.69 → 1.6.71
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 +99 -10
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
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 || '');
|
|
@@ -9639,11 +9687,12 @@ function captureClaudeSetupToken() {
|
|
|
9639
9687
|
reject(new Error(`claude setup-token exited with code ${code}`));
|
|
9640
9688
|
return;
|
|
9641
9689
|
}
|
|
9690
|
+
const stripToToken = (s) => s.replace(/\x1B\[[0-9;?]*[a-zA-Z]/g, "").replace(/\x1B\][^\x07]*\x07/g, "").replace(/[\s\x00-\x1F\x7F]/g, "");
|
|
9642
9691
|
const yellowRe = /\x1B\[38;2;255;193;7m([^\x1B]*)/g;
|
|
9643
9692
|
let yellow = "";
|
|
9644
9693
|
let m;
|
|
9645
9694
|
while ((m = yellowRe.exec(raw)) !== null) yellow += m[1];
|
|
9646
|
-
const
|
|
9695
|
+
const yToken = stripToToken(yellow);
|
|
9647
9696
|
let lineMatch = "";
|
|
9648
9697
|
const clean = raw.replace(/\x1B\[[0-9;?]*[a-zA-Z]/g, "").replace(/\x1B\][^\x07]*\x07/g, "");
|
|
9649
9698
|
const lines = clean.split("\n").map((l) => l.trim());
|
|
@@ -9655,13 +9704,13 @@ function captureClaudeSetupToken() {
|
|
|
9655
9704
|
lineMatch = tok;
|
|
9656
9705
|
break;
|
|
9657
9706
|
}
|
|
9658
|
-
const
|
|
9659
|
-
const
|
|
9660
|
-
if (!
|
|
9661
|
-
reject(new Error(`Could not
|
|
9707
|
+
const valid = (t) => /^sk-ant-oat01-[A-Za-z0-9_-]{40,}$/.test(t);
|
|
9708
|
+
const token = valid(yToken) ? yToken : valid(lineMatch) ? lineMatch : "";
|
|
9709
|
+
if (!token) {
|
|
9710
|
+
reject(new Error(`Could not capture a full token from claude setup-token output (file=${raw.length}b, yellow=${yellow.length}b, yToken=${yToken.length}b)`));
|
|
9662
9711
|
return;
|
|
9663
9712
|
}
|
|
9664
|
-
resolve4(
|
|
9713
|
+
resolve4(token);
|
|
9665
9714
|
});
|
|
9666
9715
|
});
|
|
9667
9716
|
}
|
|
@@ -10005,6 +10054,7 @@ var init_setupGithub = __esm({
|
|
|
10005
10054
|
var install_exports = {};
|
|
10006
10055
|
__export(install_exports, {
|
|
10007
10056
|
detectGitRepo: () => detectGitRepo2,
|
|
10057
|
+
getOrMintCloudToken: () => getOrMintCloudToken,
|
|
10008
10058
|
installCommand: () => installCommand,
|
|
10009
10059
|
parseArgs: () => parseArgs,
|
|
10010
10060
|
reconcileDeployLocation: () => reconcileDeployLocation,
|
|
@@ -10336,7 +10386,7 @@ function writeConfigEnv(opts) {
|
|
|
10336
10386
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
10337
10387
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
10338
10388
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
10339
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.6.
|
|
10389
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.6.71")}`
|
|
10340
10390
|
];
|
|
10341
10391
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
10342
10392
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
@@ -10356,6 +10406,38 @@ function writeConfigEnv(opts) {
|
|
|
10356
10406
|
writeFileSync8(CONFIG_PATH2, lines.join("\n"), "utf-8");
|
|
10357
10407
|
chmodSync2(CONFIG_PATH2, 384);
|
|
10358
10408
|
}
|
|
10409
|
+
function jwtExpired(token) {
|
|
10410
|
+
try {
|
|
10411
|
+
const p = JSON.parse(Buffer.from(token.split(".")[1], "base64url").toString("utf-8"));
|
|
10412
|
+
if (!p?.exp) return true;
|
|
10413
|
+
return Date.now() > p.exp * 1e3 - 5 * 60 * 1e3;
|
|
10414
|
+
} catch {
|
|
10415
|
+
return true;
|
|
10416
|
+
}
|
|
10417
|
+
}
|
|
10418
|
+
async function getOrMintCloudToken(gatewayUrl) {
|
|
10419
|
+
assertGatewayAllowed(gatewayUrl);
|
|
10420
|
+
let stored = "";
|
|
10421
|
+
try {
|
|
10422
|
+
stored = readFileSync10(CLOUD_JWT_PATH, "utf-8").trim();
|
|
10423
|
+
} catch {
|
|
10424
|
+
}
|
|
10425
|
+
if (stored && !jwtExpired(stored)) return stored;
|
|
10426
|
+
await refreshToken().catch(() => false);
|
|
10427
|
+
const jwt2 = getAccessToken();
|
|
10428
|
+
if (!jwt2) throw new Error("Not authenticated \u2014 run `synkro login`");
|
|
10429
|
+
const resp = await fetch(`${gatewayUrl}/api/v1/cli/cloud-token`, {
|
|
10430
|
+
method: "POST",
|
|
10431
|
+
headers: { Authorization: `Bearer ${jwt2}`, "Content-Type": "application/json" }
|
|
10432
|
+
});
|
|
10433
|
+
if (!resp.ok) {
|
|
10434
|
+
const t = await resp.text().catch(() => "");
|
|
10435
|
+
throw new Error(`cloud-token mint failed (${resp.status}): ${t.slice(0, 200)}`);
|
|
10436
|
+
}
|
|
10437
|
+
const { token } = await resp.json();
|
|
10438
|
+
writeFileSync8(CLOUD_JWT_PATH, token + "\n", { mode: 384 });
|
|
10439
|
+
return token;
|
|
10440
|
+
}
|
|
10359
10441
|
async function provisionCloudContainer(opts) {
|
|
10360
10442
|
let setupToken;
|
|
10361
10443
|
try {
|
|
@@ -10382,10 +10464,16 @@ async function provisionCloudContainer(opts) {
|
|
|
10382
10464
|
} catch {
|
|
10383
10465
|
}
|
|
10384
10466
|
}
|
|
10467
|
+
let cloudJwt = opts.jwt;
|
|
10468
|
+
try {
|
|
10469
|
+
cloudJwt = await getOrMintCloudToken(opts.gatewayUrl);
|
|
10470
|
+
} catch (e) {
|
|
10471
|
+
console.warn(` (cloud token unavailable, using session token: ${e.message})`);
|
|
10472
|
+
}
|
|
10385
10473
|
try {
|
|
10386
10474
|
const resp = await fetch(`${opts.gatewayUrl}/api/v1/cli/cloud-provision`, {
|
|
10387
10475
|
method: "POST",
|
|
10388
|
-
headers: { "Authorization": `Bearer ${
|
|
10476
|
+
headers: { "Authorization": `Bearer ${cloudJwt}`, "Content-Type": "application/json" },
|
|
10389
10477
|
body: JSON.stringify({
|
|
10390
10478
|
org_id: opts.orgId,
|
|
10391
10479
|
user_id: opts.userId,
|
|
@@ -11823,7 +11911,7 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
|
|
|
11823
11911
|
}
|
|
11824
11912
|
return { sessions: totalSessions, messages: totalMessages };
|
|
11825
11913
|
}
|
|
11826
|
-
var SYNKRO_DIR5, HOOKS_DIR, BIN_DIR, CONFIG_PATH2, MCP_STDIO_PROXY_SRC, OFFSETS_DIR;
|
|
11914
|
+
var SYNKRO_DIR5, HOOKS_DIR, BIN_DIR, CONFIG_PATH2, MCP_STDIO_PROXY_SRC, OFFSETS_DIR, CLOUD_JWT_PATH;
|
|
11827
11915
|
var init_install = __esm({
|
|
11828
11916
|
"cli/commands/install.ts"() {
|
|
11829
11917
|
"use strict";
|
|
@@ -11957,6 +12045,7 @@ rl.on('line', async (line) => {
|
|
|
11957
12045
|
});
|
|
11958
12046
|
`;
|
|
11959
12047
|
OFFSETS_DIR = join10(SYNKRO_DIR5, ".transcript-offsets");
|
|
12048
|
+
CLOUD_JWT_PATH = join10(SYNKRO_DIR5, ".cloud-jwt");
|
|
11960
12049
|
}
|
|
11961
12050
|
});
|
|
11962
12051
|
|
|
@@ -13930,7 +14019,7 @@ var args = process.argv.slice(2);
|
|
|
13930
14019
|
var cmd = args[0] || "";
|
|
13931
14020
|
var subArgs = args.slice(1);
|
|
13932
14021
|
function printVersion() {
|
|
13933
|
-
console.log("1.6.
|
|
14022
|
+
console.log("1.6.71");
|
|
13934
14023
|
}
|
|
13935
14024
|
function printHelp2() {
|
|
13936
14025
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|