@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,400 @@
1
+ import { deriveKeyFromPassphrase, generateMasterKey, readEncryptedVault, vaultFileExists, writeEncryptedVault } from "./encrypted-file.mjs";
2
+ import { __decorate } from "./_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs";
3
+ import { resolveMasterKey, storeKeychain } from "./key-resolver.mjs";
4
+ import { joinURL } from "ufo";
5
+ import { AFSNotFoundError } from "@aigne/afs";
6
+ import { z } from "zod";
7
+ import { AFSBaseProvider, Delete, Explain, List, Meta, Read, Search, Stat, Write } from "@aigne/afs/provider";
8
+
9
+ //#region ../../providers/vault/dist/index.mjs
10
+ /**
11
+ * AFS Vault Provider — Encrypted secret storage.
12
+ *
13
+ * Tree: /vault/{group}/{name} → secret value (string)
14
+ * Encryption: AES-256-GCM, master key from keychain/env/passphrase
15
+ * Security: admin (readwrite) + system (readonly) profiles
16
+ * No exec — vault is passive storage.
17
+ */
18
+ const afsVaultOptionsSchema = z.object({
19
+ name: z.string().optional().describe("Module name"),
20
+ description: z.string().optional().describe("Module description"),
21
+ vaultPath: z.string().describe("Path to the encrypted vault file"),
22
+ accessMode: z.enum(["readonly", "readwrite"]).optional().describe("Access mode")
23
+ });
24
+ /** Prototype pollution guard — reject dangerous path segments. */
25
+ const DANGEROUS_NAMES = new Set([
26
+ "__proto__",
27
+ "constructor",
28
+ "prototype"
29
+ ]);
30
+ function assertSafeName(segment) {
31
+ if (DANGEROUS_NAMES.has(segment)) throw new Error(`Forbidden path segment: ${segment}`);
32
+ }
33
+ var AFSVault = class AFSVault extends AFSBaseProvider {
34
+ name;
35
+ description;
36
+ accessMode;
37
+ vaultPath;
38
+ _masterKey;
39
+ _data = null;
40
+ _loaded = false;
41
+ constructor(options) {
42
+ super();
43
+ this.name = options.name || "vault";
44
+ this.description = options.description || "Encrypted secret storage";
45
+ this.accessMode = options.accessMode || "readwrite";
46
+ this.vaultPath = options.vaultPath;
47
+ this._masterKey = options.masterKey;
48
+ }
49
+ static securityProfiles() {
50
+ return {
51
+ admin: {
52
+ actionPolicy: "full",
53
+ accessMode: "readwrite"
54
+ },
55
+ system: {
56
+ actionPolicy: "full",
57
+ accessMode: "readonly"
58
+ }
59
+ };
60
+ }
61
+ static schema() {
62
+ return afsVaultOptionsSchema;
63
+ }
64
+ static treeSchema() {
65
+ return {
66
+ operations: [
67
+ "list",
68
+ "read",
69
+ "write",
70
+ "delete",
71
+ "search",
72
+ "stat",
73
+ "explain"
74
+ ],
75
+ tree: {
76
+ "/": {
77
+ kind: "vault:root",
78
+ operations: ["list", "read"]
79
+ },
80
+ "/{group}": {
81
+ kind: "vault:group",
82
+ operations: [
83
+ "list",
84
+ "read",
85
+ "delete"
86
+ ],
87
+ destructive: ["delete"]
88
+ },
89
+ "/{group}/{name}": {
90
+ kind: "vault:secret",
91
+ operations: [
92
+ "read",
93
+ "write",
94
+ "delete"
95
+ ],
96
+ destructive: ["delete"]
97
+ }
98
+ },
99
+ auth: {
100
+ type: "custom",
101
+ env: ["AFS_VAULT_KEY"]
102
+ },
103
+ bestFor: ["secret storage", "API key management"],
104
+ notFor: ["large file storage"]
105
+ };
106
+ }
107
+ static manifest() {
108
+ return {
109
+ name: "vault",
110
+ description: "Encrypted secret storage.\n- Store API keys, tokens, and passwords with AES-256-GCM encryption\n- Secrets organized in groups: /vault/{group}/{name}\n- Security profiles: admin (readwrite), system (readonly)",
111
+ uriTemplate: "vault://{vaultPath+}",
112
+ category: "security",
113
+ schema: z.object({ vaultPath: z.string() }),
114
+ tags: [
115
+ "vault",
116
+ "secrets",
117
+ "encryption",
118
+ "credentials"
119
+ ],
120
+ capabilityTags: [
121
+ "read-write",
122
+ "crud",
123
+ "search",
124
+ "auth:none",
125
+ "local"
126
+ ],
127
+ security: {
128
+ riskLevel: "local",
129
+ resourceAccess: ["local-filesystem"],
130
+ dataSensitivity: ["credentials"],
131
+ notes: ["Stores encrypted secrets in a local file (AES-256-GCM)", "Master key stored in OS keychain or derived from passphrase"]
132
+ },
133
+ capabilities: {
134
+ filesystem: {
135
+ read: true,
136
+ write: true
137
+ },
138
+ secrets: ["vault/master-key"]
139
+ }
140
+ };
141
+ }
142
+ static async load({ config } = {}) {
143
+ return new AFSVault(afsVaultOptionsSchema.parse(config || {}));
144
+ }
145
+ async ensureLoaded() {
146
+ if (!this._loaded && this._masterKey) {
147
+ this._data = await readEncryptedVault(this.vaultPath, this._masterKey);
148
+ this._loaded = true;
149
+ }
150
+ if (!this._data) {
151
+ this._data = { secrets: {} };
152
+ this._loaded = true;
153
+ }
154
+ return this._data;
155
+ }
156
+ async save() {
157
+ if (!this._masterKey) throw new Error("Vault master key not set — cannot write");
158
+ if (!this._data) return;
159
+ await writeEncryptedVault(this.vaultPath, this._data, this._masterKey);
160
+ }
161
+ /** Set the master key at runtime (for lazy initialization). */
162
+ setMasterKey(key) {
163
+ this._masterKey = key;
164
+ this._loaded = false;
165
+ }
166
+ buildGroupEntry(group, secrets) {
167
+ return this.buildEntry(joinURL("/", group), { meta: { childrenCount: Object.keys(secrets).length } });
168
+ }
169
+ buildSecretEntry(group, name, value, includeContent = false) {
170
+ return this.buildEntry(joinURL("/", group, name), {
171
+ content: includeContent ? value : void 0,
172
+ meta: { size: value.length }
173
+ });
174
+ }
175
+ buildRootEntry(data) {
176
+ return this.buildEntry("/", { meta: {
177
+ childrenCount: Object.keys(data.secrets).length,
178
+ provider: "vault",
179
+ encrypted: true
180
+ } });
181
+ }
182
+ parsePath(ctx) {
183
+ return (ctx.params?.path || "").split("/").filter(Boolean);
184
+ }
185
+ async handleMeta(ctx) {
186
+ const data = await this.ensureLoaded();
187
+ const parts = this.parsePath(ctx);
188
+ const metaPath = parts.length === 0 ? "/.meta" : joinURL("/", ...parts, ".meta");
189
+ if (parts.length === 0) return this.buildEntry(metaPath, { meta: {
190
+ childrenCount: Object.keys(data.secrets).length,
191
+ provider: "vault",
192
+ encrypted: true
193
+ } });
194
+ if (parts.length === 1) {
195
+ const group = parts[0];
196
+ const secrets = data.secrets[group];
197
+ if (!secrets) throw new AFSNotFoundError(joinURL("/", group));
198
+ return this.buildEntry(metaPath, { meta: { childrenCount: Object.keys(secrets).length } });
199
+ }
200
+ if (parts.length === 2) {
201
+ const group = parts[0];
202
+ const name = parts[1];
203
+ const secrets = data.secrets[group];
204
+ if (!secrets || !(name in secrets)) throw new AFSNotFoundError(joinURL("/", group, name));
205
+ return this.buildEntry(metaPath, { meta: { size: secrets[name].length } });
206
+ }
207
+ throw new AFSNotFoundError(joinURL("/", ...parts));
208
+ }
209
+ async handleList(ctx) {
210
+ const data = await this.ensureLoaded();
211
+ const parts = this.parsePath(ctx);
212
+ if (parts.length === 0) return { data: Object.keys(data.secrets).sort().map((g) => this.buildGroupEntry(g, data.secrets[g])) };
213
+ if (parts.length === 1) {
214
+ const group = parts[0];
215
+ const secrets = data.secrets[group];
216
+ if (!secrets) throw new AFSNotFoundError(joinURL("/", group));
217
+ return { data: Object.keys(secrets).sort().map((n) => this.buildSecretEntry(group, n, secrets[n], false)) };
218
+ }
219
+ if (parts.length === 2) {
220
+ const group = parts[0];
221
+ const name = parts[1];
222
+ const secrets = data.secrets[group];
223
+ if (!secrets || !(name in secrets)) throw new AFSNotFoundError(joinURL("/", group, name));
224
+ return { data: [] };
225
+ }
226
+ throw new AFSNotFoundError(joinURL("/", ...parts));
227
+ }
228
+ async handleReadCapabilities() {
229
+ const operations = this.getOperationsDeclaration();
230
+ const manifest = {
231
+ schemaVersion: 1,
232
+ provider: this.name,
233
+ description: this.description,
234
+ tools: [],
235
+ actions: [],
236
+ operations
237
+ };
238
+ return this.buildEntry("/.meta/.capabilities", {
239
+ content: manifest,
240
+ meta: {
241
+ kind: "afs:capabilities",
242
+ operations
243
+ }
244
+ });
245
+ }
246
+ async handleRead(ctx) {
247
+ const data = await this.ensureLoaded();
248
+ const parts = this.parsePath(ctx);
249
+ if (parts.length === 0) return this.buildRootEntry(data);
250
+ if (parts.length === 1) {
251
+ const group = parts[0];
252
+ const secrets = data.secrets[group];
253
+ if (!secrets) throw new AFSNotFoundError(joinURL("/", group));
254
+ return this.buildGroupEntry(group, secrets);
255
+ }
256
+ if (parts.length === 2) {
257
+ const group = parts[0];
258
+ const name = parts[1];
259
+ const secrets = data.secrets[group];
260
+ if (!secrets || !(name in secrets)) throw new AFSNotFoundError(joinURL("/", group, name));
261
+ return this.buildSecretEntry(group, name, secrets[name], true);
262
+ }
263
+ throw new AFSNotFoundError(joinURL("/", ...parts));
264
+ }
265
+ async handleStat(ctx) {
266
+ const data = await this.ensureLoaded();
267
+ const parts = this.parsePath(ctx);
268
+ if (parts.length === 0) return { data: this.buildRootEntry(data) };
269
+ if (parts.length === 1) {
270
+ const group = parts[0];
271
+ const secrets = data.secrets[group];
272
+ if (!secrets) throw new AFSNotFoundError(joinURL("/", group));
273
+ return { data: this.buildGroupEntry(group, secrets) };
274
+ }
275
+ if (parts.length === 2) {
276
+ const group = parts[0];
277
+ const name = parts[1];
278
+ const secrets = data.secrets[group];
279
+ if (!secrets || !(name in secrets)) throw new AFSNotFoundError(joinURL("/", group, name));
280
+ return { data: this.buildSecretEntry(group, name, secrets[name], false) };
281
+ }
282
+ throw new AFSNotFoundError(joinURL("/", ...parts));
283
+ }
284
+ async handleWrite(ctx, payload) {
285
+ const data = await this.ensureLoaded();
286
+ const group = ctx.params.group;
287
+ const name = ctx.params.name;
288
+ assertSafeName(group);
289
+ assertSafeName(name);
290
+ const content = payload?.content;
291
+ if (typeof content !== "string") throw new Error("Vault secrets must be string values");
292
+ if (!data.secrets[group]) data.secrets[group] = {};
293
+ data.secrets[group][name] = content;
294
+ await this.save();
295
+ return { data: this.buildSecretEntry(group, name, content, true) };
296
+ }
297
+ async handleDelete(ctx) {
298
+ const data = await this.ensureLoaded();
299
+ const group = ctx.params.group;
300
+ const name = ctx.params.name;
301
+ const secrets = data.secrets[group];
302
+ if (!secrets || !(name in secrets)) throw new AFSNotFoundError(joinURL("/", group, name));
303
+ delete secrets[name];
304
+ if (Object.keys(secrets).length === 0) delete data.secrets[group];
305
+ await this.save();
306
+ return { message: `Deleted secret: ${joinURL("/", group, name)}` };
307
+ }
308
+ async handleDeleteGroup(ctx) {
309
+ const data = await this.ensureLoaded();
310
+ const group = ctx.params.group;
311
+ if (!data.secrets[group]) throw new AFSNotFoundError(joinURL("/", group));
312
+ delete data.secrets[group];
313
+ await this.save();
314
+ return { message: `Deleted group: ${joinURL("/", group)}` };
315
+ }
316
+ async handleSearch(ctx, query, options) {
317
+ const data = await this.ensureLoaded();
318
+ const pattern = (query || "").toLowerCase();
319
+ const groupFilter = this.parsePath(ctx)[0];
320
+ const limit = options?.limit;
321
+ const results = [];
322
+ const groups = groupFilter ? [groupFilter] : Object.keys(data.secrets);
323
+ for (const group of groups) {
324
+ const secrets = data.secrets[group];
325
+ if (!secrets) continue;
326
+ if (!groupFilter && group.toLowerCase().includes(pattern)) {
327
+ results.push(this.buildGroupEntry(group, secrets));
328
+ if (limit && results.length >= limit) break;
329
+ }
330
+ for (const name of Object.keys(secrets)) {
331
+ if (limit && results.length >= limit) break;
332
+ if (name.toLowerCase().includes(pattern)) results.push(this.buildSecretEntry(group, name, secrets[name], false));
333
+ }
334
+ if (limit && results.length >= limit) break;
335
+ }
336
+ return { data: limit ? results.slice(0, limit) : results };
337
+ }
338
+ async handleExplain(ctx) {
339
+ const parts = this.parsePath(ctx);
340
+ let content;
341
+ if (parts.length >= 2) content = `Secret "${parts[1]}" in group "${parts[0]}". Read to get the secret value.`;
342
+ else if (parts.length === 1) content = `Secret group "${parts[0]}". List to see available secrets.`;
343
+ else content = "AFS Vault — encrypted secret storage. List to see secret groups.";
344
+ return {
345
+ format: "text",
346
+ content
347
+ };
348
+ }
349
+ /** Get a secret value directly (bypasses AFS routing). */
350
+ async getSecret(group, name) {
351
+ return (await this.ensureLoaded()).secrets[group]?.[name];
352
+ }
353
+ /** Set a secret value directly. */
354
+ async setSecret(group, name, value) {
355
+ const data = await this.ensureLoaded();
356
+ if (!data.secrets[group]) data.secrets[group] = {};
357
+ data.secrets[group][name] = value;
358
+ await this.save();
359
+ }
360
+ /** Delete a secret directly. */
361
+ async deleteSecret(group, name) {
362
+ const data = await this.ensureLoaded();
363
+ const secrets = data.secrets[group];
364
+ if (!secrets || !(name in secrets)) return false;
365
+ delete secrets[name];
366
+ if (Object.keys(secrets).length === 0) delete data.secrets[group];
367
+ await this.save();
368
+ return true;
369
+ }
370
+ /** List all groups, or all secrets in a group. */
371
+ async listSecrets(group) {
372
+ const data = await this.ensureLoaded();
373
+ if (!group) return Object.keys(data.secrets).sort();
374
+ const secrets = data.secrets[group];
375
+ if (!secrets) return [];
376
+ return Object.keys(secrets).sort();
377
+ }
378
+ /** Get all secrets as flat map: "group/name" → value. Used for migration. */
379
+ async getAllSecrets() {
380
+ const data = await this.ensureLoaded();
381
+ const result = {};
382
+ for (const [group, secrets] of Object.entries(data.secrets)) for (const [name, value] of Object.entries(secrets)) result[`${group}/${name}`] = value;
383
+ return result;
384
+ }
385
+ };
386
+ __decorate([Meta("/:path*")], AFSVault.prototype, "handleMeta", null);
387
+ __decorate([List("/:path*")], AFSVault.prototype, "handleList", null);
388
+ __decorate([Read("/.meta/.capabilities")], AFSVault.prototype, "handleReadCapabilities", null);
389
+ __decorate([Read("/:path*")], AFSVault.prototype, "handleRead", null);
390
+ __decorate([Stat("/:path*")], AFSVault.prototype, "handleStat", null);
391
+ __decorate([Write("/:group/:name")], AFSVault.prototype, "handleWrite", null);
392
+ __decorate([Delete("/:group/:name")], AFSVault.prototype, "handleDelete", null);
393
+ __decorate([Delete("/:group")], AFSVault.prototype, "handleDeleteGroup", null);
394
+ __decorate([Search("/:path*")], AFSVault.prototype, "handleSearch", null);
395
+ __decorate([Explain("/:path*")], AFSVault.prototype, "handleExplain", null);
396
+ AFSVault.load = AFSVault.load;
397
+
398
+ //#endregion
399
+ export { AFSVault, generateMasterKey, resolveMasterKey, storeKeychain, vaultFileExists, writeEncryptedVault };
400
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../providers/vault/dist/index.mjs"],"sourcesContent":["import { deriveKeyFromPassphrase, generateMasterKey, readEncryptedVault, vaultFileExists, writeEncryptedVault } from \"./encrypted-file.mjs\";\nimport { __decorate } from \"./_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs\";\nimport { _resetCachedKeyForTesting, deleteKeychain, resolveMasterKey, storeKeychain } from \"./key-resolver.mjs\";\nimport { AFSNotFoundError } from \"@aigne/afs\";\nimport { AFSBaseProvider, Delete, Explain, List, Meta, Read, Search, Stat, Write } from \"@aigne/afs/provider\";\nimport { joinURL } from \"ufo\";\nimport { z } from \"zod\";\n\n//#region src/index.ts\n/**\n* AFS Vault Provider — Encrypted secret storage.\n*\n* Tree: /vault/{group}/{name} → secret value (string)\n* Encryption: AES-256-GCM, master key from keychain/env/passphrase\n* Security: admin (readwrite) + system (readonly) profiles\n* No exec — vault is passive storage.\n*/\nconst afsVaultOptionsSchema = z.object({\n\tname: z.string().optional().describe(\"Module name\"),\n\tdescription: z.string().optional().describe(\"Module description\"),\n\tvaultPath: z.string().describe(\"Path to the encrypted vault file\"),\n\taccessMode: z.enum([\"readonly\", \"readwrite\"]).optional().describe(\"Access mode\")\n});\n/** Prototype pollution guard — reject dangerous path segments. */\nconst DANGEROUS_NAMES = new Set([\n\t\"__proto__\",\n\t\"constructor\",\n\t\"prototype\"\n]);\nfunction assertSafeName(segment) {\n\tif (DANGEROUS_NAMES.has(segment)) throw new Error(`Forbidden path segment: ${segment}`);\n}\nvar AFSVault = class AFSVault extends AFSBaseProvider {\n\tname;\n\tdescription;\n\taccessMode;\n\tvaultPath;\n\t_masterKey;\n\t_data = null;\n\t_loaded = false;\n\tconstructor(options) {\n\t\tsuper();\n\t\tthis.name = options.name || \"vault\";\n\t\tthis.description = options.description || \"Encrypted secret storage\";\n\t\tthis.accessMode = options.accessMode || \"readwrite\";\n\t\tthis.vaultPath = options.vaultPath;\n\t\tthis._masterKey = options.masterKey;\n\t}\n\tstatic securityProfiles() {\n\t\treturn {\n\t\t\tadmin: {\n\t\t\t\tactionPolicy: \"full\",\n\t\t\t\taccessMode: \"readwrite\"\n\t\t\t},\n\t\t\tsystem: {\n\t\t\t\tactionPolicy: \"full\",\n\t\t\t\taccessMode: \"readonly\"\n\t\t\t}\n\t\t};\n\t}\n\tstatic schema() {\n\t\treturn afsVaultOptionsSchema;\n\t}\n\tstatic treeSchema() {\n\t\treturn {\n\t\t\toperations: [\n\t\t\t\t\"list\",\n\t\t\t\t\"read\",\n\t\t\t\t\"write\",\n\t\t\t\t\"delete\",\n\t\t\t\t\"search\",\n\t\t\t\t\"stat\",\n\t\t\t\t\"explain\"\n\t\t\t],\n\t\t\ttree: {\n\t\t\t\t\"/\": {\n\t\t\t\t\tkind: \"vault:root\",\n\t\t\t\t\toperations: [\"list\", \"read\"]\n\t\t\t\t},\n\t\t\t\t\"/{group}\": {\n\t\t\t\t\tkind: \"vault:group\",\n\t\t\t\t\toperations: [\n\t\t\t\t\t\t\"list\",\n\t\t\t\t\t\t\"read\",\n\t\t\t\t\t\t\"delete\"\n\t\t\t\t\t],\n\t\t\t\t\tdestructive: [\"delete\"]\n\t\t\t\t},\n\t\t\t\t\"/{group}/{name}\": {\n\t\t\t\t\tkind: \"vault:secret\",\n\t\t\t\t\toperations: [\n\t\t\t\t\t\t\"read\",\n\t\t\t\t\t\t\"write\",\n\t\t\t\t\t\t\"delete\"\n\t\t\t\t\t],\n\t\t\t\t\tdestructive: [\"delete\"]\n\t\t\t\t}\n\t\t\t},\n\t\t\tauth: {\n\t\t\t\ttype: \"custom\",\n\t\t\t\tenv: [\"AFS_VAULT_KEY\"]\n\t\t\t},\n\t\t\tbestFor: [\"secret storage\", \"API key management\"],\n\t\t\tnotFor: [\"large file storage\"]\n\t\t};\n\t}\n\tstatic manifest() {\n\t\treturn {\n\t\t\tname: \"vault\",\n\t\t\tdescription: \"Encrypted secret storage.\\n- Store API keys, tokens, and passwords with AES-256-GCM encryption\\n- Secrets organized in groups: /vault/{group}/{name}\\n- Security profiles: admin (readwrite), system (readonly)\",\n\t\t\turiTemplate: \"vault://{vaultPath+}\",\n\t\t\tcategory: \"security\",\n\t\t\tschema: z.object({ vaultPath: z.string() }),\n\t\t\ttags: [\n\t\t\t\t\"vault\",\n\t\t\t\t\"secrets\",\n\t\t\t\t\"encryption\",\n\t\t\t\t\"credentials\"\n\t\t\t],\n\t\t\tcapabilityTags: [\n\t\t\t\t\"read-write\",\n\t\t\t\t\"crud\",\n\t\t\t\t\"search\",\n\t\t\t\t\"auth:none\",\n\t\t\t\t\"local\"\n\t\t\t],\n\t\t\tsecurity: {\n\t\t\t\triskLevel: \"local\",\n\t\t\t\tresourceAccess: [\"local-filesystem\"],\n\t\t\t\tdataSensitivity: [\"credentials\"],\n\t\t\t\tnotes: [\"Stores encrypted secrets in a local file (AES-256-GCM)\", \"Master key stored in OS keychain or derived from passphrase\"]\n\t\t\t},\n\t\t\tcapabilities: {\n\t\t\t\tfilesystem: {\n\t\t\t\t\tread: true,\n\t\t\t\t\twrite: true\n\t\t\t\t},\n\t\t\t\tsecrets: [\"vault/master-key\"]\n\t\t\t}\n\t\t};\n\t}\n\tstatic async load({ config } = {}) {\n\t\treturn new AFSVault(afsVaultOptionsSchema.parse(config || {}));\n\t}\n\tasync ensureLoaded() {\n\t\tif (!this._loaded && this._masterKey) {\n\t\t\tthis._data = await readEncryptedVault(this.vaultPath, this._masterKey);\n\t\t\tthis._loaded = true;\n\t\t}\n\t\tif (!this._data) {\n\t\t\tthis._data = { secrets: {} };\n\t\t\tthis._loaded = true;\n\t\t}\n\t\treturn this._data;\n\t}\n\tasync save() {\n\t\tif (!this._masterKey) throw new Error(\"Vault master key not set — cannot write\");\n\t\tif (!this._data) return;\n\t\tawait writeEncryptedVault(this.vaultPath, this._data, this._masterKey);\n\t}\n\t/** Set the master key at runtime (for lazy initialization). */\n\tsetMasterKey(key) {\n\t\tthis._masterKey = key;\n\t\tthis._loaded = false;\n\t}\n\tbuildGroupEntry(group, secrets) {\n\t\treturn this.buildEntry(joinURL(\"/\", group), { meta: { childrenCount: Object.keys(secrets).length } });\n\t}\n\tbuildSecretEntry(group, name, value, includeContent = false) {\n\t\treturn this.buildEntry(joinURL(\"/\", group, name), {\n\t\t\tcontent: includeContent ? value : void 0,\n\t\t\tmeta: { size: value.length }\n\t\t});\n\t}\n\tbuildRootEntry(data) {\n\t\treturn this.buildEntry(\"/\", { meta: {\n\t\t\tchildrenCount: Object.keys(data.secrets).length,\n\t\t\tprovider: \"vault\",\n\t\t\tencrypted: true\n\t\t} });\n\t}\n\tparsePath(ctx) {\n\t\treturn (ctx.params?.path || \"\").split(\"/\").filter(Boolean);\n\t}\n\tasync handleMeta(ctx) {\n\t\tconst data = await this.ensureLoaded();\n\t\tconst parts = this.parsePath(ctx);\n\t\tconst metaPath = parts.length === 0 ? \"/.meta\" : joinURL(\"/\", ...parts, \".meta\");\n\t\tif (parts.length === 0) return this.buildEntry(metaPath, { meta: {\n\t\t\tchildrenCount: Object.keys(data.secrets).length,\n\t\t\tprovider: \"vault\",\n\t\t\tencrypted: true\n\t\t} });\n\t\tif (parts.length === 1) {\n\t\t\tconst group = parts[0];\n\t\t\tconst secrets = data.secrets[group];\n\t\t\tif (!secrets) throw new AFSNotFoundError(joinURL(\"/\", group));\n\t\t\treturn this.buildEntry(metaPath, { meta: { childrenCount: Object.keys(secrets).length } });\n\t\t}\n\t\tif (parts.length === 2) {\n\t\t\tconst group = parts[0];\n\t\t\tconst name = parts[1];\n\t\t\tconst secrets = data.secrets[group];\n\t\t\tif (!secrets || !(name in secrets)) throw new AFSNotFoundError(joinURL(\"/\", group, name));\n\t\t\treturn this.buildEntry(metaPath, { meta: { size: secrets[name].length } });\n\t\t}\n\t\tthrow new AFSNotFoundError(joinURL(\"/\", ...parts));\n\t}\n\tasync handleList(ctx) {\n\t\tconst data = await this.ensureLoaded();\n\t\tconst parts = this.parsePath(ctx);\n\t\tif (parts.length === 0) return { data: Object.keys(data.secrets).sort().map((g) => this.buildGroupEntry(g, data.secrets[g])) };\n\t\tif (parts.length === 1) {\n\t\t\tconst group = parts[0];\n\t\t\tconst secrets = data.secrets[group];\n\t\t\tif (!secrets) throw new AFSNotFoundError(joinURL(\"/\", group));\n\t\t\treturn { data: Object.keys(secrets).sort().map((n) => this.buildSecretEntry(group, n, secrets[n], false)) };\n\t\t}\n\t\tif (parts.length === 2) {\n\t\t\tconst group = parts[0];\n\t\t\tconst name = parts[1];\n\t\t\tconst secrets = data.secrets[group];\n\t\t\tif (!secrets || !(name in secrets)) throw new AFSNotFoundError(joinURL(\"/\", group, name));\n\t\t\treturn { data: [] };\n\t\t}\n\t\tthrow new AFSNotFoundError(joinURL(\"/\", ...parts));\n\t}\n\tasync handleReadCapabilities() {\n\t\tconst operations = this.getOperationsDeclaration();\n\t\tconst manifest = {\n\t\t\tschemaVersion: 1,\n\t\t\tprovider: this.name,\n\t\t\tdescription: this.description,\n\t\t\ttools: [],\n\t\t\tactions: [],\n\t\t\toperations\n\t\t};\n\t\treturn this.buildEntry(\"/.meta/.capabilities\", {\n\t\t\tcontent: manifest,\n\t\t\tmeta: {\n\t\t\t\tkind: \"afs:capabilities\",\n\t\t\t\toperations\n\t\t\t}\n\t\t});\n\t}\n\tasync handleRead(ctx) {\n\t\tconst data = await this.ensureLoaded();\n\t\tconst parts = this.parsePath(ctx);\n\t\tif (parts.length === 0) return this.buildRootEntry(data);\n\t\tif (parts.length === 1) {\n\t\t\tconst group = parts[0];\n\t\t\tconst secrets = data.secrets[group];\n\t\t\tif (!secrets) throw new AFSNotFoundError(joinURL(\"/\", group));\n\t\t\treturn this.buildGroupEntry(group, secrets);\n\t\t}\n\t\tif (parts.length === 2) {\n\t\t\tconst group = parts[0];\n\t\t\tconst name = parts[1];\n\t\t\tconst secrets = data.secrets[group];\n\t\t\tif (!secrets || !(name in secrets)) throw new AFSNotFoundError(joinURL(\"/\", group, name));\n\t\t\treturn this.buildSecretEntry(group, name, secrets[name], true);\n\t\t}\n\t\tthrow new AFSNotFoundError(joinURL(\"/\", ...parts));\n\t}\n\tasync handleStat(ctx) {\n\t\tconst data = await this.ensureLoaded();\n\t\tconst parts = this.parsePath(ctx);\n\t\tif (parts.length === 0) return { data: this.buildRootEntry(data) };\n\t\tif (parts.length === 1) {\n\t\t\tconst group = parts[0];\n\t\t\tconst secrets = data.secrets[group];\n\t\t\tif (!secrets) throw new AFSNotFoundError(joinURL(\"/\", group));\n\t\t\treturn { data: this.buildGroupEntry(group, secrets) };\n\t\t}\n\t\tif (parts.length === 2) {\n\t\t\tconst group = parts[0];\n\t\t\tconst name = parts[1];\n\t\t\tconst secrets = data.secrets[group];\n\t\t\tif (!secrets || !(name in secrets)) throw new AFSNotFoundError(joinURL(\"/\", group, name));\n\t\t\treturn { data: this.buildSecretEntry(group, name, secrets[name], false) };\n\t\t}\n\t\tthrow new AFSNotFoundError(joinURL(\"/\", ...parts));\n\t}\n\tasync handleWrite(ctx, payload) {\n\t\tconst data = await this.ensureLoaded();\n\t\tconst group = ctx.params.group;\n\t\tconst name = ctx.params.name;\n\t\tassertSafeName(group);\n\t\tassertSafeName(name);\n\t\tconst content = payload?.content;\n\t\tif (typeof content !== \"string\") throw new Error(\"Vault secrets must be string values\");\n\t\tif (!data.secrets[group]) data.secrets[group] = {};\n\t\tdata.secrets[group][name] = content;\n\t\tawait this.save();\n\t\treturn { data: this.buildSecretEntry(group, name, content, true) };\n\t}\n\tasync handleDelete(ctx) {\n\t\tconst data = await this.ensureLoaded();\n\t\tconst group = ctx.params.group;\n\t\tconst name = ctx.params.name;\n\t\tconst secrets = data.secrets[group];\n\t\tif (!secrets || !(name in secrets)) throw new AFSNotFoundError(joinURL(\"/\", group, name));\n\t\tdelete secrets[name];\n\t\tif (Object.keys(secrets).length === 0) delete data.secrets[group];\n\t\tawait this.save();\n\t\treturn { message: `Deleted secret: ${joinURL(\"/\", group, name)}` };\n\t}\n\tasync handleDeleteGroup(ctx) {\n\t\tconst data = await this.ensureLoaded();\n\t\tconst group = ctx.params.group;\n\t\tif (!data.secrets[group]) throw new AFSNotFoundError(joinURL(\"/\", group));\n\t\tdelete data.secrets[group];\n\t\tawait this.save();\n\t\treturn { message: `Deleted group: ${joinURL(\"/\", group)}` };\n\t}\n\tasync handleSearch(ctx, query, options) {\n\t\tconst data = await this.ensureLoaded();\n\t\tconst pattern = (query || \"\").toLowerCase();\n\t\tconst groupFilter = this.parsePath(ctx)[0];\n\t\tconst limit = options?.limit;\n\t\tconst results = [];\n\t\tconst groups = groupFilter ? [groupFilter] : Object.keys(data.secrets);\n\t\tfor (const group of groups) {\n\t\t\tconst secrets = data.secrets[group];\n\t\t\tif (!secrets) continue;\n\t\t\tif (!groupFilter && group.toLowerCase().includes(pattern)) {\n\t\t\t\tresults.push(this.buildGroupEntry(group, secrets));\n\t\t\t\tif (limit && results.length >= limit) break;\n\t\t\t}\n\t\t\tfor (const name of Object.keys(secrets)) {\n\t\t\t\tif (limit && results.length >= limit) break;\n\t\t\t\tif (name.toLowerCase().includes(pattern)) results.push(this.buildSecretEntry(group, name, secrets[name], false));\n\t\t\t}\n\t\t\tif (limit && results.length >= limit) break;\n\t\t}\n\t\treturn { data: limit ? results.slice(0, limit) : results };\n\t}\n\tasync handleExplain(ctx) {\n\t\tconst parts = this.parsePath(ctx);\n\t\tlet content;\n\t\tif (parts.length >= 2) content = `Secret \"${parts[1]}\" in group \"${parts[0]}\". Read to get the secret value.`;\n\t\telse if (parts.length === 1) content = `Secret group \"${parts[0]}\". List to see available secrets.`;\n\t\telse content = \"AFS Vault — encrypted secret storage. List to see secret groups.\";\n\t\treturn {\n\t\t\tformat: \"text\",\n\t\t\tcontent\n\t\t};\n\t}\n\t/** Get a secret value directly (bypasses AFS routing). */\n\tasync getSecret(group, name) {\n\t\treturn (await this.ensureLoaded()).secrets[group]?.[name];\n\t}\n\t/** Set a secret value directly. */\n\tasync setSecret(group, name, value) {\n\t\tconst data = await this.ensureLoaded();\n\t\tif (!data.secrets[group]) data.secrets[group] = {};\n\t\tdata.secrets[group][name] = value;\n\t\tawait this.save();\n\t}\n\t/** Delete a secret directly. */\n\tasync deleteSecret(group, name) {\n\t\tconst data = await this.ensureLoaded();\n\t\tconst secrets = data.secrets[group];\n\t\tif (!secrets || !(name in secrets)) return false;\n\t\tdelete secrets[name];\n\t\tif (Object.keys(secrets).length === 0) delete data.secrets[group];\n\t\tawait this.save();\n\t\treturn true;\n\t}\n\t/** List all groups, or all secrets in a group. */\n\tasync listSecrets(group) {\n\t\tconst data = await this.ensureLoaded();\n\t\tif (!group) return Object.keys(data.secrets).sort();\n\t\tconst secrets = data.secrets[group];\n\t\tif (!secrets) return [];\n\t\treturn Object.keys(secrets).sort();\n\t}\n\t/** Get all secrets as flat map: \"group/name\" → value. Used for migration. */\n\tasync getAllSecrets() {\n\t\tconst data = await this.ensureLoaded();\n\t\tconst result = {};\n\t\tfor (const [group, secrets] of Object.entries(data.secrets)) for (const [name, value] of Object.entries(secrets)) result[`${group}/${name}`] = value;\n\t\treturn result;\n\t}\n};\n__decorate([Meta(\"/:path*\")], AFSVault.prototype, \"handleMeta\", null);\n__decorate([List(\"/:path*\")], AFSVault.prototype, \"handleList\", null);\n__decorate([Read(\"/.meta/.capabilities\")], AFSVault.prototype, \"handleReadCapabilities\", null);\n__decorate([Read(\"/:path*\")], AFSVault.prototype, \"handleRead\", null);\n__decorate([Stat(\"/:path*\")], AFSVault.prototype, \"handleStat\", null);\n__decorate([Write(\"/:group/:name\")], AFSVault.prototype, \"handleWrite\", null);\n__decorate([Delete(\"/:group/:name\")], AFSVault.prototype, \"handleDelete\", null);\n__decorate([Delete(\"/:group\")], AFSVault.prototype, \"handleDeleteGroup\", null);\n__decorate([Search(\"/:path*\")], AFSVault.prototype, \"handleSearch\", null);\n__decorate([Explain(\"/:path*\")], AFSVault.prototype, \"handleExplain\", null);\nAFSVault.load = AFSVault.load;\n\n//#endregion\nexport { AFSVault, _resetCachedKeyForTesting, deleteKeychain, deriveKeyFromPassphrase, generateMasterKey, readEncryptedVault, resolveMasterKey, storeKeychain, vaultFileExists, writeEncryptedVault };\n//# sourceMappingURL=index.mjs.map"],"mappings":"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"}
@@ -0,0 +1,181 @@
1
+ const require_rolldown_runtime = require('../../../_virtual/rolldown_runtime.cjs');
2
+ const require_encrypted_file = require('./encrypted-file.cjs');
3
+ let node_crypto = require("node:crypto");
4
+ let node_child_process = require("node:child_process");
5
+ let node_os = require("node:os");
6
+ let node_util = require("node:util");
7
+
8
+ //#region ../../providers/vault/dist/key-resolver.mjs
9
+ /**
10
+ * Master key resolution chain.
11
+ *
12
+ * Priority (first available wins):
13
+ * 1. AFS_VAULT_KEY environment variable (hex-encoded) — explicit override for CI/automation
14
+ * 2. OS Keychain (macOS Keychain / Linux Secret Service) — desktop default
15
+ * 3. Passphrase prompt (key derived via crypto.scrypt, cached in memory)
16
+ *
17
+ * No native dependencies — uses system CLIs for keychain access.
18
+ */
19
+ const execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
20
+ const SERVICE_NAME = "afs-vault";
21
+ const ACCOUNT_NAME = "master-key";
22
+ const KEY_LENGTH = 32;
23
+ /** Cached key for the process lifetime (avoids repeated prompts). */
24
+ let cachedKey = null;
25
+ /**
26
+ * Resolve master key using the priority chain.
27
+ *
28
+ * @param interactive - If true, allows passphrase prompt. Default true.
29
+ * @param vaultPath - Path to vault file (used for per-file salt in passphrase mode).
30
+ * @returns 32-byte master key buffer.
31
+ */
32
+ async function resolveMasterKey(interactive = true, vaultPath) {
33
+ if (cachedKey) return cachedKey;
34
+ const envKey = process.env.AFS_VAULT_KEY;
35
+ if (envKey) {
36
+ const buf = Buffer.from(envKey, "hex");
37
+ if (buf.length !== KEY_LENGTH) throw new Error("AFS_VAULT_KEY must be a 64-character hex string (32 bytes)");
38
+ cachedKey = buf;
39
+ return buf;
40
+ }
41
+ const keychainKey = await readKeychain();
42
+ if (keychainKey) {
43
+ cachedKey = keychainKey;
44
+ return keychainKey;
45
+ }
46
+ if (!interactive) throw new Error("No vault master key found. Set AFS_VAULT_KEY environment variable (64-char hex),\nor run in interactive mode to enter a passphrase.");
47
+ const key = require_encrypted_file.deriveKeyFromPassphrase(await promptPassphrase(), (vaultPath ? await require_encrypted_file.readVaultSalt(vaultPath) : null) ?? await readPassphraseSalt());
48
+ cachedKey = key;
49
+ return key;
50
+ }
51
+ /**
52
+ * Store a master key in the OS keychain.
53
+ * Returns true on success, false if keychain is unavailable.
54
+ */
55
+ async function storeKeychain(masterKey) {
56
+ const hexKey = masterKey.toString("hex");
57
+ const os = (0, node_os.platform)();
58
+ try {
59
+ if (os === "darwin") {
60
+ await execFileAsync("security", [
61
+ "add-generic-password",
62
+ "-s",
63
+ SERVICE_NAME,
64
+ "-a",
65
+ ACCOUNT_NAME,
66
+ "-w",
67
+ hexKey,
68
+ "-U"
69
+ ]);
70
+ return true;
71
+ }
72
+ if (os === "linux") {
73
+ const { spawn } = await import("node:child_process");
74
+ return new Promise((resolve) => {
75
+ const proc = spawn("secret-tool", [
76
+ "store",
77
+ "--label",
78
+ "AFS Vault Master Key",
79
+ "service",
80
+ SERVICE_NAME,
81
+ "account",
82
+ ACCOUNT_NAME
83
+ ]);
84
+ proc.stdin.write(hexKey);
85
+ proc.stdin.end();
86
+ proc.on("close", (code) => resolve(code === 0));
87
+ proc.on("error", () => resolve(false));
88
+ });
89
+ }
90
+ return false;
91
+ } catch {
92
+ return false;
93
+ }
94
+ }
95
+ /**
96
+ * Read master key from OS keychain.
97
+ * Returns null if not found or keychain unavailable.
98
+ */
99
+ async function readKeychain() {
100
+ const os = (0, node_os.platform)();
101
+ try {
102
+ if (os === "darwin") {
103
+ const { stdout } = await execFileAsync("security", [
104
+ "find-generic-password",
105
+ "-s",
106
+ SERVICE_NAME,
107
+ "-a",
108
+ ACCOUNT_NAME,
109
+ "-w"
110
+ ]);
111
+ const hex = stdout.trim();
112
+ if (hex.length === KEY_LENGTH * 2) return Buffer.from(hex, "hex");
113
+ return null;
114
+ }
115
+ if (os === "linux") {
116
+ const { stdout } = await execFileAsync("secret-tool", [
117
+ "lookup",
118
+ "service",
119
+ SERVICE_NAME,
120
+ "account",
121
+ ACCOUNT_NAME
122
+ ]);
123
+ const hex = stdout.trim();
124
+ if (hex.length === KEY_LENGTH * 2) return Buffer.from(hex, "hex");
125
+ return null;
126
+ }
127
+ return null;
128
+ } catch {
129
+ return null;
130
+ }
131
+ }
132
+ /**
133
+ * Prompt user for passphrase via stdin (no echo).
134
+ */
135
+ async function promptPassphrase() {
136
+ const { createInterface } = await import("node:readline");
137
+ return new Promise((resolve, reject) => {
138
+ const rl = createInterface({
139
+ input: process.stdin,
140
+ output: process.stderr,
141
+ terminal: true
142
+ });
143
+ if (process.stdin.isTTY) {
144
+ process.stderr.write("Vault passphrase: ");
145
+ rl._writeToOutput = () => {};
146
+ }
147
+ rl.question("", (answer) => {
148
+ rl.close();
149
+ if (process.stdin.isTTY) process.stderr.write("\n");
150
+ if (!answer || answer.trim() === "") {
151
+ reject(/* @__PURE__ */ new Error("Passphrase cannot be empty"));
152
+ return;
153
+ }
154
+ resolve(answer);
155
+ });
156
+ });
157
+ }
158
+ /**
159
+ * Read or create the passphrase salt file.
160
+ *
161
+ * The salt is stored alongside the vault file to enable consistent
162
+ * key derivation from the same passphrase.
163
+ */
164
+ async function readPassphraseSalt() {
165
+ const { homedir } = await import("node:os");
166
+ const { join } = await import("node:path");
167
+ const { readFile, writeFile, mkdir } = await import("node:fs/promises");
168
+ const saltPath = join(homedir(), ".afs-config", "vault-salt");
169
+ try {
170
+ const data = await readFile(saltPath);
171
+ if (data.length === 32) return data;
172
+ } catch {}
173
+ const salt = (0, node_crypto.randomBytes)(32);
174
+ await mkdir(join(homedir(), ".afs-config"), { recursive: true });
175
+ await writeFile(saltPath, salt, { mode: 384 });
176
+ return salt;
177
+ }
178
+
179
+ //#endregion
180
+ exports.resolveMasterKey = resolveMasterKey;
181
+ exports.storeKeychain = storeKeychain;