@synkro-sh/cli 1.7.93 → 1.7.94

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
@@ -147,7 +147,7 @@ function getIdentity() {
147
147
  if (cached2) return cached2;
148
148
  let cliVersion = "0.0.0";
149
149
  try {
150
- cliVersion = "1.7.93";
150
+ cliVersion = "1.7.94";
151
151
  } catch {
152
152
  }
153
153
  const creds = loadCredentialsIdentity();
@@ -2147,7 +2147,7 @@ var init_codexHookConfig = __esm({
2147
2147
 
2148
2148
  // cli/installer/codexHookTrust.ts
2149
2149
  import { spawn as spawn2 } from "child_process";
2150
- function parseCodexHookTrustOutput(stdout) {
2150
+ function parseCodexHookTrustRecords(stdout) {
2151
2151
  for (const line of stdout.split("\n")) {
2152
2152
  let message;
2153
2153
  try {
@@ -2159,20 +2159,41 @@ function parseCodexHookTrustOutput(stdout) {
2159
2159
  if (!Array.isArray(data)) continue;
2160
2160
  const hooks = data.flatMap((entry) => Array.isArray(entry?.hooks) ? entry.hooks : []).filter((hook) => typeof hook.command === "string" && isSynkroHookCommand(hook.command));
2161
2161
  if (!hooks.length) return null;
2162
- return {
2163
- total: hooks.length,
2164
- trusted: hooks.filter((hook) => hook.trustStatus === "trusted" || hook.trustStatus === "managed").length,
2165
- needsReview: hooks.filter((hook) => hook.trustStatus === "modified" || hook.trustStatus === "untrusted").length,
2166
- disabled: hooks.filter((hook) => hook.enabled === false).length
2167
- };
2162
+ return hooks;
2168
2163
  }
2169
2164
  return null;
2170
2165
  }
2171
- function inspectCodexHookTrust(codexBinary = "codex", cwd = process.cwd()) {
2166
+ function summarizeCodexHookTrust(hooks) {
2167
+ return {
2168
+ total: hooks.length,
2169
+ trusted: hooks.filter((hook) => hook.trustStatus === "trusted" || hook.trustStatus === "managed").length,
2170
+ needsReview: hooks.filter((hook) => hook.trustStatus === "modified" || hook.trustStatus === "untrusted").length,
2171
+ disabled: hooks.filter((hook) => hook.enabled === false).length
2172
+ };
2173
+ }
2174
+ function parseCodexHookTrustOutput(stdout) {
2175
+ const hooks = parseCodexHookTrustRecords(stdout);
2176
+ return hooks ? summarizeCodexHookTrust(hooks) : null;
2177
+ }
2178
+ function buildCodexHookTrustEdits(hooks) {
2179
+ const edits = /* @__PURE__ */ new Map();
2180
+ for (const hook of hooks) {
2181
+ if (typeof hook.command !== "string" || !isSynkroHookCommand(hook.command) || typeof hook.key !== "string" || !hook.key || typeof hook.currentHash !== "string" || !/^sha256:[0-9a-f]{64}$/i.test(hook.currentHash)) continue;
2182
+ const edit = {
2183
+ keyPath: `hooks.state.${JSON.stringify(hook.key)}.trusted_hash`,
2184
+ value: hook.currentHash,
2185
+ mergeStrategy: "upsert"
2186
+ };
2187
+ edits.set(edit.keyPath, edit);
2188
+ }
2189
+ return [...edits.values()];
2190
+ }
2191
+ function queryCodexHookTrust(codexBinary = "codex", cwd = process.cwd(), autoTrust = false) {
2172
2192
  return new Promise((resolve6) => {
2173
2193
  let settled = false;
2174
2194
  let stdout = "";
2175
2195
  let pending = "";
2196
+ let hooks = null;
2176
2197
  let child;
2177
2198
  const finish = (summary) => {
2178
2199
  if (settled) return;
@@ -2217,7 +2238,32 @@ function inspectCodexHookTrust(codexBinary = "codex", cwd = process.cwd()) {
2217
2238
  params: { cwds: [cwd] }
2218
2239
  }) + "\n");
2219
2240
  } else if (message?.id === 2) {
2220
- finish(parseCodexHookTrustOutput(line));
2241
+ hooks = parseCodexHookTrustRecords(line);
2242
+ if (!hooks || !autoTrust) {
2243
+ finish(hooks ? summarizeCodexHookTrust(hooks) : null);
2244
+ continue;
2245
+ }
2246
+ const edits = buildCodexHookTrustEdits(hooks);
2247
+ if (!edits.length) {
2248
+ finish(summarizeCodexHookTrust(hooks));
2249
+ continue;
2250
+ }
2251
+ child.stdin.write(JSON.stringify({
2252
+ id: 3,
2253
+ method: "config/batchWrite",
2254
+ params: { edits, reloadUserConfig: true }
2255
+ }) + "\n");
2256
+ } else if (message?.id === 3) {
2257
+ if (message?.result?.status !== "ok" || !hooks) {
2258
+ finish(hooks ? summarizeCodexHookTrust(hooks) : null);
2259
+ continue;
2260
+ }
2261
+ finish({
2262
+ total: hooks.length,
2263
+ trusted: hooks.length,
2264
+ needsReview: 0,
2265
+ disabled: hooks.filter((hook) => hook.enabled === false).length
2266
+ });
2221
2267
  }
2222
2268
  }
2223
2269
  });
@@ -2234,6 +2280,9 @@ function inspectCodexHookTrust(codexBinary = "codex", cwd = process.cwd()) {
2234
2280
  }
2235
2281
  });
2236
2282
  }
2283
+ function trustCodexHooks(codexBinary = "codex", cwd = process.cwd()) {
2284
+ return queryCodexHookTrust(codexBinary, cwd, true);
2285
+ }
2237
2286
  function codexHookTrustLines(summary) {
2238
2287
  if (!summary) {
2239
2288
  return [
@@ -2254,7 +2303,7 @@ function codexHookTrustLines(summary) {
2254
2303
  ];
2255
2304
  }
2256
2305
  async function reportCodexHookTrust(codexBinary, cwd) {
2257
- const summary = await inspectCodexHookTrust(codexBinary || "codex", cwd);
2306
+ const summary = await trustCodexHooks(codexBinary || "codex", cwd);
2258
2307
  for (const line of codexHookTrustLines(summary)) console.log(line);
2259
2308
  return summary;
2260
2309
  }
@@ -2428,9 +2477,54 @@ function removeCodexManagedBlock(content, path) {
2428
2477
  if (inside) throw new Error(`Incomplete Synkro MCP marker block in ${path}`);
2429
2478
  return { content: out.join("\n").replace(/\n{3,}$/g, "\n\n"), removed };
2430
2479
  }
2431
- function hasUnmanagedCodexEntry(content) {
2432
- const section = /^\s*\[\s*mcp_servers\s*\.\s*(?:"synkro-guardrails"|'synkro-guardrails'|synkro-guardrails)\s*\]\s*(?:#.*)?$/m;
2433
- return section.test(content);
2480
+ function findCodexMcpSection(content) {
2481
+ const section = /^\s*\[\s*mcp_servers\s*\.\s*(?:"synkro-guardrails"|'synkro-guardrails'|synkro-guardrails)\s*\]\s*(?:#.*)?$/gm;
2482
+ const match = section.exec(content);
2483
+ if (!match) return null;
2484
+ const nextSection = /^\s*\[{1,2}[^\r\n]+?\]{1,2}\s*(?:#.*)?$/gm;
2485
+ nextSection.lastIndex = match.index + match[0].length;
2486
+ const next = nextSection.exec(content);
2487
+ return {
2488
+ start: match.index,
2489
+ end: next?.index ?? content.length,
2490
+ body: content.slice(match.index, next?.index ?? content.length)
2491
+ };
2492
+ }
2493
+ function readTomlString(block, key) {
2494
+ const match = block.match(new RegExp(`^\\s*${key}\\s*=\\s*("(?:[^"\\\\]|\\\\.)*")\\s*$`, "m"));
2495
+ if (!match) return null;
2496
+ try {
2497
+ return JSON.parse(match[1]);
2498
+ } catch {
2499
+ return null;
2500
+ }
2501
+ }
2502
+ function isRecognizedCodexEntry(block, expectedUrl) {
2503
+ const command = readTomlString(block, "command");
2504
+ const argsLine = block.match(/^\s*args\s*=\s*(\[[^\r\n]*\])\s*$/m);
2505
+ if (command && argsLine) {
2506
+ try {
2507
+ const args2 = JSON.parse(argsLine[1]);
2508
+ if (/(?:^|[\\/])bun(?:\.exe)?$/i.test(command) && Array.isArray(args2) && args2.length === 2 && args2[0] === "run" && args2[1] === MCP_STDIO_PROXY_PATH) return true;
2509
+ } catch {
2510
+ }
2511
+ }
2512
+ const url = readTomlString(block, "url");
2513
+ return Boolean(expectedUrl && url === expectedUrl);
2514
+ }
2515
+ function stripDetachedCodexMarkers(content) {
2516
+ return content.split("\n").filter((line) => line.trim() !== CODEX_MCP_BEGIN && line.trim() !== CODEX_MCP_END).join("\n");
2517
+ }
2518
+ function removeRecognizedCodexEntry(content, path, expectedUrl) {
2519
+ const range = findCodexMcpSection(content);
2520
+ if (!range) return { content, removed: false };
2521
+ if (!isRecognizedCodexEntry(range.body, expectedUrl)) {
2522
+ throw new Error(
2523
+ `${path} already defines ${CODEX_MCP_SECTION}; remove or rename that unmanaged entry before installing Synkro`
2524
+ );
2525
+ }
2526
+ const next = `${content.slice(0, range.start)}${content.slice(range.end)}`;
2527
+ return { content: next.replace(/\n{3,}$/g, "\n\n"), removed: true };
2434
2528
  }
2435
2529
  function tomlString(value) {
2436
2530
  return JSON.stringify(value);
@@ -2438,16 +2532,17 @@ function tomlString(value) {
2438
2532
  function installCodexMcpConfig(opts) {
2439
2533
  const path = codexConfigPath(opts.configPath);
2440
2534
  const current = readCodexToml(path);
2441
- const withoutManaged = removeCodexManagedBlock(current, path).content;
2442
- if (hasUnmanagedCodexEntry(withoutManaged)) {
2443
- throw new Error(
2444
- `${path} already defines ${CODEX_MCP_SECTION}; remove or rename that unmanaged entry before installing Synkro`
2445
- );
2446
- }
2535
+ const targetUrl = opts.local ? `stdio://${MCP_STDIO_PROXY_PATH}` : `${opts.gatewayUrl.replace(/\/$/, "")}/api/v1/mcp/guardrails`;
2536
+ const withoutManaged = stripDetachedCodexMarkers(removeCodexManagedBlock(current, path).content);
2537
+ const withoutExisting = removeRecognizedCodexEntry(
2538
+ withoutManaged,
2539
+ path,
2540
+ opts.local ? void 0 : targetUrl
2541
+ ).content;
2447
2542
  let url;
2448
2543
  let body;
2449
2544
  if (opts.local) {
2450
- url = `stdio://${MCP_STDIO_PROXY_PATH}`;
2545
+ url = targetUrl;
2451
2546
  body = [
2452
2547
  `[${CODEX_MCP_SECTION}]`,
2453
2548
  `command = ${tomlString(opts.bunBin || resolveBunBin2())}`,
@@ -2456,7 +2551,7 @@ function installCodexMcpConfig(opts) {
2456
2551
  ];
2457
2552
  } else {
2458
2553
  if (!opts.bearerToken) throw new Error("Codex cloud MCP registration requires a bearer token");
2459
- url = `${opts.gatewayUrl.replace(/\/$/, "")}/api/v1/mcp/guardrails`;
2554
+ url = targetUrl;
2460
2555
  body = [
2461
2556
  `[${CODEX_MCP_SECTION}]`,
2462
2557
  `url = ${tomlString(url)}`,
@@ -2464,7 +2559,7 @@ function installCodexMcpConfig(opts) {
2464
2559
  "enabled = true"
2465
2560
  ];
2466
2561
  }
2467
- const prefix = withoutManaged.trimEnd();
2562
+ const prefix = withoutExisting.trimEnd();
2468
2563
  const managed = [CODEX_MCP_BEGIN, ...body, CODEX_MCP_END].join("\n");
2469
2564
  writeCodexTomlAtomic(path, `${prefix}${prefix ? "\n\n" : ""}${managed}
2470
2565
  `);
@@ -2475,8 +2570,19 @@ function uninstallCodexMcpConfig(configPath) {
2475
2570
  if (!existsSync13(path)) return false;
2476
2571
  const current = readCodexToml(path);
2477
2572
  const next = removeCodexManagedBlock(current, path);
2478
- if (!next.removed) return false;
2479
- writeCodexTomlAtomic(path, next.content.trimEnd() ? `${next.content.trimEnd()}
2573
+ let content = stripDetachedCodexMarkers(next.content);
2574
+ let removed = next.removed;
2575
+ if (!removed) {
2576
+ try {
2577
+ const recognized = removeRecognizedCodexEntry(content, path);
2578
+ content = recognized.content;
2579
+ removed = recognized.removed;
2580
+ } catch {
2581
+ return false;
2582
+ }
2583
+ }
2584
+ if (!removed) return false;
2585
+ writeCodexTomlAtomic(path, content.trimEnd() ? `${content.trimEnd()}
2480
2586
  ` : "");
2481
2587
  return true;
2482
2588
  }
@@ -6765,7 +6871,7 @@ var init_dockerInstall = __esm({
6765
6871
  HOST_PGLITE_PORT = parseInt(process.env.SYNKRO_HOST_PGLITE_PORT || "15433", 10);
6766
6872
  CONTAINER_NAME = resolveContainerName();
6767
6873
  defaultImageVersion = () => {
6768
- if (true) return "1.7.93";
6874
+ if (true) return "1.7.94";
6769
6875
  try {
6770
6876
  const pkg = JSON.parse(readFileSync17(new URL("../../package.json", import.meta.url), "utf8"));
6771
6877
  if (pkg.version) return pkg.version;
@@ -7734,7 +7840,7 @@ function writeConfigEnv(opts) {
7734
7840
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle2(credsPath)}`,
7735
7841
  `SYNKRO_TIER=${shellQuoteSingle2(safeTier)}`,
7736
7842
  `SYNKRO_INFERENCE=${shellQuoteSingle2(safeInference)}`,
7737
- `SYNKRO_VERSION=${shellQuoteSingle2("1.7.93")}`
7843
+ `SYNKRO_VERSION=${shellQuoteSingle2("1.7.94")}`
7738
7844
  ];
7739
7845
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle2(safeSynkroBin)}`);
7740
7846
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle2(safeUserId)}`);
@@ -8476,7 +8582,7 @@ async function installCommand(opts = {}) {
8476
8582
  await setTelemetryState({ enabled: true, remoteFlushEnabled: telemetryConsent });
8477
8583
  emit("install", {
8478
8584
  phase: "started",
8479
- cli_version_to: "1.7.93",
8585
+ cli_version_to: "1.7.94",
8480
8586
  agents_detected: agents.map((a) => a.kind),
8481
8587
  with_github: false,
8482
8588
  with_local_cc: false,
@@ -14233,7 +14339,7 @@ var subArgs = args.slice(1);
14233
14339
  var isDetachedChild = process.env.SYNKRO_TELEMETRY_DETACHED === "1";
14234
14340
  var FLUSH_SKIP = /* @__PURE__ */ new Set(["grade", "version", "--version", "-v", "help", "--help", "-h", ""]);
14235
14341
  function printVersion() {
14236
- console.log("1.7.93");
14342
+ console.log("1.7.94");
14237
14343
  }
14238
14344
  function printHelp2() {
14239
14345
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents