@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 +23 -0
- package/dist/bin/exe-boot.js +13 -0
- package/dist/bin/exe-cloud.js +96 -91
- package/dist/bin/exe-link.js +129 -88
- package/dist/bin/setup.js +13 -0
- package/dist/hooks/summary-worker.js +13 -0
- package/dist/lib/cloud-sync.js +172 -22
- package/package.json +1 -1
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"
|
package/dist/bin/exe-boot.js
CHANGED
|
@@ -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();
|
package/dist/bin/exe-cloud.js
CHANGED
|
@@ -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
|
-
|
|
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";
|
package/dist/bin/exe-link.js
CHANGED
|
@@ -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();
|
package/dist/lib/cloud-sync.js
CHANGED
|
@@ -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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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 (!
|
|
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 ??
|
|
612
|
-
const identityDir = paths?.identityDir ??
|
|
613
|
-
const configPath = paths?.configPath ??
|
|
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 (
|
|
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 (
|
|
772
|
+
if (existsSync5(identityDir)) {
|
|
623
773
|
for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
624
774
|
try {
|
|
625
|
-
identities[file] = readFileSync4(
|
|
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 (
|
|
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 ??
|
|
859
|
+
const cfgPath = configPath ?? path5.join(EXE_AI_DIR, "config.json");
|
|
710
860
|
let local = {};
|
|
711
|
-
if (
|
|
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 =
|
|
719
|
-
if (!
|
|
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 ??
|
|
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 (!
|
|
747
|
-
const idPath =
|
|
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 =
|
|
900
|
+
localIdentity = existsSync5(idPath) ? readFileSync4(idPath, "utf-8") : null;
|
|
751
901
|
} catch {
|
|
752
902
|
}
|
|
753
903
|
if (localIdentity !== remoteIdentity) {
|
package/package.json
CHANGED