@askexenow/exe-os 0.8.75 → 0.8.77

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
@@ -2801,6 +2801,19 @@ async function cloudPull(sinceVersion, config) {
2801
2801
  }
2802
2802
  }
2803
2803
  async function cloudSync(config) {
2804
+ if (!isSyncCryptoInitialized()) {
2805
+ try {
2806
+ const { getMasterKey: getMasterKey2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
2807
+ const masterKey = await getMasterKey2();
2808
+ if (masterKey) {
2809
+ initSyncCrypto(masterKey);
2810
+ } else {
2811
+ throw new Error("No master key found");
2812
+ }
2813
+ } catch (err) {
2814
+ throw new Error(`[cloud-sync] Cannot initialize encryption: ${err instanceof Error ? err.message : String(err)}`);
2815
+ }
2816
+ }
2804
2817
  let client;
2805
2818
  try {
2806
2819
  client = getClient();
@@ -24846,6 +24859,16 @@ import { existsSync as existsSync23, readFileSync as readFileSync19, writeFileSy
24846
24859
  import path35 from "path";
24847
24860
  import os12 from "os";
24848
24861
  var args = process.argv.slice(2);
24862
+ if (args.includes("--version") || args.includes("-v")) {
24863
+ try {
24864
+ const pkgPath = path35.join(path35.dirname(new URL(import.meta.url).pathname), "..", "..", "package.json");
24865
+ const pkg = JSON.parse(readFileSync19(pkgPath, "utf8"));
24866
+ console.log(pkg.version);
24867
+ } catch {
24868
+ console.log("unknown");
24869
+ }
24870
+ process.exit(0);
24871
+ }
24849
24872
  if (args.includes("--global")) {
24850
24873
  process.stderr.write(
24851
24874
  "\x1B[33m[deprecated]\x1B[0m --global is deprecated. Use: exe-os claude install\n"
@@ -5865,6 +5865,19 @@ async function cloudPull(sinceVersion, config) {
5865
5865
  }
5866
5866
  }
5867
5867
  async function cloudSync(config) {
5868
+ if (!isSyncCryptoInitialized()) {
5869
+ try {
5870
+ const { getMasterKey: getMasterKey2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
5871
+ const masterKey = await getMasterKey2();
5872
+ if (masterKey) {
5873
+ initSyncCrypto(masterKey);
5874
+ } else {
5875
+ throw new Error("No master key found");
5876
+ }
5877
+ } catch (err) {
5878
+ throw new Error(`[cloud-sync] Cannot initialize encryption: ${err instanceof Error ? err.message : String(err)}`);
5879
+ }
5880
+ }
5868
5881
  let client;
5869
5882
  try {
5870
5883
  client = getClient();
@@ -15,6 +15,100 @@ var __export = (target, all) => {
15
15
  __defProp(target, name, { get: all[name], enumerable: true });
16
16
  };
17
17
 
18
+ // src/lib/keychain.ts
19
+ import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
20
+ import { existsSync } from "fs";
21
+ import path from "path";
22
+ import os from "os";
23
+ function getKeyDir() {
24
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
25
+ }
26
+ function getKeyPath() {
27
+ return path.join(getKeyDir(), "master.key");
28
+ }
29
+ async function tryKeytar() {
30
+ try {
31
+ return await import("keytar");
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+ async function getMasterKey() {
37
+ const keytar = await tryKeytar();
38
+ if (keytar) {
39
+ try {
40
+ const stored = await keytar.getPassword(SERVICE, ACCOUNT);
41
+ if (stored) {
42
+ return Buffer.from(stored, "base64");
43
+ }
44
+ } catch {
45
+ }
46
+ }
47
+ const keyPath = getKeyPath();
48
+ if (!existsSync(keyPath)) {
49
+ return null;
50
+ }
51
+ try {
52
+ const content = await readFile(keyPath, "utf-8");
53
+ return Buffer.from(content.trim(), "base64");
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+ async function setMasterKey(key) {
59
+ const b64 = key.toString("base64");
60
+ const keytar = await tryKeytar();
61
+ if (keytar) {
62
+ try {
63
+ await keytar.setPassword(SERVICE, ACCOUNT, b64);
64
+ return;
65
+ } catch {
66
+ }
67
+ }
68
+ const dir = getKeyDir();
69
+ await mkdir(dir, { recursive: true });
70
+ const keyPath = getKeyPath();
71
+ await writeFile(keyPath, b64 + "\n", "utf-8");
72
+ await chmod(keyPath, 384);
73
+ }
74
+ async function loadBip39() {
75
+ try {
76
+ return await import("bip39");
77
+ } catch {
78
+ throw new Error(
79
+ "bip39 package not found. Run: npm install -g bip39\nOr reinstall exe-os: npm install -g @askexenow/exe-os"
80
+ );
81
+ }
82
+ }
83
+ async function exportMnemonic(key) {
84
+ if (key.length !== 32) {
85
+ throw new Error(`Key must be 32 bytes, got ${key.length}`);
86
+ }
87
+ const { entropyToMnemonic } = await loadBip39();
88
+ return entropyToMnemonic(key.toString("hex"));
89
+ }
90
+ async function importMnemonic(mnemonic) {
91
+ const trimmed = mnemonic.trim();
92
+ const words = trimmed.split(/\s+/);
93
+ if (words.length !== 24) {
94
+ throw new Error(`Expected 24 words, got ${words.length}`);
95
+ }
96
+ const { validateMnemonic, mnemonicToEntropy } = await loadBip39();
97
+ if (!validateMnemonic(trimmed)) {
98
+ throw new Error("Invalid mnemonic \u2014 check for typos or missing words");
99
+ }
100
+ const entropy = mnemonicToEntropy(trimmed);
101
+ return Buffer.from(entropy, "hex");
102
+ }
103
+ var SERVICE, ACCOUNT;
104
+ var init_keychain = __esm({
105
+ "src/lib/keychain.ts"() {
106
+ "use strict";
107
+ SERVICE = "exe-mem";
108
+ ACCOUNT = "master-key";
109
+ }
110
+ });
111
+
18
112
  // src/lib/config.ts
19
113
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
20
114
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
@@ -601,98 +695,9 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
601
695
  });
602
696
 
603
697
  // src/bin/exe-cloud.ts
604
- import { createInterface } from "readline";
605
-
606
- // src/lib/keychain.ts
607
- import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
608
- import { existsSync } from "fs";
609
- import path from "path";
610
- import os from "os";
611
- var SERVICE = "exe-mem";
612
- var ACCOUNT = "master-key";
613
- function getKeyDir() {
614
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
615
- }
616
- function getKeyPath() {
617
- return path.join(getKeyDir(), "master.key");
618
- }
619
- async function tryKeytar() {
620
- try {
621
- return await import("keytar");
622
- } catch {
623
- return null;
624
- }
625
- }
626
- async function getMasterKey() {
627
- const keytar = await tryKeytar();
628
- if (keytar) {
629
- try {
630
- const stored = await keytar.getPassword(SERVICE, ACCOUNT);
631
- if (stored) {
632
- return Buffer.from(stored, "base64");
633
- }
634
- } catch {
635
- }
636
- }
637
- const keyPath = getKeyPath();
638
- if (!existsSync(keyPath)) {
639
- return null;
640
- }
641
- try {
642
- const content = await readFile(keyPath, "utf-8");
643
- return Buffer.from(content.trim(), "base64");
644
- } catch {
645
- return null;
646
- }
647
- }
648
- async function setMasterKey(key) {
649
- const b64 = key.toString("base64");
650
- const keytar = await tryKeytar();
651
- if (keytar) {
652
- try {
653
- await keytar.setPassword(SERVICE, ACCOUNT, b64);
654
- return;
655
- } catch {
656
- }
657
- }
658
- const dir = getKeyDir();
659
- await mkdir(dir, { recursive: true });
660
- const keyPath = getKeyPath();
661
- await writeFile(keyPath, b64 + "\n", "utf-8");
662
- await chmod(keyPath, 384);
663
- }
664
- async function loadBip39() {
665
- try {
666
- return await import("bip39");
667
- } catch {
668
- throw new Error(
669
- "bip39 package not found. Run: npm install -g bip39\nOr reinstall exe-os: npm install -g @askexenow/exe-os"
670
- );
671
- }
672
- }
673
- async function exportMnemonic(key) {
674
- if (key.length !== 32) {
675
- throw new Error(`Key must be 32 bytes, got ${key.length}`);
676
- }
677
- const { entropyToMnemonic } = await loadBip39();
678
- return entropyToMnemonic(key.toString("hex"));
679
- }
680
- async function importMnemonic(mnemonic) {
681
- const trimmed = mnemonic.trim();
682
- const words = trimmed.split(/\s+/);
683
- if (words.length !== 24) {
684
- throw new Error(`Expected 24 words, got ${words.length}`);
685
- }
686
- const { validateMnemonic, mnemonicToEntropy } = await loadBip39();
687
- if (!validateMnemonic(trimmed)) {
688
- throw new Error("Invalid mnemonic \u2014 check for typos or missing words");
689
- }
690
- const entropy = mnemonicToEntropy(trimmed);
691
- return Buffer.from(entropy, "hex");
692
- }
693
-
694
- // src/bin/exe-cloud.ts
698
+ init_keychain();
695
699
  init_config();
700
+ import { createInterface } from "readline";
696
701
 
697
702
  // src/lib/ws-auth.ts
698
703
  import crypto from "crypto";
@@ -9,6 +9,121 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
+ // src/lib/keychain.ts
13
+ var keychain_exports = {};
14
+ __export(keychain_exports, {
15
+ deleteMasterKey: () => deleteMasterKey,
16
+ exportMnemonic: () => exportMnemonic,
17
+ getMasterKey: () => getMasterKey,
18
+ importMnemonic: () => importMnemonic,
19
+ setMasterKey: () => setMasterKey
20
+ });
21
+ import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
22
+ import { existsSync } from "fs";
23
+ import path from "path";
24
+ import os from "os";
25
+ function getKeyDir() {
26
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
27
+ }
28
+ function getKeyPath() {
29
+ return path.join(getKeyDir(), "master.key");
30
+ }
31
+ async function tryKeytar() {
32
+ try {
33
+ return await import("keytar");
34
+ } catch {
35
+ return null;
36
+ }
37
+ }
38
+ async function getMasterKey() {
39
+ const keytar = await tryKeytar();
40
+ if (keytar) {
41
+ try {
42
+ const stored = await keytar.getPassword(SERVICE, ACCOUNT);
43
+ if (stored) {
44
+ return Buffer.from(stored, "base64");
45
+ }
46
+ } catch {
47
+ }
48
+ }
49
+ const keyPath = getKeyPath();
50
+ if (!existsSync(keyPath)) {
51
+ return null;
52
+ }
53
+ try {
54
+ const content = await readFile(keyPath, "utf-8");
55
+ return Buffer.from(content.trim(), "base64");
56
+ } catch {
57
+ return null;
58
+ }
59
+ }
60
+ async function setMasterKey(key) {
61
+ const b64 = key.toString("base64");
62
+ const keytar = await tryKeytar();
63
+ if (keytar) {
64
+ try {
65
+ await keytar.setPassword(SERVICE, ACCOUNT, b64);
66
+ return;
67
+ } catch {
68
+ }
69
+ }
70
+ const dir = getKeyDir();
71
+ await mkdir(dir, { recursive: true });
72
+ const keyPath = getKeyPath();
73
+ await writeFile(keyPath, b64 + "\n", "utf-8");
74
+ await chmod(keyPath, 384);
75
+ }
76
+ async function deleteMasterKey() {
77
+ const keytar = await tryKeytar();
78
+ if (keytar) {
79
+ try {
80
+ await keytar.deletePassword(SERVICE, ACCOUNT);
81
+ } catch {
82
+ }
83
+ }
84
+ const keyPath = getKeyPath();
85
+ if (existsSync(keyPath)) {
86
+ await unlink(keyPath);
87
+ }
88
+ }
89
+ async function loadBip39() {
90
+ try {
91
+ return await import("bip39");
92
+ } catch {
93
+ throw new Error(
94
+ "bip39 package not found. Run: npm install -g bip39\nOr reinstall exe-os: npm install -g @askexenow/exe-os"
95
+ );
96
+ }
97
+ }
98
+ async function exportMnemonic(key) {
99
+ if (key.length !== 32) {
100
+ throw new Error(`Key must be 32 bytes, got ${key.length}`);
101
+ }
102
+ const { entropyToMnemonic } = await loadBip39();
103
+ return entropyToMnemonic(key.toString("hex"));
104
+ }
105
+ async function importMnemonic(mnemonic) {
106
+ const trimmed = mnemonic.trim();
107
+ const words = trimmed.split(/\s+/);
108
+ if (words.length !== 24) {
109
+ throw new Error(`Expected 24 words, got ${words.length}`);
110
+ }
111
+ const { validateMnemonic, mnemonicToEntropy } = await loadBip39();
112
+ if (!validateMnemonic(trimmed)) {
113
+ throw new Error("Invalid mnemonic \u2014 check for typos or missing words");
114
+ }
115
+ const entropy = mnemonicToEntropy(trimmed);
116
+ return Buffer.from(entropy, "hex");
117
+ }
118
+ var SERVICE, ACCOUNT;
119
+ var init_keychain = __esm({
120
+ "src/lib/keychain.ts"() {
121
+ "use strict";
122
+ SERVICE = "exe-mem";
123
+ ACCOUNT = "master-key";
124
+ }
125
+ });
126
+
12
127
  // src/lib/config.ts
13
128
  var config_exports = {};
14
129
  __export(config_exports, {
@@ -645,6 +760,19 @@ async function cloudPull(sinceVersion, config) {
645
760
  }
646
761
  }
647
762
  async function cloudSync(config) {
763
+ if (!isSyncCryptoInitialized()) {
764
+ try {
765
+ const { getMasterKey: getMasterKey2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
766
+ const masterKey = await getMasterKey2();
767
+ if (masterKey) {
768
+ initSyncCrypto(masterKey);
769
+ } else {
770
+ throw new Error("No master key found");
771
+ }
772
+ } catch (err) {
773
+ throw new Error(`[cloud-sync] Cannot initialize encryption: ${err instanceof Error ? err.message : String(err)}`);
774
+ }
775
+ }
648
776
  let client;
649
777
  try {
650
778
  client = getClient();
@@ -1458,96 +1586,9 @@ var init_cloud_sync = __esm({
1458
1586
  });
1459
1587
 
1460
1588
  // src/bin/exe-link.ts
1589
+ init_keychain();
1461
1590
  import { createInterface } from "readline";
1462
1591
 
1463
- // src/lib/keychain.ts
1464
- import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1465
- import { existsSync } from "fs";
1466
- import path from "path";
1467
- import os from "os";
1468
- var SERVICE = "exe-mem";
1469
- var ACCOUNT = "master-key";
1470
- function getKeyDir() {
1471
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
1472
- }
1473
- function getKeyPath() {
1474
- return path.join(getKeyDir(), "master.key");
1475
- }
1476
- async function tryKeytar() {
1477
- try {
1478
- return await import("keytar");
1479
- } catch {
1480
- return null;
1481
- }
1482
- }
1483
- async function getMasterKey() {
1484
- const keytar = await tryKeytar();
1485
- if (keytar) {
1486
- try {
1487
- const stored = await keytar.getPassword(SERVICE, ACCOUNT);
1488
- if (stored) {
1489
- return Buffer.from(stored, "base64");
1490
- }
1491
- } catch {
1492
- }
1493
- }
1494
- const keyPath = getKeyPath();
1495
- if (!existsSync(keyPath)) {
1496
- return null;
1497
- }
1498
- try {
1499
- const content = await readFile(keyPath, "utf-8");
1500
- return Buffer.from(content.trim(), "base64");
1501
- } catch {
1502
- return null;
1503
- }
1504
- }
1505
- async function setMasterKey(key) {
1506
- const b64 = key.toString("base64");
1507
- const keytar = await tryKeytar();
1508
- if (keytar) {
1509
- try {
1510
- await keytar.setPassword(SERVICE, ACCOUNT, b64);
1511
- return;
1512
- } catch {
1513
- }
1514
- }
1515
- const dir = getKeyDir();
1516
- await mkdir(dir, { recursive: true });
1517
- const keyPath = getKeyPath();
1518
- await writeFile(keyPath, b64 + "\n", "utf-8");
1519
- await chmod(keyPath, 384);
1520
- }
1521
- async function loadBip39() {
1522
- try {
1523
- return await import("bip39");
1524
- } catch {
1525
- throw new Error(
1526
- "bip39 package not found. Run: npm install -g bip39\nOr reinstall exe-os: npm install -g @askexenow/exe-os"
1527
- );
1528
- }
1529
- }
1530
- async function exportMnemonic(key) {
1531
- if (key.length !== 32) {
1532
- throw new Error(`Key must be 32 bytes, got ${key.length}`);
1533
- }
1534
- const { entropyToMnemonic } = await loadBip39();
1535
- return entropyToMnemonic(key.toString("hex"));
1536
- }
1537
- async function importMnemonic(mnemonic) {
1538
- const trimmed = mnemonic.trim();
1539
- const words = trimmed.split(/\s+/);
1540
- if (words.length !== 24) {
1541
- throw new Error(`Expected 24 words, got ${words.length}`);
1542
- }
1543
- const { validateMnemonic, mnemonicToEntropy } = await loadBip39();
1544
- if (!validateMnemonic(trimmed)) {
1545
- throw new Error("Invalid mnemonic \u2014 check for typos or missing words");
1546
- }
1547
- const entropy = mnemonicToEntropy(trimmed);
1548
- return Buffer.from(entropy, "hex");
1549
- }
1550
-
1551
1592
  // src/lib/is-main.ts
1552
1593
  import { realpathSync } from "fs";
1553
1594
  import { fileURLToPath } from "url";
package/dist/bin/setup.js CHANGED
@@ -1633,6 +1633,19 @@ async function cloudPull(sinceVersion, config) {
1633
1633
  }
1634
1634
  }
1635
1635
  async function cloudSync(config) {
1636
+ if (!isSyncCryptoInitialized()) {
1637
+ try {
1638
+ const { getMasterKey: getMasterKey2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
1639
+ const masterKey = await getMasterKey2();
1640
+ if (masterKey) {
1641
+ initSyncCrypto(masterKey);
1642
+ } else {
1643
+ throw new Error("No master key found");
1644
+ }
1645
+ } catch (err) {
1646
+ throw new Error(`[cloud-sync] Cannot initialize encryption: ${err instanceof Error ? err.message : String(err)}`);
1647
+ }
1648
+ }
1636
1649
  let client;
1637
1650
  try {
1638
1651
  client = getClient();
@@ -3476,6 +3476,19 @@ async function cloudPull(sinceVersion, config) {
3476
3476
  }
3477
3477
  }
3478
3478
  async function cloudSync(config) {
3479
+ if (!isSyncCryptoInitialized()) {
3480
+ try {
3481
+ const { getMasterKey: getMasterKey2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
3482
+ const masterKey = await getMasterKey2();
3483
+ if (masterKey) {
3484
+ initSyncCrypto(masterKey);
3485
+ } else {
3486
+ throw new Error("No master key found");
3487
+ }
3488
+ } catch (err) {
3489
+ throw new Error(`[cloud-sync] Cannot initialize encryption: ${err instanceof Error ? err.message : String(err)}`);
3490
+ }
3491
+ }
3479
3492
  let client;
3480
3493
  try {
3481
3494
  client = getClient();
@@ -1,7 +1,132 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // src/lib/keychain.ts
12
+ var keychain_exports = {};
13
+ __export(keychain_exports, {
14
+ deleteMasterKey: () => deleteMasterKey,
15
+ exportMnemonic: () => exportMnemonic,
16
+ getMasterKey: () => getMasterKey,
17
+ importMnemonic: () => importMnemonic,
18
+ setMasterKey: () => setMasterKey
19
+ });
20
+ import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
21
+ import { existsSync as existsSync4 } from "fs";
22
+ import path4 from "path";
23
+ import os3 from "os";
24
+ function getKeyDir() {
25
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(os3.homedir(), ".exe-os");
26
+ }
27
+ function getKeyPath() {
28
+ return path4.join(getKeyDir(), "master.key");
29
+ }
30
+ async function tryKeytar() {
31
+ try {
32
+ return await import("keytar");
33
+ } catch {
34
+ return null;
35
+ }
36
+ }
37
+ async function getMasterKey() {
38
+ const keytar = await tryKeytar();
39
+ if (keytar) {
40
+ try {
41
+ const stored = await keytar.getPassword(SERVICE, ACCOUNT);
42
+ if (stored) {
43
+ return Buffer.from(stored, "base64");
44
+ }
45
+ } catch {
46
+ }
47
+ }
48
+ const keyPath = getKeyPath();
49
+ if (!existsSync4(keyPath)) {
50
+ return null;
51
+ }
52
+ try {
53
+ const content = await readFile3(keyPath, "utf-8");
54
+ return Buffer.from(content.trim(), "base64");
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+ async function setMasterKey(key) {
60
+ const b64 = key.toString("base64");
61
+ const keytar = await tryKeytar();
62
+ if (keytar) {
63
+ try {
64
+ await keytar.setPassword(SERVICE, ACCOUNT, b64);
65
+ return;
66
+ } catch {
67
+ }
68
+ }
69
+ const dir = getKeyDir();
70
+ await mkdir3(dir, { recursive: true });
71
+ const keyPath = getKeyPath();
72
+ await writeFile3(keyPath, b64 + "\n", "utf-8");
73
+ await chmod2(keyPath, 384);
74
+ }
75
+ async function deleteMasterKey() {
76
+ const keytar = await tryKeytar();
77
+ if (keytar) {
78
+ try {
79
+ await keytar.deletePassword(SERVICE, ACCOUNT);
80
+ } catch {
81
+ }
82
+ }
83
+ const keyPath = getKeyPath();
84
+ if (existsSync4(keyPath)) {
85
+ await unlink(keyPath);
86
+ }
87
+ }
88
+ async function loadBip39() {
89
+ try {
90
+ return await import("bip39");
91
+ } catch {
92
+ throw new Error(
93
+ "bip39 package not found. Run: npm install -g bip39\nOr reinstall exe-os: npm install -g @askexenow/exe-os"
94
+ );
95
+ }
96
+ }
97
+ async function exportMnemonic(key) {
98
+ if (key.length !== 32) {
99
+ throw new Error(`Key must be 32 bytes, got ${key.length}`);
100
+ }
101
+ const { entropyToMnemonic } = await loadBip39();
102
+ return entropyToMnemonic(key.toString("hex"));
103
+ }
104
+ async function importMnemonic(mnemonic) {
105
+ const trimmed = mnemonic.trim();
106
+ const words = trimmed.split(/\s+/);
107
+ if (words.length !== 24) {
108
+ throw new Error(`Expected 24 words, got ${words.length}`);
109
+ }
110
+ const { validateMnemonic, mnemonicToEntropy } = await loadBip39();
111
+ if (!validateMnemonic(trimmed)) {
112
+ throw new Error("Invalid mnemonic \u2014 check for typos or missing words");
113
+ }
114
+ const entropy = mnemonicToEntropy(trimmed);
115
+ return Buffer.from(entropy, "hex");
116
+ }
117
+ var SERVICE, ACCOUNT;
118
+ var init_keychain = __esm({
119
+ "src/lib/keychain.ts"() {
120
+ "use strict";
121
+ SERVICE = "exe-mem";
122
+ ACCOUNT = "master-key";
123
+ }
124
+ });
125
+
1
126
  // src/lib/cloud-sync.ts
2
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync as unlinkSync2, openSync, closeSync } from "fs";
127
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync as unlinkSync2, openSync, closeSync } from "fs";
3
128
  import crypto2 from "crypto";
4
- import path4 from "path";
129
+ import path5 from "path";
5
130
  import { homedir } from "os";
6
131
 
7
132
  // src/lib/database.ts
@@ -19,7 +144,19 @@ import crypto from "crypto";
19
144
  var ALGORITHM = "aes-256-gcm";
20
145
  var IV_LENGTH = 12;
21
146
  var TAG_LENGTH = 16;
147
+ var SYNC_HKDF_INFO = "exe-mem-sync-v2";
22
148
  var _syncKey = null;
149
+ function initSyncCrypto(masterKey) {
150
+ if (masterKey.length !== 32) {
151
+ throw new Error(`Master key must be 32 bytes, got ${masterKey.length}`);
152
+ }
153
+ _syncKey = Buffer.from(
154
+ crypto.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
155
+ );
156
+ }
157
+ function isSyncCryptoInitialized() {
158
+ return _syncKey !== null;
159
+ }
23
160
  function requireSyncKey() {
24
161
  if (!_syncKey) {
25
162
  throw new Error("Sync crypto not initialized. Call initSyncCrypto(masterKey) first.");
@@ -255,7 +392,7 @@ function sqlSafe(v) {
255
392
  }
256
393
  function logError(msg) {
257
394
  try {
258
- const logPath = path4.join(homedir(), ".exe-os", "workers.log");
395
+ const logPath = path5.join(homedir(), ".exe-os", "workers.log");
259
396
  appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
260
397
  `);
261
398
  } catch {
@@ -264,7 +401,7 @@ function logError(msg) {
264
401
  var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
265
402
  var FETCH_TIMEOUT_MS = 3e4;
266
403
  var PUSH_BATCH_SIZE = 5e3;
267
- var ROSTER_LOCK_PATH = path4.join(EXE_AI_DIR, "roster-merge.lock");
404
+ var ROSTER_LOCK_PATH = path5.join(EXE_AI_DIR, "roster-merge.lock");
268
405
  var LOCK_STALE_MS = 3e4;
269
406
  async function withRosterLock(fn) {
270
407
  try {
@@ -401,6 +538,19 @@ async function cloudPull(sinceVersion, config) {
401
538
  }
402
539
  }
403
540
  async function cloudSync(config) {
541
+ if (!isSyncCryptoInitialized()) {
542
+ try {
543
+ const { getMasterKey: getMasterKey2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
544
+ const masterKey = await getMasterKey2();
545
+ if (masterKey) {
546
+ initSyncCrypto(masterKey);
547
+ } else {
548
+ throw new Error("No master key found");
549
+ }
550
+ } catch (err) {
551
+ throw new Error(`[cloud-sync] Cannot initialize encryption: ${err instanceof Error ? err.message : String(err)}`);
552
+ }
553
+ }
404
554
  let client;
405
555
  try {
406
556
  client = getClient();
@@ -585,11 +735,11 @@ async function cloudSync(config) {
585
735
  documents: documentsResult
586
736
  };
587
737
  }
588
- var ROSTER_DELETIONS_PATH = path4.join(EXE_AI_DIR, "roster-deletions.json");
738
+ var ROSTER_DELETIONS_PATH = path5.join(EXE_AI_DIR, "roster-deletions.json");
589
739
  function recordRosterDeletion(name) {
590
740
  let deletions = [];
591
741
  try {
592
- if (existsSync4(ROSTER_DELETIONS_PATH)) {
742
+ if (existsSync5(ROSTER_DELETIONS_PATH)) {
593
743
  deletions = JSON.parse(readFileSync4(ROSTER_DELETIONS_PATH, "utf-8"));
594
744
  }
595
745
  } catch {
@@ -599,7 +749,7 @@ function recordRosterDeletion(name) {
599
749
  }
600
750
  function consumeRosterDeletions() {
601
751
  try {
602
- if (!existsSync4(ROSTER_DELETIONS_PATH)) return [];
752
+ if (!existsSync5(ROSTER_DELETIONS_PATH)) return [];
603
753
  const deletions = JSON.parse(readFileSync4(ROSTER_DELETIONS_PATH, "utf-8"));
604
754
  writeFileSync3(ROSTER_DELETIONS_PATH, "[]");
605
755
  return deletions;
@@ -608,27 +758,27 @@ function consumeRosterDeletions() {
608
758
  }
609
759
  }
610
760
  function buildRosterBlob(paths) {
611
- const rosterPath = paths?.rosterPath ?? path4.join(EXE_AI_DIR, "exe-employees.json");
612
- const identityDir = paths?.identityDir ?? path4.join(EXE_AI_DIR, "identity");
613
- const configPath = paths?.configPath ?? path4.join(EXE_AI_DIR, "config.json");
761
+ const rosterPath = paths?.rosterPath ?? path5.join(EXE_AI_DIR, "exe-employees.json");
762
+ const identityDir = paths?.identityDir ?? path5.join(EXE_AI_DIR, "identity");
763
+ const configPath = paths?.configPath ?? path5.join(EXE_AI_DIR, "config.json");
614
764
  let roster = [];
615
- if (existsSync4(rosterPath)) {
765
+ if (existsSync5(rosterPath)) {
616
766
  try {
617
767
  roster = JSON.parse(readFileSync4(rosterPath, "utf-8"));
618
768
  } catch {
619
769
  }
620
770
  }
621
771
  const identities = {};
622
- if (existsSync4(identityDir)) {
772
+ if (existsSync5(identityDir)) {
623
773
  for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
624
774
  try {
625
- identities[file] = readFileSync4(path4.join(identityDir, file), "utf-8");
775
+ identities[file] = readFileSync4(path5.join(identityDir, file), "utf-8");
626
776
  } catch {
627
777
  }
628
778
  }
629
779
  }
630
780
  let config;
631
- if (existsSync4(configPath)) {
781
+ if (existsSync5(configPath)) {
632
782
  try {
633
783
  config = JSON.parse(readFileSync4(configPath, "utf-8"));
634
784
  } catch {
@@ -706,23 +856,23 @@ async function cloudPullRoster(config) {
706
856
  }
707
857
  }
708
858
  function mergeConfig(remoteConfig, configPath) {
709
- const cfgPath = configPath ?? path4.join(EXE_AI_DIR, "config.json");
859
+ const cfgPath = configPath ?? path5.join(EXE_AI_DIR, "config.json");
710
860
  let local = {};
711
- if (existsSync4(cfgPath)) {
861
+ if (existsSync5(cfgPath)) {
712
862
  try {
713
863
  local = JSON.parse(readFileSync4(cfgPath, "utf-8"));
714
864
  } catch {
715
865
  }
716
866
  }
717
867
  const merged = { ...remoteConfig, ...local };
718
- const dir = path4.dirname(cfgPath);
719
- if (!existsSync4(dir)) mkdirSync2(dir, { recursive: true });
868
+ const dir = path5.dirname(cfgPath);
869
+ if (!existsSync5(dir)) mkdirSync2(dir, { recursive: true });
720
870
  writeFileSync3(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
721
871
  }
722
872
  async function mergeRosterFromRemote(remote, paths) {
723
873
  return withRosterLock(async () => {
724
874
  const rosterPath = paths?.rosterPath ?? void 0;
725
- const identityDir = paths?.identityDir ?? path4.join(EXE_AI_DIR, "identity");
875
+ const identityDir = paths?.identityDir ?? path5.join(EXE_AI_DIR, "identity");
726
876
  const localEmployees = await loadEmployees(rosterPath);
727
877
  const localNames = new Set(localEmployees.map((e) => e.name));
728
878
  let added = 0;
@@ -743,11 +893,11 @@ async function mergeRosterFromRemote(remote, paths) {
743
893
  ) ?? lookupKey;
744
894
  const remoteIdentity = remote.identities[matchedKey];
745
895
  if (remoteIdentity) {
746
- if (!existsSync4(identityDir)) mkdirSync2(identityDir, { recursive: true });
747
- const idPath = path4.join(identityDir, `${remoteEmp.name}.md`);
896
+ if (!existsSync5(identityDir)) mkdirSync2(identityDir, { recursive: true });
897
+ const idPath = path5.join(identityDir, `${remoteEmp.name}.md`);
748
898
  let localIdentity = null;
749
899
  try {
750
- localIdentity = existsSync4(idPath) ? readFileSync4(idPath, "utf-8") : null;
900
+ localIdentity = existsSync5(idPath) ? readFileSync4(idPath, "utf-8") : null;
751
901
  } catch {
752
902
  }
753
903
  if (localIdentity !== remoteIdentity) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askexenow/exe-os",
3
- "version": "0.8.75",
3
+ "version": "0.8.77",
4
4
  "description": "AI employee operating system — persistent memory, task management, and multi-agent coordination for Claude Code.",
5
5
  "license": "CC-BY-NC-4.0",
6
6
  "type": "module",