@synkro-sh/cli 1.5.3 → 1.5.5

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
@@ -2849,6 +2849,61 @@ async function main() {
2849
2849
  const cmdShort = command.slice(0, 80);
2850
2850
  log('bashGuard checking: ' + cmdShort);
2851
2851
 
2852
+ // ─── Hook-side short-circuit for safe in-repo reads ───
2853
+ // The judge primer already deterministically allows these, but the round
2854
+ // trip + batch queue still costs 1–25s per call. Skipping the grade for
2855
+ // unambiguously read-only operations removes that latency for ~half of
2856
+ // typical commands (cat/grep/git status/ls/etc.) and unblocks the worker
2857
+ // pool to grade the operations that actually need judgment.
2858
+ function isSafeInRepoRead(tName: string, cmd: string): boolean {
2859
+ // CC's native read tools are inherently safe.
2860
+ if (tName === 'Read' || tName === 'Grep' || tName === 'Glob') return true;
2861
+ if (tName !== 'Bash' && tName !== 'Shell' && tName !== 'terminal' &&
2862
+ tName !== 'run_terminal_cmd' && tName !== 'execute_command') return false;
2863
+ // Reject any shell metacharacter that could turn a "read" into a write
2864
+ // or chain to an unsafe consumer. Using string includes instead of a
2865
+ // regex because escaping inside a String.raw template literal is a
2866
+ // footgun (we got bitten by an unbalanced-paren regex once already).
2867
+ const UNSAFE_CHARS = ['>', ';', '&', '|', '\`'];
2868
+ for (const ch of UNSAFE_CHARS) { if (cmd.indexOf(ch) !== -1) return false; }
2869
+ const padded = ' ' + cmd + ' ';
2870
+ const UNSAFE_WORDS = [' sudo ', ' su ', ' rm ', ' mv ', ' cp ', ' chmod ', ' chown ', ' tee ', ' kill ', ' sed -i', ' sed --in-place', '\$('];
2871
+ for (const w of UNSAFE_WORDS) { if (padded.indexOf(w) !== -1) return false; }
2872
+ const SAFE_VERBS = new Set([
2873
+ 'cat','head','tail','less','more','grep','egrep','fgrep','rg','ag',
2874
+ 'find','fd','ls','wc','cmp','diff','file','stat','which','whereis','type',
2875
+ 'pwd','whoami','id','date','echo','printf','env','true','false',
2876
+ 'jq','yq','awk','sort','uniq','cut','tr','xxd','hexdump','od','column',
2877
+ 'node','npm','pnpm','yarn','bun','python','python3','ruby','go','rustc','cargo',
2878
+ 'git','sed',
2879
+ ]);
2880
+ const tokens = cmd.trim().split(' ').filter(t => t.length > 0);
2881
+ const verb = tokens[0] || '';
2882
+ if (!SAFE_VERBS.has(verb)) return false;
2883
+ if (verb === 'git') {
2884
+ const SAFE_GIT = new Set(['log','show','diff','blame','status','branch','tag','remote','config','rev-parse','ls-files','ls-tree','cat-file','shortlog','reflog','describe','symbolic-ref']);
2885
+ const sub = tokens[1] || '';
2886
+ return SAFE_GIT.has(sub);
2887
+ }
2888
+ if (['npm','pnpm','yarn','bun','cargo','go'].includes(verb)) {
2889
+ const sub = tokens[1] || '';
2890
+ const SAFE_PKG = new Set(['--version','-v','version','list','ls','why','view','show','info','outdated','-h','--help','help']);
2891
+ return SAFE_PKG.has(sub);
2892
+ }
2893
+ if (['node','python','python3','ruby','rustc'].includes(verb)) {
2894
+ const sub = tokens[1] || '';
2895
+ return sub === '--version' || sub === '-v' || sub === '-V';
2896
+ }
2897
+ // sed: only safe without -i (we filtered that above).
2898
+ return true;
2899
+ }
2900
+
2901
+ if (isSafeInRepoRead(toolName, command)) {
2902
+ log('bashGuard ' + cmdShort + ' → instant allow (safe in-repo read)');
2903
+ outputJson({ hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: 'Synkro: safe in-repo read, deterministic allow.' } });
2904
+ return;
2905
+ }
2906
+
2852
2907
  let jwt = loadJwt();
2853
2908
  if (!jwt) { outputEmpty(); return; }
2854
2909
  jwt = await ensureFreshJwt(jwt);
@@ -5570,6 +5625,9 @@ async function dockerInstall(opts = {}) {
5570
5625
  `${CLAUDE_HOST_STATE_DIR}:/data/claude-host-state:ro`,
5571
5626
  "-e",
5572
5627
  `WORKERS_PER_POOL=${workers}`,
5628
+ // Pass through the batch-size lever if the operator set it. Defaults
5629
+ // inside the container to 5; clamped to [1, 20] by synkro-server.ts.
5630
+ ...process.env.SYNKRO_MAX_BATCH_SIZE ? ["-e", `SYNKRO_MAX_BATCH_SIZE=${process.env.SYNKRO_MAX_BATCH_SIZE}`] : [],
5573
5631
  image
5574
5632
  ];
5575
5633
  const run = spawnSync2("docker", args2, { encoding: "utf-8", stdio: "inherit", timeout: 6e4 });
@@ -5796,7 +5854,7 @@ function writeConfigEnv(opts) {
5796
5854
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
5797
5855
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
5798
5856
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
5799
- `SYNKRO_VERSION=${shellQuoteSingle("1.5.3")}`
5857
+ `SYNKRO_VERSION=${shellQuoteSingle("1.5.5")}`
5800
5858
  ];
5801
5859
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
5802
5860
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -6203,7 +6261,7 @@ async function installCommand(opts = {}) {
6203
6261
  process.exit(1);
6204
6262
  }
6205
6263
  console.log("Installing Synkro server container...");
6206
- const workersPerPool = parseInt(process.env.SYNKRO_WORKERS_PER_POOL || "4", 10);
6264
+ const workersPerPool = parseInt(process.env.SYNKRO_WORKERS_PER_POOL || "8", 10);
6207
6265
  const { image, hostMcpPort, hostGraderPort, hostCwePort } = await dockerInstall({ workersPerPool });
6208
6266
  console.log(` \u2713 pulled ${image}`);
6209
6267
  console.log(` container started \u2014 MCP=${hostMcpPort} general=${hostGraderPort} CWE=${hostCwePort}`);
@@ -7186,7 +7244,7 @@ var args = process.argv.slice(2);
7186
7244
  var cmd = args[0] || "";
7187
7245
  var subArgs = args.slice(1);
7188
7246
  function printVersion() {
7189
- console.log("1.5.3");
7247
+ console.log("1.5.5");
7190
7248
  }
7191
7249
  function printHelp() {
7192
7250
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents