@synkro-sh/cli 1.3.18 → 1.3.19

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
@@ -3224,6 +3224,7 @@ function writeConfigEnv(opts) {
3224
3224
  const safeOrgId = sanitizeConfigValue(opts.orgId);
3225
3225
  const safeEmail = sanitizeConfigValue(opts.email);
3226
3226
  const safeTier = sanitizeConfigValue(opts.tier ?? "pro", 32);
3227
+ const safeInference = sanitizeConfigValue(opts.inference ?? "fast", 16);
3227
3228
  const lines = [
3228
3229
  "# Synkro CLI config (managed by synkro install)",
3229
3230
  "# JWT auth \u2014 the hook scripts read SYNKRO_CREDENTIALS_PATH at runtime",
@@ -3231,7 +3232,8 @@ function writeConfigEnv(opts) {
3231
3232
  `SYNKRO_GATEWAY_URL=${shellQuoteSingle(safeGateway)}`,
3232
3233
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
3233
3234
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
3234
- `SYNKRO_VERSION=${shellQuoteSingle("1.3.18")}`
3235
+ `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
3236
+ `SYNKRO_VERSION=${shellQuoteSingle("1.3.19")}`
3235
3237
  ];
3236
3238
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
3237
3239
  if (safeOrgId) lines.push(`SYNKRO_ORG_ID=${shellQuoteSingle(safeOrgId)}`);
@@ -3243,6 +3245,42 @@ function writeConfigEnv(opts) {
3243
3245
  writeFileSync5(CONFIG_PATH2, lines.join("\n"), "utf-8");
3244
3246
  chmodSync(CONFIG_PATH2, 384);
3245
3247
  }
3248
+ function collectLocalMetadata() {
3249
+ const meta = { platform: process.platform };
3250
+ try {
3251
+ meta.display_name = execSync4("git config user.name", { encoding: "utf-8", timeout: 3e3 }).trim();
3252
+ } catch {
3253
+ }
3254
+ try {
3255
+ const remote = execSync4("git remote get-url origin", { encoding: "utf-8", timeout: 3e3 }).trim();
3256
+ const m = remote.match(/(?:github\.com)[:/](.+?)(?:\.git)?$/);
3257
+ if (m) meta.active_repo = m[1];
3258
+ } catch {
3259
+ }
3260
+ return meta;
3261
+ }
3262
+ async function fetchUserProfile(gatewayUrl, token) {
3263
+ try {
3264
+ const resp = await fetch(`${gatewayUrl}/api/v1/cli/me`, {
3265
+ headers: { "Authorization": `Bearer ${token}` }
3266
+ });
3267
+ if (!resp.ok) return { tier: "pro", inference: "fast" };
3268
+ const data = await resp.json();
3269
+ const meta = collectLocalMetadata();
3270
+ fetch(`${gatewayUrl}/api/v1/cli/me`, {
3271
+ method: "PATCH",
3272
+ headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
3273
+ body: JSON.stringify(meta)
3274
+ }).catch(() => {
3275
+ });
3276
+ return {
3277
+ tier: data.plan_tier ?? "pro",
3278
+ inference: data.fast_inference ? "fast" : "standard"
3279
+ };
3280
+ } catch {
3281
+ return { tier: "pro", inference: "fast" };
3282
+ }
3283
+ }
3246
3284
  function assertGatewayAllowed(gatewayUrl) {
3247
3285
  let parsed;
3248
3286
  try {
@@ -3459,8 +3497,10 @@ async function installCommand(opts = {}) {
3459
3497
  email = info.email;
3460
3498
  } catch {
3461
3499
  }
3462
- writeConfigEnv({ gatewayUrl, userId, orgId, email, transcriptConsent });
3463
- console.log(`Wrote config to ${CONFIG_PATH2}
3500
+ const profile = await fetchUserProfile(gatewayUrl, token);
3501
+ writeConfigEnv({ gatewayUrl, userId, orgId, email, tier: profile.tier, inference: profile.inference, transcriptConsent });
3502
+ console.log(`Wrote config to ${CONFIG_PATH2}`);
3503
+ console.log(` inference: ${profile.inference} (server-side grading)
3464
3504
  `);
3465
3505
  if (transcriptConsent) {
3466
3506
  try {
@@ -3806,7 +3846,7 @@ function readConfigEnv() {
3806
3846
  }
3807
3847
  return out;
3808
3848
  }
3809
- function statusCommand() {
3849
+ async function statusCommand() {
3810
3850
  console.log("Synkro CLI status\n");
3811
3851
  if (isAuthenticated()) {
3812
3852
  const info = getUserInfo();
@@ -3818,19 +3858,30 @@ function statusCommand() {
3818
3858
  }
3819
3859
  console.log();
3820
3860
  const config = readConfigEnv();
3861
+ const gatewayUrl = (config.SYNKRO_GATEWAY_URL || "https://api.synkro.sh").replace(/^['"]|['"]$/g, "");
3862
+ let serverTier = config.SYNKRO_TIER || "(unset)";
3863
+ let serverInference = config.SYNKRO_INFERENCE || "fast";
3864
+ const token = getAccessToken();
3865
+ if (token) {
3866
+ try {
3867
+ const resp = await fetch(`${gatewayUrl}/api/v1/cli/me`, {
3868
+ headers: { "Authorization": `Bearer ${token}` },
3869
+ signal: AbortSignal.timeout(5e3)
3870
+ });
3871
+ if (resp.ok) {
3872
+ const data = await resp.json();
3873
+ serverInference = data.fast_inference ? "fast" : "standard";
3874
+ serverTier = data.plan_tier ?? data.tier ?? serverTier;
3875
+ }
3876
+ } catch {
3877
+ }
3878
+ }
3821
3879
  console.log("Config:");
3822
- console.log(` gateway: ${config.SYNKRO_GATEWAY_URL ?? "(unset)"}`);
3880
+ console.log(` gateway: ${gatewayUrl}`);
3823
3881
  console.log(` credentials: ${config.SYNKRO_CREDENTIALS_PATH ?? "(unset)"}`);
3824
- console.log(` tier: ${config.SYNKRO_TIER ?? "(unset)"}`);
3825
- const info2 = getUserInfo();
3826
- const userId = info2?.id ?? config.SYNKRO_USER_ID ?? "default";
3827
- const tierCacheFile = join7(SYNKRO_DIR3, `.tier-cache-${userId}`);
3828
- let inferenceTier = config.SYNKRO_INFERENCE_TIER || null;
3829
- if (!inferenceTier && existsSync8(tierCacheFile)) {
3830
- inferenceTier = readFileSync6(tierCacheFile, "utf-8").trim() || null;
3831
- }
3832
- const tierLabel = inferenceTier === "fast" ? "'fast' (server-side grading)" : inferenceTier === "free" ? "'free' (local daemon grading)" : "(unknown \u2014 fires on next hook)";
3833
- console.log(` inference: ${tierLabel}`);
3882
+ console.log(` tier: ${serverTier}`);
3883
+ const inferenceLabel = serverInference === "fast" ? "'fast' (server-side grading)" : "'standard' (local grading)";
3884
+ console.log(` inference: ${inferenceLabel}`);
3834
3885
  console.log(` version: ${config.SYNKRO_VERSION ?? "(unset)"}`);
3835
3886
  console.log();
3836
3887
  const agents = detectAgents();
@@ -3971,6 +4022,102 @@ var init_unlink = __esm({
3971
4022
  }
3972
4023
  });
3973
4024
 
4025
+ // cli/commands/config.ts
4026
+ var config_exports = {};
4027
+ __export(config_exports, {
4028
+ configCommand: () => configCommand
4029
+ });
4030
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync9 } from "fs";
4031
+ import { join as join8 } from "path";
4032
+ import { homedir as homedir7 } from "os";
4033
+ function readConfigEnv2() {
4034
+ if (!existsSync9(CONFIG_PATH4)) return {};
4035
+ const out = {};
4036
+ for (const line of readFileSync7(CONFIG_PATH4, "utf-8").split("\n")) {
4037
+ const t = line.trim();
4038
+ if (!t || t.startsWith("#")) continue;
4039
+ const eq = t.indexOf("=");
4040
+ if (eq > 0) out[t.slice(0, eq).trim()] = t.slice(eq + 1).trim().replace(/^['"]|['"]$/g, "");
4041
+ }
4042
+ return out;
4043
+ }
4044
+ function updateConfigValue(key, value) {
4045
+ if (!existsSync9(CONFIG_PATH4)) {
4046
+ console.error("No config found. Run `synkro install` first.");
4047
+ process.exit(1);
4048
+ }
4049
+ const lines = readFileSync7(CONFIG_PATH4, "utf-8").split("\n");
4050
+ const pattern = new RegExp(`^${key}=`);
4051
+ let found = false;
4052
+ const updated = lines.map((line) => {
4053
+ if (pattern.test(line.trim())) {
4054
+ found = true;
4055
+ return `${key}='${value}'`;
4056
+ }
4057
+ return line;
4058
+ });
4059
+ if (!found) updated.splice(updated.length - 1, 0, `${key}='${value}'`);
4060
+ writeFileSync6(CONFIG_PATH4, updated.join("\n"), "utf-8");
4061
+ }
4062
+ async function configCommand(args2) {
4063
+ if (args2.length === 0) {
4064
+ const config2 = readConfigEnv2();
4065
+ console.log("Synkro config:\n");
4066
+ console.log(` inference: ${config2.SYNKRO_INFERENCE || "fast"}`);
4067
+ console.log(` tier: ${config2.SYNKRO_TIER || "pro"}`);
4068
+ console.log(` gateway: ${config2.SYNKRO_GATEWAY_URL || "https://api.synkro.sh"}`);
4069
+ console.log(` version: ${config2.SYNKRO_VERSION || "?"}`);
4070
+ console.log(`
4071
+ To change: synkro config --inference fast|standard`);
4072
+ return;
4073
+ }
4074
+ let inferenceValue;
4075
+ for (const a of args2) {
4076
+ if (a.startsWith("--inference=")) inferenceValue = a.slice("--inference=".length);
4077
+ else if (a === "--inference" && args2.indexOf(a) + 1 < args2.length) inferenceValue = args2[args2.indexOf(a) + 1];
4078
+ }
4079
+ if (!inferenceValue || !["fast", "standard"].includes(inferenceValue)) {
4080
+ console.error("Usage: synkro config --inference fast|standard");
4081
+ process.exit(1);
4082
+ }
4083
+ if (!isAuthenticated()) {
4084
+ console.error("Not authenticated. Run `synkro login` first.");
4085
+ process.exit(1);
4086
+ }
4087
+ const token = getAccessToken();
4088
+ const config = readConfigEnv2();
4089
+ const gatewayUrl = (config.SYNKRO_GATEWAY_URL || "https://api.synkro.sh").replace(/\/$/, "");
4090
+ try {
4091
+ const resp = await fetch(`${gatewayUrl}/api/v1/cli/me`, {
4092
+ method: "PATCH",
4093
+ headers: {
4094
+ "Authorization": `Bearer ${token}`,
4095
+ "Content-Type": "application/json"
4096
+ },
4097
+ body: JSON.stringify({ fast_inference: inferenceValue === "fast" })
4098
+ });
4099
+ if (!resp.ok) {
4100
+ const errText = await resp.text().catch(() => "");
4101
+ console.error(`Failed to update: ${resp.status} ${errText.slice(0, 200)}`);
4102
+ process.exit(1);
4103
+ }
4104
+ } catch (err) {
4105
+ console.error(`Failed to reach server: ${err.message}`);
4106
+ process.exit(1);
4107
+ }
4108
+ updateConfigValue("SYNKRO_INFERENCE", inferenceValue);
4109
+ console.log(`\u2713 Inference set to '${inferenceValue}'.`);
4110
+ }
4111
+ var SYNKRO_DIR4, CONFIG_PATH4;
4112
+ var init_config = __esm({
4113
+ "cli/commands/config.ts"() {
4114
+ "use strict";
4115
+ init_stub();
4116
+ SYNKRO_DIR4 = join8(homedir7(), ".synkro");
4117
+ CONFIG_PATH4 = join8(SYNKRO_DIR4, "config.env");
4118
+ }
4119
+ });
4120
+
3974
4121
  // cli/commands/scanPr.ts
3975
4122
  var scanPr_exports = {};
3976
4123
  __export(scanPr_exports, {
@@ -4563,9 +4710,9 @@ var disconnect_exports = {};
4563
4710
  __export(disconnect_exports, {
4564
4711
  disconnectCommand: () => disconnectCommand
4565
4712
  });
4566
- import { existsSync as existsSync9, rmSync } from "fs";
4567
- import { homedir as homedir7 } from "os";
4568
- import { join as join8 } from "path";
4713
+ import { existsSync as existsSync10, rmSync } from "fs";
4714
+ import { homedir as homedir8 } from "os";
4715
+ import { join as join9 } from "path";
4569
4716
  function disconnectCommand(args2 = []) {
4570
4717
  const purge = args2.includes("--purge");
4571
4718
  console.log("Synkro disconnect starting...\n");
@@ -4583,25 +4730,25 @@ function disconnectCommand(args2 = []) {
4583
4730
  console.log(`${mcpRemoved ? "\u2713" : "\xB7"} MCP guardrails server: ${mcpRemoved ? "removed entry from ~/.claude.json" : "no Synkro MCP entry found"}`);
4584
4731
  }
4585
4732
  if (purge) {
4586
- if (existsSync9(SYNKRO_DIR4)) {
4587
- rmSync(SYNKRO_DIR4, { recursive: true, force: true });
4588
- console.log(`\u2713 Removed ${SYNKRO_DIR4}`);
4733
+ if (existsSync10(SYNKRO_DIR5)) {
4734
+ rmSync(SYNKRO_DIR5, { recursive: true, force: true });
4735
+ console.log(`\u2713 Removed ${SYNKRO_DIR5}`);
4589
4736
  } else {
4590
- console.log(`\xB7 ${SYNKRO_DIR4} already gone, nothing to remove`);
4737
+ console.log(`\xB7 ${SYNKRO_DIR5} already gone, nothing to remove`);
4591
4738
  }
4592
- } else if (existsSync9(SYNKRO_DIR4)) {
4593
- console.log(`Config preserved at ${SYNKRO_DIR4}. Run with --purge to remove.`);
4739
+ } else if (existsSync10(SYNKRO_DIR5)) {
4740
+ console.log(`Config preserved at ${SYNKRO_DIR5}. Run with --purge to remove.`);
4594
4741
  }
4595
4742
  console.log("\nSynkro disconnected.");
4596
4743
  }
4597
- var SYNKRO_DIR4;
4744
+ var SYNKRO_DIR5;
4598
4745
  var init_disconnect = __esm({
4599
4746
  "cli/commands/disconnect.ts"() {
4600
4747
  "use strict";
4601
4748
  init_agentDetect();
4602
4749
  init_ccHookConfig();
4603
4750
  init_mcpConfig();
4604
- SYNKRO_DIR4 = join8(homedir7(), ".synkro");
4751
+ SYNKRO_DIR5 = join9(homedir8(), ".synkro");
4605
4752
  }
4606
4753
  });
4607
4754
 
@@ -4643,15 +4790,15 @@ var init_reinstall = __esm({
4643
4790
  });
4644
4791
 
4645
4792
  // cli/bootstrap.js
4646
- import { readFileSync as readFileSync7, existsSync as existsSync10 } from "fs";
4793
+ import { readFileSync as readFileSync8, existsSync as existsSync11 } from "fs";
4647
4794
  import { resolve } from "path";
4648
4795
  var envCandidates = [
4649
4796
  resolve(process.cwd(), ".env"),
4650
4797
  resolve(process.env.HOME ?? "", ".synkro", "config.env")
4651
4798
  ];
4652
4799
  for (const envPath of envCandidates) {
4653
- if (!existsSync10(envPath)) continue;
4654
- const envContent = readFileSync7(envPath, "utf-8");
4800
+ if (!existsSync11(envPath)) continue;
4801
+ const envContent = readFileSync8(envPath, "utf-8");
4655
4802
  for (const line of envContent.split("\n")) {
4656
4803
  const trimmed = line.trim();
4657
4804
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -4679,6 +4826,7 @@ Commands:
4679
4826
  status Show current setup state
4680
4827
  link Link repos to a Synkro project (local git or GitHub OAuth)
4681
4828
  unlink Remove repo links from Synkro projects
4829
+ config View or change settings (e.g. --inference fast|standard)
4682
4830
  setup-github Configure GitHub PR scanning (push secrets + workflow file)
4683
4831
  update Refresh hook configs and judge prompts
4684
4832
  disconnect [--purge] Remove Synkro hooks from agents (--purge also removes ~/.synkro)
@@ -4713,7 +4861,7 @@ async function main() {
4713
4861
  }
4714
4862
  case "status": {
4715
4863
  const { statusCommand: statusCommand2 } = await Promise.resolve().then(() => (init_status(), status_exports));
4716
- statusCommand2();
4864
+ await statusCommand2();
4717
4865
  break;
4718
4866
  }
4719
4867
  case "link": {
@@ -4726,6 +4874,11 @@ async function main() {
4726
4874
  await unlinkCommand2();
4727
4875
  break;
4728
4876
  }
4877
+ case "config": {
4878
+ const { configCommand: configCommand2 } = await Promise.resolve().then(() => (init_config(), config_exports));
4879
+ await configCommand2(subArgs);
4880
+ break;
4881
+ }
4729
4882
  case "setup-github": {
4730
4883
  const { setupGithubCommand: setupGithubCommand2 } = await Promise.resolve().then(() => (init_setupGithub(), setupGithub_exports));
4731
4884
  const ghOpts = {};