@synkro-sh/cli 1.6.6 → 1.6.7

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
@@ -5858,6 +5858,7 @@ __export(dockerInstall_exports, {
5858
5858
  dockerStop: () => dockerStop,
5859
5859
  dockerUpdate: () => dockerUpdate,
5860
5860
  imageTag: () => imageTag,
5861
+ readContainerConfig: () => readContainerConfig,
5861
5862
  resolveWorkerConfig: () => resolveWorkerConfig,
5862
5863
  splitWorkers: () => splitWorkers,
5863
5864
  waitForContainerReady: () => waitForContainerReady
@@ -6091,6 +6092,34 @@ function dockerStatus() {
6091
6092
  healthz: `http://127.0.0.1:${HOST_MCP_PORT}/`
6092
6093
  };
6093
6094
  }
6095
+ function readContainerConfig() {
6096
+ const r = spawnSync2("docker", ["inspect", "--format", "{{json .Config.Env}}", CONTAINER_NAME], {
6097
+ encoding: "utf-8",
6098
+ timeout: 5e3
6099
+ });
6100
+ if (r.status !== 0 || !r.stdout) return null;
6101
+ let env;
6102
+ try {
6103
+ env = JSON.parse(r.stdout.trim());
6104
+ } catch {
6105
+ return null;
6106
+ }
6107
+ if (!Array.isArray(env)) return null;
6108
+ const get = (k) => {
6109
+ const hit = env.find((e) => typeof e === "string" && e.startsWith(k + "="));
6110
+ return hit ? hit.slice(k.length + 1) : void 0;
6111
+ };
6112
+ const num = (s) => {
6113
+ if (s === void 0) return void 0;
6114
+ const n = parseInt(s, 10);
6115
+ return Number.isFinite(n) ? n : void 0;
6116
+ };
6117
+ return {
6118
+ claudeWorkers: num(get("CLAUDE_WORKERS")),
6119
+ cursorWorkers: num(get("CURSOR_WORKERS")),
6120
+ connectedRepo: get("SYNKRO_CONNECTED_REPO") || void 0
6121
+ };
6122
+ }
6094
6123
  async function dockerSafeStop() {
6095
6124
  const status = dockerStatus();
6096
6125
  if (!status.running) {
@@ -6417,7 +6446,7 @@ function writeConfigEnv(opts) {
6417
6446
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
6418
6447
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
6419
6448
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
6420
- `SYNKRO_VERSION=${shellQuoteSingle("1.6.6")}`
6449
+ `SYNKRO_VERSION=${shellQuoteSingle("1.6.7")}`
6421
6450
  ];
6422
6451
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
6423
6452
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -7540,22 +7569,22 @@ import { existsSync as existsSync11, rmSync, readdirSync as readdirSync3 } from
7540
7569
  import { homedir as homedir10 } from "os";
7541
7570
  import { join as join10 } from "path";
7542
7571
  import { spawnSync as spawnSync4 } from "child_process";
7543
- async function tearDownLocalCC(purge) {
7572
+ import { createInterface as createInterface4 } from "readline";
7573
+ async function tearDownLocalCC() {
7544
7574
  const docker = dockerStatus();
7545
7575
  if (docker.running) {
7546
7576
  await dockerSafeStop();
7547
- console.log("\u2713 stopped synkro-server container (data snapshot saved)");
7577
+ console.log("\u2713 stopped synkro-server container");
7548
7578
  } else {
7549
7579
  console.log("\xB7 no synkro-server container running");
7550
7580
  }
7551
7581
  dockerRemove();
7552
- if (purge) {
7553
- try {
7554
- const image = imageTag();
7555
- spawnSync4("docker", ["rmi", image], { encoding: "utf-8", timeout: 3e4 });
7556
- console.log(`\u2713 removed Docker image ${image}`);
7557
- } catch {
7558
- }
7582
+ console.log("\u2713 removed synkro-server container");
7583
+ try {
7584
+ const image = imageTag();
7585
+ const r = spawnSync4("docker", ["rmi", "-f", image], { encoding: "utf-8", timeout: 3e4 });
7586
+ console.log(r.status === 0 ? `\u2713 removed Docker image ${image}` : "\xB7 no Docker image to remove");
7587
+ } catch {
7559
7588
  }
7560
7589
  if (needsKeychainBridge()) {
7561
7590
  try {
@@ -7567,10 +7596,31 @@ async function tearDownLocalCC(purge) {
7567
7596
  uninstallLocalCC();
7568
7597
  console.log("\u2713 cleaned ~/.claude.json entries");
7569
7598
  }
7599
+ function confirmPurge() {
7600
+ console.log("\u26A0 WARNING \u2014 synkro uninstall --purge");
7601
+ console.log(" This permanently deletes ALL Synkro data on this machine,");
7602
+ console.log(" including every scan finding, telemetry record, and backup in");
7603
+ console.log(" ~/.synkro/pgdata and ~/.synkro/pgdata-backups. It cannot be undone.\n");
7604
+ if (!process.stdin.isTTY) {
7605
+ console.log(" Non-interactive shell \u2014 re-run in a terminal to confirm.");
7606
+ return Promise.resolve(false);
7607
+ }
7608
+ const rl = createInterface4({ input: process.stdin, output: process.stdout });
7609
+ return new Promise((resolve3) => {
7610
+ rl.question(" Type 'yes' to wipe everything (anything else cancels): ", (answer) => {
7611
+ rl.close();
7612
+ resolve3(answer.trim().toLowerCase() === "yes");
7613
+ });
7614
+ });
7615
+ }
7570
7616
  async function disconnectCommand(args2 = []) {
7571
7617
  const purge = args2.includes("--purge");
7572
- console.log("Synkro disconnect starting...\n");
7573
- await tearDownLocalCC(purge);
7618
+ if (purge && !await confirmPurge()) {
7619
+ console.log("\nAborted \u2014 nothing was removed.");
7620
+ return;
7621
+ }
7622
+ console.log("\nSynkro uninstall starting...\n");
7623
+ await tearDownLocalCC();
7574
7624
  const agents = detectAgents();
7575
7625
  let sawClaudeCode = false;
7576
7626
  for (const agent of agents) {
@@ -7591,31 +7641,32 @@ async function disconnectCommand(args2 = []) {
7591
7641
  const cursorMcpRemoved = uninstallCursorMcpConfig();
7592
7642
  console.log(`${cursorMcpRemoved ? "\u2713" : "\xB7"} MCP guardrails (Cursor): ${cursorMcpRemoved ? "removed from ~/.cursor/mcp.json" : "no entry found"}`);
7593
7643
  }
7594
- if (purge) {
7595
- if (existsSync11(SYNKRO_DIR5)) {
7596
- const pgdataPath = join10(SYNKRO_DIR5, "pgdata");
7597
- const backupsPath = join10(SYNKRO_DIR5, "pgdata-backups");
7644
+ if (existsSync11(SYNKRO_DIR5)) {
7645
+ if (purge) {
7646
+ rmSync(SYNKRO_DIR5, { recursive: true, force: true });
7647
+ console.log(`\u2713 wiped ${SYNKRO_DIR5} entirely \u2014 including all scan data and backups`);
7648
+ } else {
7649
+ const keep = /* @__PURE__ */ new Set([join10(SYNKRO_DIR5, "pgdata"), join10(SYNKRO_DIR5, "pgdata-backups")]);
7598
7650
  const preserved = [];
7599
7651
  for (const entry of readdirSync3(SYNKRO_DIR5)) {
7600
7652
  const full = join10(SYNKRO_DIR5, entry);
7601
- if (full === pgdataPath || full === backupsPath) {
7653
+ if (keep.has(full)) {
7602
7654
  preserved.push(entry);
7603
7655
  continue;
7604
7656
  }
7605
7657
  rmSync(full, { recursive: true, force: true });
7606
7658
  }
7607
7659
  if (preserved.length > 0) {
7608
- console.log(`\u2713 Removed ${SYNKRO_DIR5} config (preserved: ${preserved.join(", ")} \u2014 your data is safe)`);
7660
+ console.log(`\u2713 removed Synkro config from ${SYNKRO_DIR5} (kept your scan data: ${preserved.join(", ")})`);
7661
+ console.log(" run `synkro uninstall --purge` to delete that too");
7609
7662
  } else {
7610
- console.log(`\u2713 Removed ${SYNKRO_DIR5}`);
7663
+ console.log(`\u2713 removed ${SYNKRO_DIR5}`);
7611
7664
  }
7612
- } else {
7613
- console.log(`\xB7 ${SYNKRO_DIR5} already gone, nothing to remove`);
7614
7665
  }
7615
- } else if (existsSync11(SYNKRO_DIR5)) {
7616
- console.log(`Config preserved at ${SYNKRO_DIR5}. Run with --purge to remove.`);
7666
+ } else {
7667
+ console.log(`\xB7 ${SYNKRO_DIR5} already gone`);
7617
7668
  }
7618
- console.log("\nSynkro disconnected.");
7669
+ console.log(purge ? "\nSynkro fully removed \u2014 this machine is clean, as if it was never installed." : "\nSynkro uninstalled. Your scan data is preserved.");
7619
7670
  }
7620
7671
  var SYNKRO_DIR5;
7621
7672
  var init_disconnect = __esm({
@@ -7812,7 +7863,8 @@ var lifecycle_exports = {};
7812
7863
  __export(lifecycle_exports, {
7813
7864
  restartCommand: () => restartCommand,
7814
7865
  startCommand: () => startCommand,
7815
- stopCommand: () => stopCommand
7866
+ stopCommand: () => stopCommand,
7867
+ updateCommand: () => updateCommand
7816
7868
  });
7817
7869
  async function stopCommand() {
7818
7870
  assertDockerAvailable();
@@ -7848,6 +7900,26 @@ Start failed: ${result.error}`);
7848
7900
  }
7849
7901
  console.log("\nServer is running.");
7850
7902
  }
7903
+ async function updateCommand() {
7904
+ assertDockerAvailable();
7905
+ const cfg = readContainerConfig();
7906
+ if (!cfg) {
7907
+ console.error("No synkro-server container found. Run `synkro install` first.");
7908
+ process.exit(1);
7909
+ }
7910
+ const claudeWorkers = cfg.claudeWorkers ?? 8;
7911
+ const cursorWorkers = cfg.cursorWorkers ?? 0;
7912
+ console.log("Synkro: updating to the latest container image");
7913
+ console.log(` preserving pool: ${claudeWorkers} claude + ${cursorWorkers} cursor worker(s)
7914
+ `);
7915
+ await dockerUpdate({ claudeWorkers, cursorWorkers, connectedRepo: cfg.connectedRepo });
7916
+ const ready = await waitForContainerReady(9e4);
7917
+ if (!ready) {
7918
+ console.error("\n\u26A0 container did not pass its health check within 90s \u2014 check: docker logs synkro-server");
7919
+ process.exit(1);
7920
+ }
7921
+ console.log("\nSynkro updated \u2014 now running the latest version.");
7922
+ }
7851
7923
  async function restartCommand(rest = []) {
7852
7924
  assertDockerAvailable();
7853
7925
  const cfg = resolveWorkerConfig(rest);
@@ -7904,7 +7976,7 @@ var args = process.argv.slice(2);
7904
7976
  var cmd = args[0] || "";
7905
7977
  var subArgs = args.slice(1);
7906
7978
  function printVersion() {
7907
- console.log("1.6.6");
7979
+ console.log("1.6.7");
7908
7980
  }
7909
7981
  function printHelp() {
7910
7982
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
@@ -7914,10 +7986,11 @@ Usage:
7914
7986
 
7915
7987
  Commands:
7916
7988
  install [--force] Install or update Synkro
7917
- uninstall [--purge] Remove Synkro hooks (--purge also removes ~/.synkro)
7989
+ uninstall [--purge] Remove Synkro (keeps scan data; --purge wipes that too)
7918
7990
  stop Gracefully stop the server (snapshot + checkpoint)
7919
7991
  start [opts] Start the server (with pgdata integrity check)
7920
7992
  restart [opts] Safe restart (stop \u2192 start, data preserved)
7993
+ update Pull the latest container image and safely restart
7921
7994
  version Show version
7922
7995
 
7923
7996
  start/restart opts (recreate the worker pool):
@@ -7976,6 +8049,11 @@ async function main() {
7976
8049
  await restartCommand2(args.slice(1));
7977
8050
  break;
7978
8051
  }
8052
+ case "update": {
8053
+ const { updateCommand: updateCommand2 } = await Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports));
8054
+ await updateCommand2();
8055
+ break;
8056
+ }
7979
8057
  default: {
7980
8058
  console.error(`Unknown command: ${cmd}`);
7981
8059
  printHelp();