@synkro-sh/cli 1.4.73 → 1.4.74
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 +94 -37
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -536,21 +536,15 @@ function installMcpConfig(opts) {
|
|
|
536
536
|
if (entry?.[SYNKRO_MARKER3] === true) delete config.mcpServers[name];
|
|
537
537
|
}
|
|
538
538
|
if (opts.local) {
|
|
539
|
-
const
|
|
540
|
-
const tokenPath = join2(homedir3(), ".synkro", ".mcp-local-token");
|
|
541
|
-
let localToken = "";
|
|
542
|
-
try {
|
|
543
|
-
localToken = readFileSync3(tokenPath, "utf-8").trim();
|
|
544
|
-
} catch {
|
|
545
|
-
}
|
|
539
|
+
const proxyScript = join2(homedir3(), ".synkro", "hooks", "mcp-stdio-proxy.ts");
|
|
546
540
|
config.mcpServers[SYNKRO_SERVER_NAME] = {
|
|
547
|
-
type: "
|
|
548
|
-
|
|
549
|
-
|
|
541
|
+
type: "stdio",
|
|
542
|
+
command: "bun",
|
|
543
|
+
args: ["run", proxyScript],
|
|
550
544
|
[SYNKRO_MARKER3]: true
|
|
551
545
|
};
|
|
552
546
|
writeClaudeJsonAtomic(config);
|
|
553
|
-
return { path: CC_CONFIG_PATH, url:
|
|
547
|
+
return { path: CC_CONFIG_PATH, url: `stdio://${proxyScript}` };
|
|
554
548
|
}
|
|
555
549
|
const url = `${opts.gatewayUrl.replace(/\/$/, "")}/api/v1/mcp/guardrails`;
|
|
556
550
|
config.mcpServers[SYNKRO_SERVER_NAME] = {
|
|
@@ -612,15 +606,15 @@ function installCursorMcpConfig(opts) {
|
|
|
612
606
|
}
|
|
613
607
|
if (opts.local) {
|
|
614
608
|
const url2 = "http://127.0.0.1:8931/";
|
|
615
|
-
const
|
|
616
|
-
let
|
|
609
|
+
const jwtPath = join2(homedir3(), ".synkro", ".mcp-jwt");
|
|
610
|
+
let jwt2 = "";
|
|
617
611
|
try {
|
|
618
|
-
|
|
612
|
+
jwt2 = readFileSync3(jwtPath, "utf-8").trim();
|
|
619
613
|
} catch {
|
|
620
614
|
}
|
|
621
615
|
config.mcpServers[SYNKRO_SERVER_NAME] = {
|
|
622
616
|
url: url2,
|
|
623
|
-
...
|
|
617
|
+
...jwt2 ? { headers: { Authorization: `Bearer ${jwt2}` } } : {},
|
|
624
618
|
[SYNKRO_MARKER3]: true
|
|
625
619
|
};
|
|
626
620
|
writeCursorMcpJsonAtomic(config);
|
|
@@ -6267,6 +6261,7 @@ function writeHookScripts() {
|
|
|
6267
6261
|
const cursorBashJudgePath = join11(HOOKS_DIR, "cursor-bash-judge.ts");
|
|
6268
6262
|
const cursorEditCapturePath = join11(HOOKS_DIR, "cursor-edit-capture.ts");
|
|
6269
6263
|
const mcpLocalServerPath = join11(HOOKS_DIR, "mcp-local-server.ts");
|
|
6264
|
+
const mcpStdioProxyPath = join11(HOOKS_DIR, "mcp-stdio-proxy.ts");
|
|
6270
6265
|
writeFileSync7(bashScriptPath, BASH_JUDGE_TS, "utf-8");
|
|
6271
6266
|
writeFileSync7(bashFollowupScriptPath, BASH_FOLLOWUP_TS, "utf-8");
|
|
6272
6267
|
writeFileSync7(editPrecheckScriptPath, EDIT_PRECHECK_TS, "utf-8");
|
|
@@ -6292,30 +6287,17 @@ import { existsSync, readFileSync, writeFileSync, renameSync, appendFileSync, mk
|
|
|
6292
6287
|
import { homedir } from 'node:os';
|
|
6293
6288
|
import { join } from 'node:path';
|
|
6294
6289
|
|
|
6295
|
-
import { randomBytes } from 'node:crypto';
|
|
6296
|
-
|
|
6297
6290
|
const PORT = parseInt(process.env.SYNKRO_MCP_PORT || '8931', 10);
|
|
6298
6291
|
const HOME = homedir();
|
|
6299
6292
|
const RULES_PATH = join(HOME, '.synkro', 'rules.json');
|
|
6300
6293
|
const TELEMETRY_PATH = join(HOME, '.synkro', 'telemetry.jsonl');
|
|
6301
|
-
const
|
|
6294
|
+
const JWT_TOKEN_PATH = join(HOME, '.synkro', '.mcp-jwt');
|
|
6302
6295
|
|
|
6303
|
-
//
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
const token = randomBytes(32).toString('hex');
|
|
6309
|
-
mkdirSync(join(HOME, '.synkro'), { recursive: true });
|
|
6310
|
-
try {
|
|
6311
|
-
writeFileSync(TOKEN_PATH, token + '\\n', { mode: 0o600, flag: 'wx' });
|
|
6312
|
-
return token;
|
|
6313
|
-
} catch {
|
|
6314
|
-
return readFileSync(TOKEN_PATH, 'utf-8').trim();
|
|
6315
|
-
}
|
|
6316
|
-
}
|
|
6317
|
-
|
|
6318
|
-
const SERVER_TOKEN = getOrCreateToken();
|
|
6296
|
+
// Synkro-signed long-lived JWT \u2014 minted during \`synkro install\`, required on all POST requests.
|
|
6297
|
+
// If missing, the server still starts (for GET health checks) but rejects all tool calls.
|
|
6298
|
+
let SERVER_TOKEN = '';
|
|
6299
|
+
try { SERVER_TOKEN = readFileSync(JWT_TOKEN_PATH, 'utf-8').trim(); } catch {}
|
|
6300
|
+
if (!SERVER_TOKEN) console.warn('[synkro] \u26A0 No MCP JWT found \u2014 run \`synkro install\` to authenticate.');
|
|
6319
6301
|
const MAX_BODY_BYTES = 1_048_576;
|
|
6320
6302
|
|
|
6321
6303
|
let _writeLock: Promise<void> = Promise.resolve();
|
|
@@ -7370,6 +7352,7 @@ const server = Bun.serve({
|
|
|
7370
7352
|
|
|
7371
7353
|
console.log(\`[synkro] local MCP guardrails server listening on http://127.0.0.1:\${server.port}\`);
|
|
7372
7354
|
`, "utf-8");
|
|
7355
|
+
writeFileSync7(mcpStdioProxyPath, MCP_STDIO_PROXY_SRC, "utf-8");
|
|
7373
7356
|
chmodSync2(bashScriptPath, 493);
|
|
7374
7357
|
chmodSync2(bashFollowupScriptPath, 493);
|
|
7375
7358
|
chmodSync2(editPrecheckScriptPath, 493);
|
|
@@ -7386,6 +7369,7 @@ console.log(\`[synkro] local MCP guardrails server listening on http://127.0.0.1
|
|
|
7386
7369
|
chmodSync2(cursorBashJudgePath, 493);
|
|
7387
7370
|
chmodSync2(cursorEditCapturePath, 493);
|
|
7388
7371
|
chmodSync2(mcpLocalServerPath, 493);
|
|
7372
|
+
chmodSync2(mcpStdioProxyPath, 493);
|
|
7389
7373
|
return {
|
|
7390
7374
|
bashScript: bashScriptPath,
|
|
7391
7375
|
bashFollowupScript: bashFollowupScriptPath,
|
|
@@ -7432,7 +7416,7 @@ function writeConfigEnv(opts) {
|
|
|
7432
7416
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
7433
7417
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
7434
7418
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
7435
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.4.
|
|
7419
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.4.74")}`
|
|
7436
7420
|
];
|
|
7437
7421
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
7438
7422
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
@@ -7915,7 +7899,20 @@ async function installCommand(opts = {}) {
|
|
|
7915
7899
|
if (hasClaudeCode && !opts.noMcp) {
|
|
7916
7900
|
if (useLocalMcp) {
|
|
7917
7901
|
try {
|
|
7918
|
-
const
|
|
7902
|
+
const mintResp = await fetch(`${gatewayUrl}/api/v1/cli/mcp-token`, {
|
|
7903
|
+
method: "POST",
|
|
7904
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
|
|
7905
|
+
body: "{}"
|
|
7906
|
+
});
|
|
7907
|
+
let mcpJwt = "";
|
|
7908
|
+
if (mintResp.ok) {
|
|
7909
|
+
const minted = await mintResp.json();
|
|
7910
|
+
mcpJwt = minted.token;
|
|
7911
|
+
writeFileSync7(join11(SYNKRO_DIR2, ".mcp-jwt"), mcpJwt + "\n", { mode: 384 });
|
|
7912
|
+
} else {
|
|
7913
|
+
console.warn(" \u26A0 Could not mint MCP token \u2014 local server will reject requests until re-installed.");
|
|
7914
|
+
}
|
|
7915
|
+
const mcp = installMcpConfig({ gatewayUrl, bearerToken: mcpJwt, local: true });
|
|
7919
7916
|
console.log(`Registered local MCP guardrails server in ${mcp.path}`);
|
|
7920
7917
|
console.log(` url: ${mcp.url}`);
|
|
7921
7918
|
console.log(" (rules stored in ~/.synkro/rules.json)");
|
|
@@ -7942,6 +7939,7 @@ async function installCommand(opts = {}) {
|
|
|
7942
7939
|
throw new Error(`mcp-token mint failed (${mintResp.status}): ${errText.slice(0, 200)}`);
|
|
7943
7940
|
}
|
|
7944
7941
|
const minted = await mintResp.json();
|
|
7942
|
+
writeFileSync7(join11(SYNKRO_DIR2, ".mcp-jwt"), minted.token + "\n", { mode: 384 });
|
|
7945
7943
|
const mcp = installMcpConfig({ gatewayUrl, bearerToken: minted.token });
|
|
7946
7944
|
console.log(`Registered Synkro guardrails MCP server in ${mcp.path}`);
|
|
7947
7945
|
console.log(` url: ${mcp.url}`);
|
|
@@ -7958,6 +7956,18 @@ async function installCommand(opts = {}) {
|
|
|
7958
7956
|
if (hasCursor && !opts.noMcp) {
|
|
7959
7957
|
try {
|
|
7960
7958
|
if (useLocalMcp) {
|
|
7959
|
+
const jwtPath = join11(SYNKRO_DIR2, ".mcp-jwt");
|
|
7960
|
+
if (!existsSync10(jwtPath)) {
|
|
7961
|
+
const mintResp = await fetch(`${gatewayUrl}/api/v1/cli/mcp-token`, {
|
|
7962
|
+
method: "POST",
|
|
7963
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
|
|
7964
|
+
body: "{}"
|
|
7965
|
+
});
|
|
7966
|
+
if (mintResp.ok) {
|
|
7967
|
+
const minted = await mintResp.json();
|
|
7968
|
+
writeFileSync7(jwtPath, minted.token + "\n", { mode: 384 });
|
|
7969
|
+
}
|
|
7970
|
+
}
|
|
7961
7971
|
const mcp = installCursorMcpConfig({ gatewayUrl, bearerToken: "", local: true });
|
|
7962
7972
|
console.log(`Registered local MCP guardrails server in ${mcp.path}`);
|
|
7963
7973
|
console.log(` url: ${mcp.url}`);
|
|
@@ -7975,6 +7985,7 @@ async function installCommand(opts = {}) {
|
|
|
7975
7985
|
throw new Error(`mcp-token mint failed (${mintResp.status}): ${errText.slice(0, 200)}`);
|
|
7976
7986
|
}
|
|
7977
7987
|
const minted = await mintResp.json();
|
|
7988
|
+
writeFileSync7(join11(SYNKRO_DIR2, ".mcp-jwt"), minted.token + "\n", { mode: 384 });
|
|
7978
7989
|
const mcp = installCursorMcpConfig({ gatewayUrl, bearerToken: minted.token });
|
|
7979
7990
|
console.log(`Registered Synkro guardrails MCP server in ${mcp.path}`);
|
|
7980
7991
|
console.log(` url: ${mcp.url}`);
|
|
@@ -8314,7 +8325,7 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
|
|
|
8314
8325
|
}
|
|
8315
8326
|
return { sessions: totalSessions, messages: totalMessages };
|
|
8316
8327
|
}
|
|
8317
|
-
var SYNKRO_DIR2, HOOKS_DIR, BIN_DIR, CONFIG_PATH3, OFFSETS_DIR, RULES_PATH, MCP_LOCAL_PORT;
|
|
8328
|
+
var SYNKRO_DIR2, HOOKS_DIR, BIN_DIR, CONFIG_PATH3, MCP_STDIO_PROXY_SRC, OFFSETS_DIR, RULES_PATH, MCP_LOCAL_PORT;
|
|
8318
8329
|
var init_install2 = __esm({
|
|
8319
8330
|
"cli/commands/install.ts"() {
|
|
8320
8331
|
"use strict";
|
|
@@ -8337,6 +8348,52 @@ var init_install2 = __esm({
|
|
|
8337
8348
|
HOOKS_DIR = join11(SYNKRO_DIR2, "hooks");
|
|
8338
8349
|
BIN_DIR = join11(SYNKRO_DIR2, "bin");
|
|
8339
8350
|
CONFIG_PATH3 = join11(SYNKRO_DIR2, "config.env");
|
|
8351
|
+
MCP_STDIO_PROXY_SRC = `#!/usr/bin/env bun
|
|
8352
|
+
import { readFileSync } from 'node:fs';
|
|
8353
|
+
import { homedir } from 'node:os';
|
|
8354
|
+
import { join } from 'node:path';
|
|
8355
|
+
import { createInterface } from 'node:readline';
|
|
8356
|
+
|
|
8357
|
+
const HOME = homedir();
|
|
8358
|
+
const TOKEN_PATH = join(HOME, '.synkro', '.mcp-jwt');
|
|
8359
|
+
const PORT = parseInt(process.env.SYNKRO_MCP_PORT || '8931', 10);
|
|
8360
|
+
const URL = \`http://127.0.0.1:\${PORT}\`;
|
|
8361
|
+
|
|
8362
|
+
let token = '';
|
|
8363
|
+
try { token = readFileSync(TOKEN_PATH, 'utf-8').trim(); } catch {}
|
|
8364
|
+
|
|
8365
|
+
const rl = createInterface({ input: process.stdin, terminal: false });
|
|
8366
|
+
|
|
8367
|
+
rl.on('line', async (line) => {
|
|
8368
|
+
if (!line.trim()) return;
|
|
8369
|
+
let msg;
|
|
8370
|
+
try { msg = JSON.parse(line); } catch { return; }
|
|
8371
|
+
if (!msg.id && msg.method?.startsWith('notifications/')) return;
|
|
8372
|
+
|
|
8373
|
+
try {
|
|
8374
|
+
const resp = await fetch(URL, {
|
|
8375
|
+
method: 'POST',
|
|
8376
|
+
headers: {
|
|
8377
|
+
'Content-Type': 'application/json',
|
|
8378
|
+
'Authorization': \`Bearer \${token}\`,
|
|
8379
|
+
},
|
|
8380
|
+
body: line,
|
|
8381
|
+
signal: AbortSignal.timeout(30000),
|
|
8382
|
+
});
|
|
8383
|
+
if (resp.status === 204) return;
|
|
8384
|
+
const body = await resp.text();
|
|
8385
|
+
process.stdout.write(body + '\\n');
|
|
8386
|
+
} catch (err) {
|
|
8387
|
+
if (msg.id != null) {
|
|
8388
|
+
process.stdout.write(JSON.stringify({
|
|
8389
|
+
jsonrpc: '2.0',
|
|
8390
|
+
id: msg.id,
|
|
8391
|
+
error: { code: -32603, message: 'MCP proxy: HTTP server unreachable' },
|
|
8392
|
+
}) + '\\n');
|
|
8393
|
+
}
|
|
8394
|
+
}
|
|
8395
|
+
});
|
|
8396
|
+
`;
|
|
8340
8397
|
OFFSETS_DIR = join11(SYNKRO_DIR2, ".transcript-offsets");
|
|
8341
8398
|
RULES_PATH = join11(SYNKRO_DIR2, "rules.json");
|
|
8342
8399
|
MCP_LOCAL_PORT = 8931;
|