@askexenow/exe-os 0.9.64 → 0.9.65

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/bin/cli.js CHANGED
@@ -5671,31 +5671,43 @@ function logError(msg) {
5671
5671
  } catch {
5672
5672
  }
5673
5673
  }
5674
+ function isTruthyEnv(value) {
5675
+ return /^(1|true|yes|on)$/i.test(value ?? "");
5676
+ }
5674
5677
  function loadPgClient() {
5675
5678
  if (_pgFailed) return null;
5676
- const postgresUrl = process.env.DATABASE_URL;
5677
5679
  const configPath = path14.join(EXE_AI_DIR, "config.json");
5678
5680
  let cloudPostgresUrl;
5681
+ let configEnabled = false;
5679
5682
  try {
5680
5683
  if (existsSync14(configPath)) {
5681
5684
  const cfg = JSON.parse(readFileSync10(configPath, "utf8"));
5682
5685
  cloudPostgresUrl = cfg.cloud?.postgresUrl;
5683
- if (cfg.cloud?.syncToPostgres === false) {
5684
- _pgFailed = true;
5685
- return null;
5686
- }
5686
+ configEnabled = cfg.cloud?.syncToPostgres === true;
5687
5687
  }
5688
5688
  } catch {
5689
5689
  }
5690
- const url = postgresUrl || cloudPostgresUrl;
5690
+ const envEnabled = isTruthyEnv(process.env.EXE_CLOUD_SYNC_TO_POSTGRES);
5691
+ if (!envEnabled && !configEnabled) {
5692
+ return null;
5693
+ }
5694
+ const url = process.env.DATABASE_URL || cloudPostgresUrl;
5691
5695
  if (!url) {
5692
5696
  _pgFailed = true;
5693
5697
  return null;
5694
5698
  }
5695
5699
  if (!_pgPromise) {
5696
5700
  _pgPromise = (async () => {
5701
+ if (!process.env.DATABASE_URL) process.env.DATABASE_URL = url;
5697
5702
  const { createRequire: createRequire3 } = await import("module");
5698
5703
  const { pathToFileURL: pathToFileURL3 } = await import("url");
5704
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
5705
+ if (explicitPath) {
5706
+ const mod2 = await import(pathToFileURL3(explicitPath).href);
5707
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
5708
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
5709
+ return new Ctor2();
5710
+ }
5699
5711
  const exeDbRoot = process.env.EXE_DB_ROOT ?? path14.join(homedir2(), "exe-db");
5700
5712
  const req = createRequire3(path14.join(exeDbRoot, "package.json"));
5701
5713
  const entry = req.resolve("@prisma/client");
@@ -5892,6 +5904,15 @@ async function cloudSync(config) {
5892
5904
  } catch {
5893
5905
  throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
5894
5906
  }
5907
+ try {
5908
+ const relink = await client.execute("SELECT value FROM sync_meta WHERE key = 'cloud_relink_required' LIMIT 1");
5909
+ if (String(relink.rows[0]?.value ?? "") === "1") {
5910
+ throw new Error("[cloud-sync] Paused after key rotation. Re-link/reupload cloud sync with the new recovery phrase before syncing.");
5911
+ }
5912
+ } catch (err) {
5913
+ const msg = err instanceof Error ? err.message : String(err);
5914
+ if (msg.includes("Paused after key rotation")) throw err;
5915
+ }
5895
5916
  try {
5896
5917
  const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
5897
5918
  await getRawClient2().execute("PRAGMA wal_checkpoint(PASSIVE)");
@@ -9061,31 +9061,43 @@ function logError(msg) {
9061
9061
  } catch {
9062
9062
  }
9063
9063
  }
9064
+ function isTruthyEnv(value) {
9065
+ return /^(1|true|yes|on)$/i.test(value ?? "");
9066
+ }
9064
9067
  function loadPgClient() {
9065
9068
  if (_pgFailed) return null;
9066
- const postgresUrl = process.env.DATABASE_URL;
9067
9069
  const configPath = path25.join(EXE_AI_DIR, "config.json");
9068
9070
  let cloudPostgresUrl;
9071
+ let configEnabled = false;
9069
9072
  try {
9070
9073
  if (existsSync21(configPath)) {
9071
9074
  const cfg = JSON.parse(readFileSync15(configPath, "utf8"));
9072
9075
  cloudPostgresUrl = cfg.cloud?.postgresUrl;
9073
- if (cfg.cloud?.syncToPostgres === false) {
9074
- _pgFailed = true;
9075
- return null;
9076
- }
9076
+ configEnabled = cfg.cloud?.syncToPostgres === true;
9077
9077
  }
9078
9078
  } catch {
9079
9079
  }
9080
- const url = postgresUrl || cloudPostgresUrl;
9080
+ const envEnabled = isTruthyEnv(process.env.EXE_CLOUD_SYNC_TO_POSTGRES);
9081
+ if (!envEnabled && !configEnabled) {
9082
+ return null;
9083
+ }
9084
+ const url = process.env.DATABASE_URL || cloudPostgresUrl;
9081
9085
  if (!url) {
9082
9086
  _pgFailed = true;
9083
9087
  return null;
9084
9088
  }
9085
9089
  if (!_pgPromise) {
9086
9090
  _pgPromise = (async () => {
9091
+ if (!process.env.DATABASE_URL) process.env.DATABASE_URL = url;
9087
9092
  const { createRequire: createRequire3 } = await import("module");
9088
9093
  const { pathToFileURL: pathToFileURL3 } = await import("url");
9094
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
9095
+ if (explicitPath) {
9096
+ const mod2 = await import(pathToFileURL3(explicitPath).href);
9097
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
9098
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
9099
+ return new Ctor2();
9100
+ }
9089
9101
  const exeDbRoot = process.env.EXE_DB_ROOT ?? path25.join(homedir2(), "exe-db");
9090
9102
  const req = createRequire3(path25.join(exeDbRoot, "package.json"));
9091
9103
  const entry = req.resolve("@prisma/client");
@@ -9282,6 +9294,15 @@ async function cloudSync(config) {
9282
9294
  } catch {
9283
9295
  throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
9284
9296
  }
9297
+ try {
9298
+ const relink = await client.execute("SELECT value FROM sync_meta WHERE key = 'cloud_relink_required' LIMIT 1");
9299
+ if (String(relink.rows[0]?.value ?? "") === "1") {
9300
+ throw new Error("[cloud-sync] Paused after key rotation. Re-link/reupload cloud sync with the new recovery phrase before syncing.");
9301
+ }
9302
+ } catch (err) {
9303
+ const msg = err instanceof Error ? err.message : String(err);
9304
+ if (msg.includes("Paused after key rotation")) throw err;
9305
+ }
9285
9306
  try {
9286
9307
  const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
9287
9308
  await getRawClient2().execute("PRAGMA wal_checkpoint(PASSIVE)");
@@ -3621,31 +3621,43 @@ function logError(msg) {
3621
3621
  } catch {
3622
3622
  }
3623
3623
  }
3624
+ function isTruthyEnv(value) {
3625
+ return /^(1|true|yes|on)$/i.test(value ?? "");
3626
+ }
3624
3627
  function loadPgClient() {
3625
3628
  if (_pgFailed) return null;
3626
- const postgresUrl = process.env.DATABASE_URL;
3627
3629
  const configPath = path10.join(EXE_AI_DIR, "config.json");
3628
3630
  let cloudPostgresUrl;
3631
+ let configEnabled = false;
3629
3632
  try {
3630
3633
  if (existsSync10(configPath)) {
3631
3634
  const cfg = JSON.parse(readFileSync7(configPath, "utf8"));
3632
3635
  cloudPostgresUrl = cfg.cloud?.postgresUrl;
3633
- if (cfg.cloud?.syncToPostgres === false) {
3634
- _pgFailed = true;
3635
- return null;
3636
- }
3636
+ configEnabled = cfg.cloud?.syncToPostgres === true;
3637
3637
  }
3638
3638
  } catch {
3639
3639
  }
3640
- const url = postgresUrl || cloudPostgresUrl;
3640
+ const envEnabled = isTruthyEnv(process.env.EXE_CLOUD_SYNC_TO_POSTGRES);
3641
+ if (!envEnabled && !configEnabled) {
3642
+ return null;
3643
+ }
3644
+ const url = process.env.DATABASE_URL || cloudPostgresUrl;
3641
3645
  if (!url) {
3642
3646
  _pgFailed = true;
3643
3647
  return null;
3644
3648
  }
3645
3649
  if (!_pgPromise) {
3646
3650
  _pgPromise = (async () => {
3651
+ if (!process.env.DATABASE_URL) process.env.DATABASE_URL = url;
3647
3652
  const { createRequire: createRequire3 } = await import("module");
3648
3653
  const { pathToFileURL: pathToFileURL3 } = await import("url");
3654
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
3655
+ if (explicitPath) {
3656
+ const mod2 = await import(pathToFileURL3(explicitPath).href);
3657
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
3658
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
3659
+ return new Ctor2();
3660
+ }
3649
3661
  const exeDbRoot = process.env.EXE_DB_ROOT ?? path10.join(homedir2(), "exe-db");
3650
3662
  const req = createRequire3(path10.join(exeDbRoot, "package.json"));
3651
3663
  const entry = req.resolve("@prisma/client");
@@ -3842,6 +3854,15 @@ async function cloudSync(config) {
3842
3854
  } catch {
3843
3855
  throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
3844
3856
  }
3857
+ try {
3858
+ const relink = await client.execute("SELECT value FROM sync_meta WHERE key = 'cloud_relink_required' LIMIT 1");
3859
+ if (String(relink.rows[0]?.value ?? "") === "1") {
3860
+ throw new Error("[cloud-sync] Paused after key rotation. Re-link/reupload cloud sync with the new recovery phrase before syncing.");
3861
+ }
3862
+ } catch (err) {
3863
+ const msg = err instanceof Error ? err.message : String(err);
3864
+ if (msg.includes("Paused after key rotation")) throw err;
3865
+ }
3845
3866
  try {
3846
3867
  const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3847
3868
  await getRawClient2().execute("PRAGMA wal_checkpoint(PASSIVE)");
package/dist/bin/setup.js CHANGED
@@ -4544,31 +4544,43 @@ function logError(msg) {
4544
4544
  } catch {
4545
4545
  }
4546
4546
  }
4547
+ function isTruthyEnv(value) {
4548
+ return /^(1|true|yes|on)$/i.test(value ?? "");
4549
+ }
4547
4550
  function loadPgClient() {
4548
4551
  if (_pgFailed) return null;
4549
- const postgresUrl = process.env.DATABASE_URL;
4550
4552
  const configPath = path12.join(EXE_AI_DIR, "config.json");
4551
4553
  let cloudPostgresUrl;
4554
+ let configEnabled = false;
4552
4555
  try {
4553
4556
  if (existsSync12(configPath)) {
4554
4557
  const cfg = JSON.parse(readFileSync8(configPath, "utf8"));
4555
4558
  cloudPostgresUrl = cfg.cloud?.postgresUrl;
4556
- if (cfg.cloud?.syncToPostgres === false) {
4557
- _pgFailed = true;
4558
- return null;
4559
- }
4559
+ configEnabled = cfg.cloud?.syncToPostgres === true;
4560
4560
  }
4561
4561
  } catch {
4562
4562
  }
4563
- const url = postgresUrl || cloudPostgresUrl;
4563
+ const envEnabled = isTruthyEnv(process.env.EXE_CLOUD_SYNC_TO_POSTGRES);
4564
+ if (!envEnabled && !configEnabled) {
4565
+ return null;
4566
+ }
4567
+ const url = process.env.DATABASE_URL || cloudPostgresUrl;
4564
4568
  if (!url) {
4565
4569
  _pgFailed = true;
4566
4570
  return null;
4567
4571
  }
4568
4572
  if (!_pgPromise) {
4569
4573
  _pgPromise = (async () => {
4574
+ if (!process.env.DATABASE_URL) process.env.DATABASE_URL = url;
4570
4575
  const { createRequire: createRequire3 } = await import("module");
4571
4576
  const { pathToFileURL: pathToFileURL3 } = await import("url");
4577
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
4578
+ if (explicitPath) {
4579
+ const mod2 = await import(pathToFileURL3(explicitPath).href);
4580
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
4581
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
4582
+ return new Ctor2();
4583
+ }
4572
4584
  const exeDbRoot = process.env.EXE_DB_ROOT ?? path12.join(homedir2(), "exe-db");
4573
4585
  const req = createRequire3(path12.join(exeDbRoot, "package.json"));
4574
4586
  const entry = req.resolve("@prisma/client");
@@ -4765,6 +4777,15 @@ async function cloudSync(config) {
4765
4777
  } catch {
4766
4778
  throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
4767
4779
  }
4780
+ try {
4781
+ const relink = await client.execute("SELECT value FROM sync_meta WHERE key = 'cloud_relink_required' LIMIT 1");
4782
+ if (String(relink.rows[0]?.value ?? "") === "1") {
4783
+ throw new Error("[cloud-sync] Paused after key rotation. Re-link/reupload cloud sync with the new recovery phrase before syncing.");
4784
+ }
4785
+ } catch (err) {
4786
+ const msg = err instanceof Error ? err.message : String(err);
4787
+ if (msg.includes("Paused after key rotation")) throw err;
4788
+ }
4768
4789
  try {
4769
4790
  const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4770
4791
  await getRawClient2().execute("PRAGMA wal_checkpoint(PASSIVE)");
@@ -5679,31 +5679,43 @@ function logError(msg) {
5679
5679
  } catch {
5680
5680
  }
5681
5681
  }
5682
+ function isTruthyEnv(value) {
5683
+ return /^(1|true|yes|on)$/i.test(value ?? "");
5684
+ }
5682
5685
  function loadPgClient() {
5683
5686
  if (_pgFailed) return null;
5684
- const postgresUrl = process.env.DATABASE_URL;
5685
5687
  const configPath = path20.join(EXE_AI_DIR, "config.json");
5686
5688
  let cloudPostgresUrl;
5689
+ let configEnabled = false;
5687
5690
  try {
5688
5691
  if (existsSync19(configPath)) {
5689
5692
  const cfg = JSON.parse(readFileSync13(configPath, "utf8"));
5690
5693
  cloudPostgresUrl = cfg.cloud?.postgresUrl;
5691
- if (cfg.cloud?.syncToPostgres === false) {
5692
- _pgFailed = true;
5693
- return null;
5694
- }
5694
+ configEnabled = cfg.cloud?.syncToPostgres === true;
5695
5695
  }
5696
5696
  } catch {
5697
5697
  }
5698
- const url = postgresUrl || cloudPostgresUrl;
5698
+ const envEnabled = isTruthyEnv(process.env.EXE_CLOUD_SYNC_TO_POSTGRES);
5699
+ if (!envEnabled && !configEnabled) {
5700
+ return null;
5701
+ }
5702
+ const url = process.env.DATABASE_URL || cloudPostgresUrl;
5699
5703
  if (!url) {
5700
5704
  _pgFailed = true;
5701
5705
  return null;
5702
5706
  }
5703
5707
  if (!_pgPromise) {
5704
5708
  _pgPromise = (async () => {
5709
+ if (!process.env.DATABASE_URL) process.env.DATABASE_URL = url;
5705
5710
  const { createRequire: createRequire3 } = await import("module");
5706
5711
  const { pathToFileURL: pathToFileURL3 } = await import("url");
5712
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
5713
+ if (explicitPath) {
5714
+ const mod2 = await import(pathToFileURL3(explicitPath).href);
5715
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
5716
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
5717
+ return new Ctor2();
5718
+ }
5707
5719
  const exeDbRoot = process.env.EXE_DB_ROOT ?? path20.join(homedir2(), "exe-db");
5708
5720
  const req = createRequire3(path20.join(exeDbRoot, "package.json"));
5709
5721
  const entry = req.resolve("@prisma/client");
@@ -5900,6 +5912,15 @@ async function cloudSync(config) {
5900
5912
  } catch {
5901
5913
  throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
5902
5914
  }
5915
+ try {
5916
+ const relink = await client.execute("SELECT value FROM sync_meta WHERE key = 'cloud_relink_required' LIMIT 1");
5917
+ if (String(relink.rows[0]?.value ?? "") === "1") {
5918
+ throw new Error("[cloud-sync] Paused after key rotation. Re-link/reupload cloud sync with the new recovery phrase before syncing.");
5919
+ }
5920
+ } catch (err) {
5921
+ const msg = err instanceof Error ? err.message : String(err);
5922
+ if (msg.includes("Paused after key rotation")) throw err;
5923
+ }
5903
5924
  try {
5904
5925
  const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
5905
5926
  await getRawClient2().execute("PRAGMA wal_checkpoint(PASSIVE)");
@@ -3421,31 +3421,43 @@ var ROSTER_LOCK_PATH = path10.join(EXE_AI_DIR, "roster-merge.lock");
3421
3421
  var LOCK_STALE_MS = 3e4;
3422
3422
  var _pgPromise = null;
3423
3423
  var _pgFailed = false;
3424
+ function isTruthyEnv(value) {
3425
+ return /^(1|true|yes|on)$/i.test(value ?? "");
3426
+ }
3424
3427
  function loadPgClient() {
3425
3428
  if (_pgFailed) return null;
3426
- const postgresUrl = process.env.DATABASE_URL;
3427
3429
  const configPath = path10.join(EXE_AI_DIR, "config.json");
3428
3430
  let cloudPostgresUrl;
3431
+ let configEnabled = false;
3429
3432
  try {
3430
3433
  if (existsSync10(configPath)) {
3431
3434
  const cfg = JSON.parse(readFileSync7(configPath, "utf8"));
3432
3435
  cloudPostgresUrl = cfg.cloud?.postgresUrl;
3433
- if (cfg.cloud?.syncToPostgres === false) {
3434
- _pgFailed = true;
3435
- return null;
3436
- }
3436
+ configEnabled = cfg.cloud?.syncToPostgres === true;
3437
3437
  }
3438
3438
  } catch {
3439
3439
  }
3440
- const url = postgresUrl || cloudPostgresUrl;
3440
+ const envEnabled = isTruthyEnv(process.env.EXE_CLOUD_SYNC_TO_POSTGRES);
3441
+ if (!envEnabled && !configEnabled) {
3442
+ return null;
3443
+ }
3444
+ const url = process.env.DATABASE_URL || cloudPostgresUrl;
3441
3445
  if (!url) {
3442
3446
  _pgFailed = true;
3443
3447
  return null;
3444
3448
  }
3445
3449
  if (!_pgPromise) {
3446
3450
  _pgPromise = (async () => {
3451
+ if (!process.env.DATABASE_URL) process.env.DATABASE_URL = url;
3447
3452
  const { createRequire: createRequire3 } = await import("module");
3448
3453
  const { pathToFileURL: pathToFileURL3 } = await import("url");
3454
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
3455
+ if (explicitPath) {
3456
+ const mod2 = await import(pathToFileURL3(explicitPath).href);
3457
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
3458
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
3459
+ return new Ctor2();
3460
+ }
3449
3461
  const exeDbRoot = process.env.EXE_DB_ROOT ?? path10.join(homedir2(), "exe-db");
3450
3462
  const req = createRequire3(path10.join(exeDbRoot, "package.json"));
3451
3463
  const entry = req.resolve("@prisma/client");
@@ -3642,6 +3654,15 @@ async function cloudSync(config) {
3642
3654
  } catch {
3643
3655
  throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
3644
3656
  }
3657
+ try {
3658
+ const relink = await client.execute("SELECT value FROM sync_meta WHERE key = 'cloud_relink_required' LIMIT 1");
3659
+ if (String(relink.rows[0]?.value ?? "") === "1") {
3660
+ throw new Error("[cloud-sync] Paused after key rotation. Re-link/reupload cloud sync with the new recovery phrase before syncing.");
3661
+ }
3662
+ } catch (err) {
3663
+ const msg = err instanceof Error ? err.message : String(err);
3664
+ if (msg.includes("Paused after key rotation")) throw err;
3665
+ }
3645
3666
  try {
3646
3667
  const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3647
3668
  await getRawClient2().execute("PRAGMA wal_checkpoint(PASSIVE)");
@@ -3498,10 +3498,12 @@ var init_memory_write_governor = __esm({
3498
3498
  // src/lib/projection-worker.ts
3499
3499
  var projection_worker_exports = {};
3500
3500
  __export(projection_worker_exports, {
3501
+ computeProjectionBackoffMs: () => computeProjectionBackoffMs,
3501
3502
  processProjectionBatch: () => processProjectionBatch,
3502
3503
  projectionHandlersForTests: () => projectionHandlersForTests,
3503
3504
  resetProjectionWorkerForTests: () => resetProjectionWorkerForTests,
3504
3505
  setProjectionWorkerPrismaClientForTests: () => setProjectionWorkerPrismaClientForTests,
3506
+ shouldStartProjectionWorker: () => shouldStartProjectionWorker,
3505
3507
  startProjectionWorker: () => startProjectionWorker,
3506
3508
  stopProjectionWorker: () => stopProjectionWorker
3507
3509
  });
@@ -3540,8 +3542,12 @@ function resetProjectionWorkerForTests() {
3540
3542
  clearTimeout(pollTimer);
3541
3543
  pollTimer = null;
3542
3544
  }
3545
+ consecutivePollErrors = 0;
3543
3546
  prismaPromise = null;
3544
3547
  }
3548
+ function isTruthyEnv(value) {
3549
+ return /^(1|true|yes|on)$/i.test(value ?? "");
3550
+ }
3545
3551
  async function processBatch() {
3546
3552
  const prisma = await loadPrisma();
3547
3553
  const events = await prisma.$queryRawUnsafe(
@@ -3583,36 +3589,65 @@ async function processBatch() {
3583
3589
  }
3584
3590
  return processed;
3585
3591
  }
3592
+ async function shouldStartProjectionWorker() {
3593
+ try {
3594
+ const config2 = await loadConfig();
3595
+ const envEnabled = isTruthyEnv(process.env.EXE_CLOUD_SYNC_TO_POSTGRES);
3596
+ if (!envEnabled && config2.cloud?.syncToPostgres !== true) {
3597
+ return { start: false, reason: "cloud.syncToPostgres is not enabled" };
3598
+ }
3599
+ } catch (err) {
3600
+ return { start: false, reason: `config unavailable: ${err instanceof Error ? err.message : String(err)}` };
3601
+ }
3602
+ if (!process.env.DATABASE_URL) {
3603
+ return { start: false, reason: "DATABASE_URL is not set" };
3604
+ }
3605
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path8.join(os5.homedir(), "exe-db");
3606
+ if (!existsSync8(path8.join(exeDbRoot, "package.json")) && !process.env.EXE_OS_PRISMA_CLIENT_PATH) {
3607
+ return { start: false, reason: "exe-db Prisma client not found" };
3608
+ }
3609
+ return { start: true };
3610
+ }
3611
+ function computeProjectionBackoffMs(errorCount) {
3612
+ if (errorCount <= 0) return POLL_INTERVAL_MS;
3613
+ return Math.min(POLL_INTERVAL_MS * 2 ** Math.min(errorCount, 5), MAX_POLL_BACKOFF_MS);
3614
+ }
3586
3615
  function startProjectionWorker() {
3587
3616
  if (running) return;
3588
- if (!process.env.DATABASE_URL) {
3589
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path8.join(os5.homedir(), "exe-db");
3590
- if (!existsSync8(path8.join(exeDbRoot, "package.json"))) {
3591
- process.stderr.write("[projection-worker] Skipped \u2014 no exe-db found. Set DATABASE_URL or EXE_DB_ROOT to enable.\n");
3617
+ void (async () => {
3618
+ const decision = await shouldStartProjectionWorker();
3619
+ if (!decision.start) {
3620
+ process.stderr.write(`[projection-worker] Skipped \u2014 ${decision.reason}.
3621
+ `);
3592
3622
  return;
3593
3623
  }
3594
- }
3595
- running = true;
3596
- process.stderr.write("[projection-worker] Starting...\n");
3597
- const tick = async () => {
3598
- if (!running) return;
3599
- try {
3600
- const count = await processBatch();
3601
- if (count > 0) {
3602
- process.stderr.write(`[projection-worker] Processed ${count} events.
3624
+ running = true;
3625
+ consecutivePollErrors = 0;
3626
+ process.stderr.write("[projection-worker] Starting...\n");
3627
+ const tick = async () => {
3628
+ if (!running) return;
3629
+ let nextDelay = POLL_INTERVAL_MS;
3630
+ try {
3631
+ const count = await processBatch();
3632
+ consecutivePollErrors = 0;
3633
+ if (count > 0) {
3634
+ process.stderr.write(`[projection-worker] Processed ${count} events.
3603
3635
  `);
3604
- }
3605
- } catch (err) {
3606
- process.stderr.write(
3607
- `[projection-worker] Poll error: ${err instanceof Error ? err.message : String(err)}
3636
+ }
3637
+ } catch (err) {
3638
+ consecutivePollErrors++;
3639
+ nextDelay = computeProjectionBackoffMs(consecutivePollErrors);
3640
+ process.stderr.write(
3641
+ `[projection-worker] Poll error (${consecutivePollErrors}; next retry ${Math.round(nextDelay / 1e3)}s): ${err instanceof Error ? err.message : String(err)}
3608
3642
  `
3609
- );
3610
- }
3611
- if (running) {
3612
- pollTimer = setTimeout(tick, POLL_INTERVAL_MS);
3613
- }
3614
- };
3615
- void tick();
3643
+ );
3644
+ }
3645
+ if (running) {
3646
+ pollTimer = setTimeout(tick, nextDelay);
3647
+ }
3648
+ };
3649
+ void tick();
3650
+ })();
3616
3651
  }
3617
3652
  function stopProjectionWorker() {
3618
3653
  running = false;
@@ -3625,10 +3660,11 @@ function stopProjectionWorker() {
3625
3660
  async function processProjectionBatch() {
3626
3661
  return processBatch();
3627
3662
  }
3628
- var prismaPromise, projectionHandlers, projectionHandlersForTests, defaultHandler, BATCH_SIZE, POLL_INTERVAL_MS, running, pollTimer;
3663
+ var prismaPromise, projectionHandlers, projectionHandlersForTests, defaultHandler, BATCH_SIZE, POLL_INTERVAL_MS, MAX_POLL_BACKOFF_MS, running, pollTimer, consecutivePollErrors;
3629
3664
  var init_projection_worker = __esm({
3630
3665
  "src/lib/projection-worker.ts"() {
3631
3666
  "use strict";
3667
+ init_config();
3632
3668
  prismaPromise = null;
3633
3669
  projectionHandlers = {
3634
3670
  async whatsapp(event, prisma) {
@@ -3739,8 +3775,10 @@ var init_projection_worker = __esm({
3739
3775
  };
3740
3776
  BATCH_SIZE = 50;
3741
3777
  POLL_INTERVAL_MS = 1e4;
3778
+ MAX_POLL_BACKOFF_MS = 5 * 6e4;
3742
3779
  running = false;
3743
3780
  pollTimer = null;
3781
+ consecutivePollErrors = 0;
3744
3782
  }
3745
3783
  });
3746
3784
 
@@ -22173,31 +22211,43 @@ function logError(msg) {
22173
22211
  } catch {
22174
22212
  }
22175
22213
  }
22214
+ function isTruthyEnv2(value) {
22215
+ return /^(1|true|yes|on)$/i.test(value ?? "");
22216
+ }
22176
22217
  function loadPgClient() {
22177
22218
  if (_pgFailed) return null;
22178
- const postgresUrl = process.env.DATABASE_URL;
22179
22219
  const configPath = path38.join(EXE_AI_DIR, "config.json");
22180
22220
  let cloudPostgresUrl;
22221
+ let configEnabled = false;
22181
22222
  try {
22182
22223
  if (existsSync33(configPath)) {
22183
22224
  const cfg = JSON.parse(readFileSync25(configPath, "utf8"));
22184
22225
  cloudPostgresUrl = cfg.cloud?.postgresUrl;
22185
- if (cfg.cloud?.syncToPostgres === false) {
22186
- _pgFailed = true;
22187
- return null;
22188
- }
22226
+ configEnabled = cfg.cloud?.syncToPostgres === true;
22189
22227
  }
22190
22228
  } catch {
22191
22229
  }
22192
- const url = postgresUrl || cloudPostgresUrl;
22230
+ const envEnabled = isTruthyEnv2(process.env.EXE_CLOUD_SYNC_TO_POSTGRES);
22231
+ if (!envEnabled && !configEnabled) {
22232
+ return null;
22233
+ }
22234
+ const url = process.env.DATABASE_URL || cloudPostgresUrl;
22193
22235
  if (!url) {
22194
22236
  _pgFailed = true;
22195
22237
  return null;
22196
22238
  }
22197
22239
  if (!_pgPromise) {
22198
22240
  _pgPromise = (async () => {
22241
+ if (!process.env.DATABASE_URL) process.env.DATABASE_URL = url;
22199
22242
  const { createRequire: createRequire7 } = await import("module");
22200
22243
  const { pathToFileURL: pathToFileURL7 } = await import("url");
22244
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
22245
+ if (explicitPath) {
22246
+ const mod2 = await import(pathToFileURL7(explicitPath).href);
22247
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
22248
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
22249
+ return new Ctor2();
22250
+ }
22201
22251
  const exeDbRoot = process.env.EXE_DB_ROOT ?? path38.join(homedir6(), "exe-db");
22202
22252
  const req = createRequire7(path38.join(exeDbRoot, "package.json"));
22203
22253
  const entry = req.resolve("@prisma/client");
@@ -22394,6 +22444,15 @@ async function cloudSync(config2) {
22394
22444
  } catch {
22395
22445
  throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
22396
22446
  }
22447
+ try {
22448
+ const relink = await client.execute("SELECT value FROM sync_meta WHERE key = 'cloud_relink_required' LIMIT 1");
22449
+ if (String(relink.rows[0]?.value ?? "") === "1") {
22450
+ throw new Error("[cloud-sync] Paused after key rotation. Re-link/reupload cloud sync with the new recovery phrase before syncing.");
22451
+ }
22452
+ } catch (err) {
22453
+ const msg = err instanceof Error ? err.message : String(err);
22454
+ if (msg.includes("Paused after key rotation")) throw err;
22455
+ }
22397
22456
  try {
22398
22457
  const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
22399
22458
  await getRawClient2().execute("PRAGMA wal_checkpoint(PASSIVE)");
@@ -19775,31 +19775,43 @@ var ROSTER_LOCK_PATH = path34.join(EXE_AI_DIR, "roster-merge.lock");
19775
19775
  var LOCK_STALE_MS = 3e4;
19776
19776
  var _pgPromise = null;
19777
19777
  var _pgFailed = false;
19778
+ function isTruthyEnv(value) {
19779
+ return /^(1|true|yes|on)$/i.test(value ?? "");
19780
+ }
19778
19781
  function loadPgClient() {
19779
19782
  if (_pgFailed) return null;
19780
- const postgresUrl = process.env.DATABASE_URL;
19781
19783
  const configPath = path34.join(EXE_AI_DIR, "config.json");
19782
19784
  let cloudPostgresUrl;
19785
+ let configEnabled = false;
19783
19786
  try {
19784
19787
  if (existsSync29(configPath)) {
19785
19788
  const cfg = JSON.parse(readFileSync22(configPath, "utf8"));
19786
19789
  cloudPostgresUrl = cfg.cloud?.postgresUrl;
19787
- if (cfg.cloud?.syncToPostgres === false) {
19788
- _pgFailed = true;
19789
- return null;
19790
- }
19790
+ configEnabled = cfg.cloud?.syncToPostgres === true;
19791
19791
  }
19792
19792
  } catch {
19793
19793
  }
19794
- const url = postgresUrl || cloudPostgresUrl;
19794
+ const envEnabled = isTruthyEnv(process.env.EXE_CLOUD_SYNC_TO_POSTGRES);
19795
+ if (!envEnabled && !configEnabled) {
19796
+ return null;
19797
+ }
19798
+ const url = process.env.DATABASE_URL || cloudPostgresUrl;
19795
19799
  if (!url) {
19796
19800
  _pgFailed = true;
19797
19801
  return null;
19798
19802
  }
19799
19803
  if (!_pgPromise) {
19800
19804
  _pgPromise = (async () => {
19805
+ if (!process.env.DATABASE_URL) process.env.DATABASE_URL = url;
19801
19806
  const { createRequire: createRequire6 } = await import("module");
19802
19807
  const { pathToFileURL: pathToFileURL6 } = await import("url");
19808
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
19809
+ if (explicitPath) {
19810
+ const mod2 = await import(pathToFileURL6(explicitPath).href);
19811
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
19812
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
19813
+ return new Ctor2();
19814
+ }
19803
19815
  const exeDbRoot = process.env.EXE_DB_ROOT ?? path34.join(homedir6(), "exe-db");
19804
19816
  const req = createRequire6(path34.join(exeDbRoot, "package.json"));
19805
19817
  const entry = req.resolve("@prisma/client");
@@ -19996,6 +20008,15 @@ async function cloudSync(config2) {
19996
20008
  } catch {
19997
20009
  throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
19998
20010
  }
20011
+ try {
20012
+ const relink = await client.execute("SELECT value FROM sync_meta WHERE key = 'cloud_relink_required' LIMIT 1");
20013
+ if (String(relink.rows[0]?.value ?? "") === "1") {
20014
+ throw new Error("[cloud-sync] Paused after key rotation. Re-link/reupload cloud sync with the new recovery phrase before syncing.");
20015
+ }
20016
+ } catch (err) {
20017
+ const msg = err instanceof Error ? err.message : String(err);
20018
+ if (msg.includes("Paused after key rotation")) throw err;
20019
+ }
19999
20020
  try {
20000
20021
  const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
20001
20022
  await getRawClient2().execute("PRAGMA wal_checkpoint(PASSIVE)");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askexenow/exe-os",
3
- "version": "0.9.64",
3
+ "version": "0.9.65",
4
4
  "description": "AI employee operating system — persistent memory, task management, and multi-agent coordination for Claude Code.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",