@aigne/afs-cli 1.11.0-beta.11 → 1.11.0-beta.12

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.
Files changed (154) hide show
  1. package/dist/cli.cjs +3 -2
  2. package/dist/cli.mjs +3 -2
  3. package/dist/cli.mjs.map +1 -1
  4. package/dist/config/afs-loader.cjs +64 -315
  5. package/dist/config/afs-loader.d.cts.map +1 -1
  6. package/dist/config/afs-loader.d.mts +2 -1
  7. package/dist/config/afs-loader.d.mts.map +1 -1
  8. package/dist/config/afs-loader.mjs +59 -310
  9. package/dist/config/afs-loader.mjs.map +1 -1
  10. package/dist/config/credential-helpers.cjs +291 -0
  11. package/dist/config/credential-helpers.d.mts +2 -0
  12. package/dist/config/credential-helpers.mjs +288 -0
  13. package/dist/config/credential-helpers.mjs.map +1 -0
  14. package/dist/config/loader.cjs +3 -1
  15. package/dist/config/loader.mjs +3 -2
  16. package/dist/config/loader.mjs.map +1 -1
  17. package/dist/config/program-install.cjs +276 -0
  18. package/dist/config/program-install.d.mts +1 -0
  19. package/dist/config/program-install.mjs +273 -0
  20. package/dist/config/program-install.mjs.map +1 -0
  21. package/dist/core/commands/connect.cjs +53 -0
  22. package/dist/core/commands/connect.d.mts +2 -0
  23. package/dist/core/commands/connect.mjs +55 -0
  24. package/dist/core/commands/connect.mjs.map +1 -0
  25. package/dist/core/commands/daemon.cjs +207 -0
  26. package/dist/core/commands/daemon.d.mts +2 -0
  27. package/dist/core/commands/daemon.mjs +208 -0
  28. package/dist/core/commands/daemon.mjs.map +1 -0
  29. package/dist/core/commands/explain.cjs +3 -1
  30. package/dist/core/commands/explain.mjs +3 -1
  31. package/dist/core/commands/explain.mjs.map +1 -1
  32. package/dist/core/commands/explore.cjs +47 -12
  33. package/dist/core/commands/explore.mjs +47 -12
  34. package/dist/core/commands/explore.mjs.map +1 -1
  35. package/dist/core/commands/gen-agent-md.cjs +126 -0
  36. package/dist/core/commands/gen-agent-md.d.mts +2 -0
  37. package/dist/core/commands/gen-agent-md.mjs +125 -0
  38. package/dist/core/commands/gen-agent-md.mjs.map +1 -0
  39. package/dist/core/commands/index.cjs +13 -1
  40. package/dist/core/commands/index.d.cts.map +1 -1
  41. package/dist/core/commands/index.d.mts +6 -0
  42. package/dist/core/commands/index.d.mts.map +1 -1
  43. package/dist/core/commands/index.mjs +13 -1
  44. package/dist/core/commands/index.mjs.map +1 -1
  45. package/dist/core/commands/install.cjs +91 -0
  46. package/dist/core/commands/install.d.mts +2 -0
  47. package/dist/core/commands/install.mjs +92 -0
  48. package/dist/core/commands/install.mjs.map +1 -0
  49. package/dist/core/commands/ls.cjs +14 -2
  50. package/dist/core/commands/ls.d.cts +2 -0
  51. package/dist/core/commands/ls.d.cts.map +1 -1
  52. package/dist/core/commands/ls.d.mts +2 -0
  53. package/dist/core/commands/ls.d.mts.map +1 -1
  54. package/dist/core/commands/ls.mjs +14 -2
  55. package/dist/core/commands/ls.mjs.map +1 -1
  56. package/dist/core/commands/mcp-bridge.cjs +201 -0
  57. package/dist/core/commands/mcp-bridge.d.mts +2 -0
  58. package/dist/core/commands/mcp-bridge.mjs +201 -0
  59. package/dist/core/commands/mcp-bridge.mjs.map +1 -0
  60. package/dist/core/commands/read.cjs +20 -7
  61. package/dist/core/commands/read.d.cts +2 -0
  62. package/dist/core/commands/read.d.cts.map +1 -1
  63. package/dist/core/commands/read.d.mts +2 -0
  64. package/dist/core/commands/read.d.mts.map +1 -1
  65. package/dist/core/commands/read.mjs +20 -7
  66. package/dist/core/commands/read.mjs.map +1 -1
  67. package/dist/core/commands/search.cjs +5 -1
  68. package/dist/core/commands/search.mjs +5 -1
  69. package/dist/core/commands/search.mjs.map +1 -1
  70. package/dist/core/commands/stat.mjs.map +1 -1
  71. package/dist/core/commands/types.d.cts +2 -0
  72. package/dist/core/commands/types.d.cts.map +1 -1
  73. package/dist/core/commands/types.d.mts +2 -0
  74. package/dist/core/commands/types.d.mts.map +1 -1
  75. package/dist/core/commands/types.mjs.map +1 -1
  76. package/dist/core/commands/vault.cjs +289 -0
  77. package/dist/core/commands/vault.d.mts +2 -0
  78. package/dist/core/commands/vault.mjs +289 -0
  79. package/dist/core/commands/vault.mjs.map +1 -0
  80. package/dist/core/commands/write.cjs +19 -6
  81. package/dist/core/commands/write.d.cts +2 -1
  82. package/dist/core/commands/write.d.cts.map +1 -1
  83. package/dist/core/commands/write.d.mts +2 -1
  84. package/dist/core/commands/write.d.mts.map +1 -1
  85. package/dist/core/commands/write.mjs +19 -6
  86. package/dist/core/commands/write.mjs.map +1 -1
  87. package/dist/core/executor/index.cjs +95 -19
  88. package/dist/core/executor/index.d.cts +4 -0
  89. package/dist/core/executor/index.d.cts.map +1 -1
  90. package/dist/core/executor/index.d.mts +4 -0
  91. package/dist/core/executor/index.d.mts.map +1 -1
  92. package/dist/core/executor/index.mjs +95 -19
  93. package/dist/core/executor/index.mjs.map +1 -1
  94. package/dist/core/formatters/index.d.mts +1 -0
  95. package/dist/core/formatters/install.cjs +21 -0
  96. package/dist/core/formatters/install.d.mts +1 -0
  97. package/dist/core/formatters/install.mjs +19 -0
  98. package/dist/core/formatters/install.mjs.map +1 -0
  99. package/dist/core/formatters/vault.cjs +36 -0
  100. package/dist/core/formatters/vault.mjs +32 -0
  101. package/dist/core/formatters/vault.mjs.map +1 -0
  102. package/dist/credential/index.d.mts +2 -1
  103. package/dist/credential/mcp-auth-context.cjs +21 -5
  104. package/dist/credential/mcp-auth-context.mjs +21 -5
  105. package/dist/credential/mcp-auth-context.mjs.map +1 -1
  106. package/dist/credential/resolver.cjs +7 -2
  107. package/dist/credential/resolver.mjs +7 -2
  108. package/dist/credential/resolver.mjs.map +1 -1
  109. package/dist/credential/vault-store.d.mts +1 -0
  110. package/dist/daemon/config-manager.cjs +279 -0
  111. package/dist/daemon/config-manager.mjs +279 -0
  112. package/dist/daemon/config-manager.mjs.map +1 -0
  113. package/dist/daemon/manager.cjs +164 -0
  114. package/dist/daemon/manager.mjs +157 -0
  115. package/dist/daemon/manager.mjs.map +1 -0
  116. package/dist/daemon/server.cjs +220 -0
  117. package/dist/daemon/server.mjs +220 -0
  118. package/dist/daemon/server.mjs.map +1 -0
  119. package/dist/mcp/http-transport.cjs +14 -1
  120. package/dist/mcp/http-transport.mjs +14 -1
  121. package/dist/mcp/http-transport.mjs.map +1 -1
  122. package/dist/mcp/server.cjs +4 -2
  123. package/dist/mcp/server.mjs +4 -2
  124. package/dist/mcp/server.mjs.map +1 -1
  125. package/dist/mcp/tools.cjs +62 -12
  126. package/dist/mcp/tools.mjs +62 -12
  127. package/dist/mcp/tools.mjs.map +1 -1
  128. package/dist/program/daemon-integration.cjs +46 -0
  129. package/dist/program/daemon-integration.mjs +45 -0
  130. package/dist/program/daemon-integration.mjs.map +1 -0
  131. package/dist/program/program-manager.cjs +162 -0
  132. package/dist/program/program-manager.mjs +162 -0
  133. package/dist/program/program-manager.mjs.map +1 -0
  134. package/dist/program/trigger-scanner.cjs +148 -0
  135. package/dist/program/trigger-scanner.mjs +148 -0
  136. package/dist/program/trigger-scanner.mjs.map +1 -0
  137. package/dist/providers/vault/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
  138. package/dist/providers/vault/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +11 -0
  139. package/dist/providers/vault/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs.map +1 -0
  140. package/dist/providers/vault/dist/encrypted-file.cjs +158 -0
  141. package/dist/providers/vault/dist/encrypted-file.mjs +153 -0
  142. package/dist/providers/vault/dist/encrypted-file.mjs.map +1 -0
  143. package/dist/providers/vault/dist/index.cjs +405 -0
  144. package/dist/providers/vault/dist/index.mjs +400 -0
  145. package/dist/providers/vault/dist/index.mjs.map +1 -0
  146. package/dist/providers/vault/dist/key-resolver.cjs +181 -0
  147. package/dist/providers/vault/dist/key-resolver.mjs +180 -0
  148. package/dist/providers/vault/dist/key-resolver.mjs.map +1 -0
  149. package/dist/repl.cjs +105 -14
  150. package/dist/repl.d.cts.map +1 -1
  151. package/dist/repl.d.mts.map +1 -1
  152. package/dist/repl.mjs +105 -14
  153. package/dist/repl.mjs.map +1 -1
  154. package/package.json +29 -22
@@ -0,0 +1,158 @@
1
+ const require_rolldown_runtime = require('../../../_virtual/rolldown_runtime.cjs');
2
+ let node_crypto = require("node:crypto");
3
+ let node_fs_promises = require("node:fs/promises");
4
+ let node_path = require("node:path");
5
+
6
+ //#region ../../providers/vault/dist/encrypted-file.mjs
7
+ /**
8
+ * Encrypted file backend for vault storage.
9
+ *
10
+ * - AES-256-GCM encryption
11
+ * - Master key: 256-bit random or crypto.scrypt-derived from passphrase
12
+ * - File format: JSON header + encrypted blob
13
+ * - File permissions: 0600
14
+ */
15
+ /** Encryption parameters */
16
+ const KEY_LENGTH = 32;
17
+ const IV_LENGTH = 12;
18
+ const SALT_LENGTH = 32;
19
+ const AUTH_TAG_LENGTH = 16;
20
+ const SCRYPT_N = 2 ** 14;
21
+ const SCRYPT_R = 8;
22
+ const SCRYPT_P = 1;
23
+ const SCRYPT_KEYLEN = KEY_LENGTH;
24
+ const FILE_MODE = 384;
25
+ /** Magic bytes to identify vault files */
26
+ const MAGIC = "AFSVAULT";
27
+ const FORMAT_VERSION = 1;
28
+ /**
29
+ * Generate a random 256-bit master key.
30
+ */
31
+ function generateMasterKey() {
32
+ return (0, node_crypto.randomBytes)(KEY_LENGTH);
33
+ }
34
+ /**
35
+ * Derive a master key from a passphrase using crypto.scrypt.
36
+ */
37
+ function deriveKeyFromPassphrase(passphrase, salt) {
38
+ return (0, node_crypto.scryptSync)(passphrase, salt, SCRYPT_KEYLEN, {
39
+ N: SCRYPT_N,
40
+ r: SCRYPT_R,
41
+ p: SCRYPT_P
42
+ });
43
+ }
44
+ /**
45
+ * Encrypt vault data and write to file.
46
+ */
47
+ async function writeEncryptedVault(filePath, data, masterKey) {
48
+ const json = JSON.stringify(data);
49
+ const plaintext = Buffer.from(json, "utf-8");
50
+ const salt = (0, node_crypto.randomBytes)(SALT_LENGTH);
51
+ const iv = (0, node_crypto.randomBytes)(IV_LENGTH);
52
+ const cipher = (0, node_crypto.createCipheriv)("aes-256-gcm", masterKey, iv);
53
+ const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
54
+ const authTag = cipher.getAuthTag();
55
+ const magic = Buffer.from(MAGIC, "ascii");
56
+ const version = Buffer.alloc(1);
57
+ version[0] = FORMAT_VERSION;
58
+ const fileContent = Buffer.concat([
59
+ magic,
60
+ version,
61
+ salt,
62
+ iv,
63
+ authTag,
64
+ encrypted
65
+ ]);
66
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(filePath), { recursive: true });
67
+ const tmpPath = `${filePath}.tmp.${process.pid}`;
68
+ try {
69
+ await (0, node_fs_promises.writeFile)(tmpPath, fileContent, { mode: FILE_MODE });
70
+ await (0, node_fs_promises.rename)(tmpPath, filePath);
71
+ } catch (err) {
72
+ try {
73
+ const { unlink } = await import("node:fs/promises");
74
+ await unlink(tmpPath);
75
+ } catch {}
76
+ throw err;
77
+ }
78
+ try {
79
+ if (((await (0, node_fs_promises.stat)(filePath)).mode & 511) !== FILE_MODE) await (0, node_fs_promises.chmod)(filePath, FILE_MODE);
80
+ } catch {}
81
+ }
82
+ /**
83
+ * Read and decrypt vault data from file.
84
+ * Returns null if file does not exist.
85
+ */
86
+ async function readEncryptedVault(filePath, masterKey) {
87
+ let fileContent;
88
+ try {
89
+ fileContent = await (0, node_fs_promises.readFile)(filePath);
90
+ } catch (err) {
91
+ if (err?.code === "ENOENT") return null;
92
+ throw err;
93
+ }
94
+ const magicLen = 8;
95
+ const minSize = magicLen + 1 + SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH;
96
+ if (fileContent.length < minSize) throw new Error("Vault file is corrupted: too small");
97
+ if (fileContent.subarray(0, magicLen).toString("ascii") !== MAGIC) throw new Error("Not a valid vault file");
98
+ const version = fileContent[magicLen];
99
+ if (version !== FORMAT_VERSION) throw new Error(`Unsupported vault format version: ${version}`);
100
+ let offset = magicLen + 1;
101
+ fileContent.subarray(offset, offset + SALT_LENGTH);
102
+ offset += SALT_LENGTH;
103
+ const iv = fileContent.subarray(offset, offset + IV_LENGTH);
104
+ offset += IV_LENGTH;
105
+ const authTag = fileContent.subarray(offset, offset + AUTH_TAG_LENGTH);
106
+ offset += AUTH_TAG_LENGTH;
107
+ const ciphertext = fileContent.subarray(offset);
108
+ const decipher = (0, node_crypto.createDecipheriv)("aes-256-gcm", masterKey, iv);
109
+ decipher.setAuthTag(authTag);
110
+ let plaintext;
111
+ try {
112
+ plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
113
+ } catch {
114
+ throw new Error("Vault decryption failed: wrong master key or corrupted data");
115
+ }
116
+ const json = plaintext.toString("utf-8");
117
+ try {
118
+ return JSON.parse(json);
119
+ } catch {
120
+ throw new Error("Vault data is corrupted: invalid JSON after decryption");
121
+ }
122
+ }
123
+ /**
124
+ * Read the per-file salt from a vault file header.
125
+ * Returns null if file does not exist or is not a valid vault file.
126
+ */
127
+ async function readVaultSalt(filePath) {
128
+ let fileContent;
129
+ try {
130
+ fileContent = await (0, node_fs_promises.readFile)(filePath);
131
+ } catch {
132
+ return null;
133
+ }
134
+ const magicLen = 8;
135
+ const minSize = magicLen + 1 + SALT_LENGTH;
136
+ if (fileContent.length < minSize) return null;
137
+ if (fileContent.subarray(0, magicLen).toString("ascii") !== MAGIC) return null;
138
+ return fileContent.subarray(magicLen + 1, magicLen + 1 + SALT_LENGTH);
139
+ }
140
+ /**
141
+ * Check if a vault file exists.
142
+ */
143
+ async function vaultFileExists(filePath) {
144
+ try {
145
+ await (0, node_fs_promises.stat)(filePath);
146
+ return true;
147
+ } catch {
148
+ return false;
149
+ }
150
+ }
151
+
152
+ //#endregion
153
+ exports.deriveKeyFromPassphrase = deriveKeyFromPassphrase;
154
+ exports.generateMasterKey = generateMasterKey;
155
+ exports.readEncryptedVault = readEncryptedVault;
156
+ exports.readVaultSalt = readVaultSalt;
157
+ exports.vaultFileExists = vaultFileExists;
158
+ exports.writeEncryptedVault = writeEncryptedVault;
@@ -0,0 +1,153 @@
1
+ import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "node:crypto";
2
+ import { chmod, mkdir, readFile, rename, stat, writeFile } from "node:fs/promises";
3
+ import { dirname } from "node:path";
4
+
5
+ //#region ../../providers/vault/dist/encrypted-file.mjs
6
+ /**
7
+ * Encrypted file backend for vault storage.
8
+ *
9
+ * - AES-256-GCM encryption
10
+ * - Master key: 256-bit random or crypto.scrypt-derived from passphrase
11
+ * - File format: JSON header + encrypted blob
12
+ * - File permissions: 0600
13
+ */
14
+ /** Encryption parameters */
15
+ const KEY_LENGTH = 32;
16
+ const IV_LENGTH = 12;
17
+ const SALT_LENGTH = 32;
18
+ const AUTH_TAG_LENGTH = 16;
19
+ const SCRYPT_N = 2 ** 14;
20
+ const SCRYPT_R = 8;
21
+ const SCRYPT_P = 1;
22
+ const SCRYPT_KEYLEN = KEY_LENGTH;
23
+ const FILE_MODE = 384;
24
+ /** Magic bytes to identify vault files */
25
+ const MAGIC = "AFSVAULT";
26
+ const FORMAT_VERSION = 1;
27
+ /**
28
+ * Generate a random 256-bit master key.
29
+ */
30
+ function generateMasterKey() {
31
+ return randomBytes(KEY_LENGTH);
32
+ }
33
+ /**
34
+ * Derive a master key from a passphrase using crypto.scrypt.
35
+ */
36
+ function deriveKeyFromPassphrase(passphrase, salt) {
37
+ return scryptSync(passphrase, salt, SCRYPT_KEYLEN, {
38
+ N: SCRYPT_N,
39
+ r: SCRYPT_R,
40
+ p: SCRYPT_P
41
+ });
42
+ }
43
+ /**
44
+ * Encrypt vault data and write to file.
45
+ */
46
+ async function writeEncryptedVault(filePath, data, masterKey) {
47
+ const json = JSON.stringify(data);
48
+ const plaintext = Buffer.from(json, "utf-8");
49
+ const salt = randomBytes(SALT_LENGTH);
50
+ const iv = randomBytes(IV_LENGTH);
51
+ const cipher = createCipheriv("aes-256-gcm", masterKey, iv);
52
+ const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
53
+ const authTag = cipher.getAuthTag();
54
+ const magic = Buffer.from(MAGIC, "ascii");
55
+ const version = Buffer.alloc(1);
56
+ version[0] = FORMAT_VERSION;
57
+ const fileContent = Buffer.concat([
58
+ magic,
59
+ version,
60
+ salt,
61
+ iv,
62
+ authTag,
63
+ encrypted
64
+ ]);
65
+ await mkdir(dirname(filePath), { recursive: true });
66
+ const tmpPath = `${filePath}.tmp.${process.pid}`;
67
+ try {
68
+ await writeFile(tmpPath, fileContent, { mode: FILE_MODE });
69
+ await rename(tmpPath, filePath);
70
+ } catch (err) {
71
+ try {
72
+ const { unlink: unlink$1 } = await import("node:fs/promises");
73
+ await unlink$1(tmpPath);
74
+ } catch {}
75
+ throw err;
76
+ }
77
+ try {
78
+ if (((await stat(filePath)).mode & 511) !== FILE_MODE) await chmod(filePath, FILE_MODE);
79
+ } catch {}
80
+ }
81
+ /**
82
+ * Read and decrypt vault data from file.
83
+ * Returns null if file does not exist.
84
+ */
85
+ async function readEncryptedVault(filePath, masterKey) {
86
+ let fileContent;
87
+ try {
88
+ fileContent = await readFile(filePath);
89
+ } catch (err) {
90
+ if (err?.code === "ENOENT") return null;
91
+ throw err;
92
+ }
93
+ const magicLen = 8;
94
+ const minSize = magicLen + 1 + SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH;
95
+ if (fileContent.length < minSize) throw new Error("Vault file is corrupted: too small");
96
+ if (fileContent.subarray(0, magicLen).toString("ascii") !== MAGIC) throw new Error("Not a valid vault file");
97
+ const version = fileContent[magicLen];
98
+ if (version !== FORMAT_VERSION) throw new Error(`Unsupported vault format version: ${version}`);
99
+ let offset = magicLen + 1;
100
+ fileContent.subarray(offset, offset + SALT_LENGTH);
101
+ offset += SALT_LENGTH;
102
+ const iv = fileContent.subarray(offset, offset + IV_LENGTH);
103
+ offset += IV_LENGTH;
104
+ const authTag = fileContent.subarray(offset, offset + AUTH_TAG_LENGTH);
105
+ offset += AUTH_TAG_LENGTH;
106
+ const ciphertext = fileContent.subarray(offset);
107
+ const decipher = createDecipheriv("aes-256-gcm", masterKey, iv);
108
+ decipher.setAuthTag(authTag);
109
+ let plaintext;
110
+ try {
111
+ plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
112
+ } catch {
113
+ throw new Error("Vault decryption failed: wrong master key or corrupted data");
114
+ }
115
+ const json = plaintext.toString("utf-8");
116
+ try {
117
+ return JSON.parse(json);
118
+ } catch {
119
+ throw new Error("Vault data is corrupted: invalid JSON after decryption");
120
+ }
121
+ }
122
+ /**
123
+ * Read the per-file salt from a vault file header.
124
+ * Returns null if file does not exist or is not a valid vault file.
125
+ */
126
+ async function readVaultSalt(filePath) {
127
+ let fileContent;
128
+ try {
129
+ fileContent = await readFile(filePath);
130
+ } catch {
131
+ return null;
132
+ }
133
+ const magicLen = 8;
134
+ const minSize = magicLen + 1 + SALT_LENGTH;
135
+ if (fileContent.length < minSize) return null;
136
+ if (fileContent.subarray(0, magicLen).toString("ascii") !== MAGIC) return null;
137
+ return fileContent.subarray(magicLen + 1, magicLen + 1 + SALT_LENGTH);
138
+ }
139
+ /**
140
+ * Check if a vault file exists.
141
+ */
142
+ async function vaultFileExists(filePath) {
143
+ try {
144
+ await stat(filePath);
145
+ return true;
146
+ } catch {
147
+ return false;
148
+ }
149
+ }
150
+
151
+ //#endregion
152
+ export { deriveKeyFromPassphrase, generateMasterKey, readEncryptedVault, readVaultSalt, vaultFileExists, writeEncryptedVault };
153
+ //# sourceMappingURL=encrypted-file.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encrypted-file.mjs","names":["unlink"],"sources":["../../../../../../providers/vault/dist/encrypted-file.mjs"],"sourcesContent":["import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from \"node:crypto\";\nimport { chmod, mkdir, readFile, rename, stat, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\n//#region src/encrypted-file.ts\n/**\n* Encrypted file backend for vault storage.\n*\n* - AES-256-GCM encryption\n* - Master key: 256-bit random or crypto.scrypt-derived from passphrase\n* - File format: JSON header + encrypted blob\n* - File permissions: 0600\n*/\n/** Encryption parameters */\nconst KEY_LENGTH = 32;\nconst IV_LENGTH = 12;\nconst SALT_LENGTH = 32;\nconst AUTH_TAG_LENGTH = 16;\nconst SCRYPT_N = 2 ** 14;\nconst SCRYPT_R = 8;\nconst SCRYPT_P = 1;\nconst SCRYPT_KEYLEN = KEY_LENGTH;\nconst FILE_MODE = 384;\n/** Magic bytes to identify vault files */\nconst MAGIC = \"AFSVAULT\";\nconst FORMAT_VERSION = 1;\n/**\n* Generate a random 256-bit master key.\n*/\nfunction generateMasterKey() {\n\treturn randomBytes(KEY_LENGTH);\n}\n/**\n* Derive a master key from a passphrase using crypto.scrypt.\n*/\nfunction deriveKeyFromPassphrase(passphrase, salt) {\n\treturn scryptSync(passphrase, salt, SCRYPT_KEYLEN, {\n\t\tN: SCRYPT_N,\n\t\tr: SCRYPT_R,\n\t\tp: SCRYPT_P\n\t});\n}\n/**\n* Encrypt vault data and write to file.\n*/\nasync function writeEncryptedVault(filePath, data, masterKey) {\n\tconst json = JSON.stringify(data);\n\tconst plaintext = Buffer.from(json, \"utf-8\");\n\tconst salt = randomBytes(SALT_LENGTH);\n\tconst iv = randomBytes(IV_LENGTH);\n\tconst cipher = createCipheriv(\"aes-256-gcm\", masterKey, iv);\n\tconst encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);\n\tconst authTag = cipher.getAuthTag();\n\tconst magic = Buffer.from(MAGIC, \"ascii\");\n\tconst version = Buffer.alloc(1);\n\tversion[0] = FORMAT_VERSION;\n\tconst fileContent = Buffer.concat([\n\t\tmagic,\n\t\tversion,\n\t\tsalt,\n\t\tiv,\n\t\tauthTag,\n\t\tencrypted\n\t]);\n\tawait mkdir(dirname(filePath), { recursive: true });\n\tconst tmpPath = `${filePath}.tmp.${process.pid}`;\n\ttry {\n\t\tawait writeFile(tmpPath, fileContent, { mode: FILE_MODE });\n\t\tawait rename(tmpPath, filePath);\n\t} catch (err) {\n\t\ttry {\n\t\t\tconst { unlink } = await import(\"node:fs/promises\");\n\t\t\tawait unlink(tmpPath);\n\t\t} catch {}\n\t\tthrow err;\n\t}\n\ttry {\n\t\tif (((await stat(filePath)).mode & 511) !== FILE_MODE) await chmod(filePath, FILE_MODE);\n\t} catch {}\n}\n/**\n* Read and decrypt vault data from file.\n* Returns null if file does not exist.\n*/\nasync function readEncryptedVault(filePath, masterKey) {\n\tlet fileContent;\n\ttry {\n\t\tfileContent = await readFile(filePath);\n\t} catch (err) {\n\t\tif (err?.code === \"ENOENT\") return null;\n\t\tthrow err;\n\t}\n\tconst magicLen = 8;\n\tconst minSize = magicLen + 1 + SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH;\n\tif (fileContent.length < minSize) throw new Error(\"Vault file is corrupted: too small\");\n\tif (fileContent.subarray(0, magicLen).toString(\"ascii\") !== MAGIC) throw new Error(\"Not a valid vault file\");\n\tconst version = fileContent[magicLen];\n\tif (version !== FORMAT_VERSION) throw new Error(`Unsupported vault format version: ${version}`);\n\tlet offset = magicLen + 1;\n\tfileContent.subarray(offset, offset + SALT_LENGTH);\n\toffset += SALT_LENGTH;\n\tconst iv = fileContent.subarray(offset, offset + IV_LENGTH);\n\toffset += IV_LENGTH;\n\tconst authTag = fileContent.subarray(offset, offset + AUTH_TAG_LENGTH);\n\toffset += AUTH_TAG_LENGTH;\n\tconst ciphertext = fileContent.subarray(offset);\n\tconst decipher = createDecipheriv(\"aes-256-gcm\", masterKey, iv);\n\tdecipher.setAuthTag(authTag);\n\tlet plaintext;\n\ttry {\n\t\tplaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);\n\t} catch {\n\t\tthrow new Error(\"Vault decryption failed: wrong master key or corrupted data\");\n\t}\n\tconst json = plaintext.toString(\"utf-8\");\n\ttry {\n\t\treturn JSON.parse(json);\n\t} catch {\n\t\tthrow new Error(\"Vault data is corrupted: invalid JSON after decryption\");\n\t}\n}\n/**\n* Read the per-file salt from a vault file header.\n* Returns null if file does not exist or is not a valid vault file.\n*/\nasync function readVaultSalt(filePath) {\n\tlet fileContent;\n\ttry {\n\t\tfileContent = await readFile(filePath);\n\t} catch {\n\t\treturn null;\n\t}\n\tconst magicLen = 8;\n\tconst minSize = magicLen + 1 + SALT_LENGTH;\n\tif (fileContent.length < minSize) return null;\n\tif (fileContent.subarray(0, magicLen).toString(\"ascii\") !== MAGIC) return null;\n\treturn fileContent.subarray(magicLen + 1, magicLen + 1 + SALT_LENGTH);\n}\n/**\n* Check if a vault file exists.\n*/\nasync function vaultFileExists(filePath) {\n\ttry {\n\t\tawait stat(filePath);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n//#endregion\nexport { deriveKeyFromPassphrase, generateMasterKey, readEncryptedVault, readVaultSalt, vaultFileExists, writeEncryptedVault };\n//# sourceMappingURL=encrypted-file.mjs.map"],"mappings":"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"}