engrm 0.4.45 → 0.4.46

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.
@@ -3280,7 +3280,7 @@ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync
3280
3280
  import { join as join3 } from "node:path";
3281
3281
  import { homedir } from "node:os";
3282
3282
  var STATE_PATH = join3(homedir(), ".engrm", "config-fingerprint.json");
3283
- var CLIENT_VERSION = "0.4.45";
3283
+ var CLIENT_VERSION = "0.4.46";
3284
3284
  function hashFile(filePath) {
3285
3285
  try {
3286
3286
  if (!existsSync3(filePath))
@@ -3415,11 +3415,20 @@ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as rea
3415
3415
  import { homedir as homedir2, hostname, networkInterfaces } from "node:os";
3416
3416
  import { join as join5 } from "node:path";
3417
3417
  import { createHash as createHash2 } from "node:crypto";
3418
- var CONFIG_DIR = join5(homedir2(), ".engrm");
3419
- var SETTINGS_PATH = join5(CONFIG_DIR, "settings.json");
3420
- var DB_PATH = join5(CONFIG_DIR, "engrm.db");
3418
+ function resolveConfigDir() {
3419
+ return process.env["ENGRM_CONFIG_DIR"]?.trim() || join5(homedir2(), ".engrm");
3420
+ }
3421
+ function resolveSettingsPath() {
3422
+ return join5(resolveConfigDir(), "settings.json");
3423
+ }
3424
+ function resolveDbPath() {
3425
+ return join5(resolveConfigDir(), "engrm.db");
3426
+ }
3427
+ function resolveAuthBackupPath() {
3428
+ return join5(resolveConfigDir(), "auth-backup.json");
3429
+ }
3421
3430
  function getDbPath() {
3422
- return DB_PATH;
3431
+ return resolveDbPath();
3423
3432
  }
3424
3433
  function generateDeviceId() {
3425
3434
  const host = hostname().toLowerCase().replace(/[^a-z0-9-]/g, "");
@@ -3442,7 +3451,7 @@ function generateDeviceId() {
3442
3451
  return `${host}-${suffix}`;
3443
3452
  }
3444
3453
  function createDefaultConfig() {
3445
- return {
3454
+ const merged = {
3446
3455
  candengo_url: "",
3447
3456
  candengo_api_key: "",
3448
3457
  site_id: "",
@@ -3497,24 +3506,26 @@ function createDefaultConfig() {
3497
3506
  },
3498
3507
  tool_profile: "full"
3499
3508
  };
3509
+ return merged;
3500
3510
  }
3501
3511
  function loadConfig() {
3502
- if (!existsSync5(SETTINGS_PATH)) {
3503
- throw new Error(`Config not found at ${SETTINGS_PATH}. Run 'engrm init --manual' to configure.`);
3512
+ const settingsPath = resolveSettingsPath();
3513
+ if (!existsSync5(settingsPath)) {
3514
+ throw new Error(`Config not found at ${settingsPath}. Run 'engrm init --manual' to configure.`);
3504
3515
  }
3505
- const raw = readFileSync4(SETTINGS_PATH, "utf-8");
3516
+ const raw = readFileSync4(settingsPath, "utf-8");
3506
3517
  let parsed;
3507
3518
  try {
3508
3519
  parsed = JSON.parse(raw);
3509
3520
  } catch {
3510
- throw new Error(`Invalid JSON in ${SETTINGS_PATH}`);
3521
+ throw new Error(`Invalid JSON in ${settingsPath}`);
3511
3522
  }
3512
3523
  if (typeof parsed !== "object" || parsed === null) {
3513
- throw new Error(`Config at ${SETTINGS_PATH} is not a JSON object`);
3524
+ throw new Error(`Config at ${settingsPath} is not a JSON object`);
3514
3525
  }
3515
3526
  const config = parsed;
3516
3527
  const defaults = createDefaultConfig();
3517
- return {
3528
+ const merged = {
3518
3529
  candengo_url: asString(config["candengo_url"], defaults.candengo_url),
3519
3530
  candengo_api_key: asString(config["candengo_api_key"], defaults.candengo_api_key),
3520
3531
  site_id: asString(config["site_id"], defaults.site_id),
@@ -3569,16 +3580,27 @@ function loadConfig() {
3569
3580
  },
3570
3581
  tool_profile: asToolProfile(config["tool_profile"], defaults.tool_profile)
3571
3582
  };
3583
+ if (looksLikePlaceholderAuth(merged)) {
3584
+ return restoreAuthBackup(merged) ?? merged;
3585
+ }
3586
+ return merged;
3572
3587
  }
3573
3588
  function saveConfig(config) {
3574
- if (!existsSync5(CONFIG_DIR)) {
3575
- mkdirSync2(CONFIG_DIR, { recursive: true });
3589
+ const configDir = resolveConfigDir();
3590
+ const settingsPath = resolveSettingsPath();
3591
+ const authBackupPath = resolveAuthBackupPath();
3592
+ if (!existsSync5(configDir)) {
3593
+ mkdirSync2(configDir, { recursive: true });
3576
3594
  }
3577
- writeFileSync2(SETTINGS_PATH, JSON.stringify(config, null, 2) + `
3595
+ writeFileSync2(settingsPath, JSON.stringify(config, null, 2) + `
3578
3596
  `, "utf-8");
3597
+ if (!looksLikePlaceholderAuth(config)) {
3598
+ writeFileSync2(authBackupPath, JSON.stringify(extractAuthBackup(config), null, 2) + `
3599
+ `, "utf-8");
3600
+ }
3579
3601
  }
3580
3602
  function configExists() {
3581
- return existsSync5(SETTINGS_PATH);
3603
+ return existsSync5(resolveSettingsPath());
3582
3604
  }
3583
3605
  function asString(value, fallback) {
3584
3606
  return typeof value === "string" ? value : fallback;
@@ -3632,6 +3654,50 @@ function asTeams(value, fallback) {
3632
3654
  return fallback;
3633
3655
  return value.filter((t) => typeof t === "object" && t !== null && typeof t.id === "string" && typeof t.name === "string" && typeof t.namespace === "string");
3634
3656
  }
3657
+ function looksLikePlaceholderAuth(config) {
3658
+ const apiKey = config.candengo_api_key.trim();
3659
+ const siteId = config.site_id.trim();
3660
+ const namespace = config.namespace.trim();
3661
+ const email = config.user_email.trim().toLowerCase();
3662
+ if (apiKey === "cvk_org" && siteId === "site-1" && namespace === "org-ns")
3663
+ return true;
3664
+ if (siteId === "site-1" && namespace === "org-ns" && email.endsWith("@example.com"))
3665
+ return true;
3666
+ return false;
3667
+ }
3668
+ function extractAuthBackup(config) {
3669
+ return {
3670
+ candengo_url: config.candengo_url,
3671
+ candengo_api_key: config.candengo_api_key,
3672
+ site_id: config.site_id,
3673
+ namespace: config.namespace,
3674
+ user_id: config.user_id,
3675
+ user_email: config.user_email,
3676
+ teams: config.teams
3677
+ };
3678
+ }
3679
+ function restoreAuthBackup(config) {
3680
+ const authBackupPath = resolveAuthBackupPath();
3681
+ if (!existsSync5(authBackupPath))
3682
+ return null;
3683
+ try {
3684
+ const raw = readFileSync4(authBackupPath, "utf-8");
3685
+ const parsed = JSON.parse(raw);
3686
+ const restored = {
3687
+ ...config,
3688
+ candengo_url: asString(parsed["candengo_url"], config.candengo_url),
3689
+ candengo_api_key: asString(parsed["candengo_api_key"], config.candengo_api_key),
3690
+ site_id: asString(parsed["site_id"], config.site_id),
3691
+ namespace: asString(parsed["namespace"], config.namespace),
3692
+ user_id: asString(parsed["user_id"], config.user_id),
3693
+ user_email: asString(parsed["user_email"], config.user_email),
3694
+ teams: asTeams(parsed["teams"], config.teams)
3695
+ };
3696
+ return looksLikePlaceholderAuth(restored) ? null : restored;
3697
+ } catch {
3698
+ return null;
3699
+ }
3700
+ }
3635
3701
 
3636
3702
  // src/sync/auth.ts
3637
3703
  import { createHash as createHash3 } from "node:crypto";
@@ -3725,6 +3791,10 @@ function resetStaleSyncingEntries(db, maxAgeSeconds = 300) {
3725
3791
 
3726
3792
  // src/sync/auth.ts
3727
3793
  var LEGACY_PUBLIC_HOSTS = new Set(["www.candengo.com", "candengo.com"]);
3794
+ var PLACEHOLDER_API_KEYS = new Set(["cvk_org"]);
3795
+ var PLACEHOLDER_SITE_IDS = new Set(["site-1"]);
3796
+ var PLACEHOLDER_NAMESPACES = new Set(["org-ns", "fleet-ns"]);
3797
+ var PLACEHOLDER_EMAIL_SUFFIXES = ["@example.com"];
3728
3798
  function normalizeBaseUrl(url) {
3729
3799
  const trimmed = url.trim();
3730
3800
  if (!trimmed)
@@ -3743,7 +3813,7 @@ function getApiKey(config) {
3743
3813
  const envKey = process.env.ENGRM_TOKEN;
3744
3814
  if (envKey && envKey.startsWith("cvk_"))
3745
3815
  return envKey;
3746
- if (config.candengo_api_key && config.candengo_api_key.length > 0) {
3816
+ if (config.candengo_api_key && config.candengo_api_key.length > 0 && !looksLikePlaceholderConfig(config)) {
3747
3817
  return config.candengo_api_key;
3748
3818
  }
3749
3819
  return null;
@@ -3764,6 +3834,23 @@ ${apiKey}
3764
3834
  ${config.namespace}
3765
3835
  ${config.site_id}`).digest("hex");
3766
3836
  }
3837
+ function looksLikePlaceholderConfig(config) {
3838
+ const apiKey = config.candengo_api_key?.trim() ?? "";
3839
+ const siteId = config.site_id?.trim() ?? "";
3840
+ const namespace = config.namespace?.trim() ?? "";
3841
+ const email = config.user_email?.trim().toLowerCase() ?? "";
3842
+ if (PLACEHOLDER_API_KEYS.has(apiKey) && PLACEHOLDER_SITE_IDS.has(siteId) && PLACEHOLDER_NAMESPACES.has(namespace)) {
3843
+ return true;
3844
+ }
3845
+ if (PLACEHOLDER_SITE_IDS.has(siteId) && PLACEHOLDER_NAMESPACES.has(namespace) && PLACEHOLDER_EMAIL_SUFFIXES.some((suffix) => email.endsWith(suffix))) {
3846
+ return true;
3847
+ }
3848
+ return false;
3849
+ }
3850
+ function clearSyncPushBlock(db) {
3851
+ db.setSyncState("sync_push_blocked_until", "0");
3852
+ db.setSyncState("sync_push_block_reason", "");
3853
+ }
3767
3854
  function recoverOutboxAfterAuthChange(db, config) {
3768
3855
  const fingerprint = getAuthFingerprint(config);
3769
3856
  if (!fingerprint) {
@@ -3782,6 +3869,7 @@ function recoverOutboxAfterAuthChange(db, config) {
3782
3869
  const syncingReset = resetSyncingEntries(db);
3783
3870
  const staleSyncingReset = 0;
3784
3871
  db.setSyncState(key, fingerprint);
3872
+ clearSyncPushBlock(db);
3785
3873
  return { fingerprintChanged: true, failedReset, authFailedReset: 0, syncingReset, staleSyncingReset };
3786
3874
  }
3787
3875
  function buildSourceId(config, localId, type = "obs") {