@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.
- package/dist/cli.cjs +3 -2
- package/dist/cli.mjs +3 -2
- package/dist/cli.mjs.map +1 -1
- package/dist/config/afs-loader.cjs +64 -315
- package/dist/config/afs-loader.d.cts.map +1 -1
- package/dist/config/afs-loader.d.mts +2 -1
- package/dist/config/afs-loader.d.mts.map +1 -1
- package/dist/config/afs-loader.mjs +59 -310
- package/dist/config/afs-loader.mjs.map +1 -1
- package/dist/config/credential-helpers.cjs +291 -0
- package/dist/config/credential-helpers.d.mts +2 -0
- package/dist/config/credential-helpers.mjs +288 -0
- package/dist/config/credential-helpers.mjs.map +1 -0
- package/dist/config/loader.cjs +3 -1
- package/dist/config/loader.mjs +3 -2
- package/dist/config/loader.mjs.map +1 -1
- package/dist/config/program-install.cjs +276 -0
- package/dist/config/program-install.d.mts +1 -0
- package/dist/config/program-install.mjs +273 -0
- package/dist/config/program-install.mjs.map +1 -0
- package/dist/core/commands/connect.cjs +53 -0
- package/dist/core/commands/connect.d.mts +2 -0
- package/dist/core/commands/connect.mjs +55 -0
- package/dist/core/commands/connect.mjs.map +1 -0
- package/dist/core/commands/daemon.cjs +207 -0
- package/dist/core/commands/daemon.d.mts +2 -0
- package/dist/core/commands/daemon.mjs +208 -0
- package/dist/core/commands/daemon.mjs.map +1 -0
- package/dist/core/commands/explain.cjs +3 -1
- package/dist/core/commands/explain.mjs +3 -1
- package/dist/core/commands/explain.mjs.map +1 -1
- package/dist/core/commands/explore.cjs +47 -12
- package/dist/core/commands/explore.mjs +47 -12
- package/dist/core/commands/explore.mjs.map +1 -1
- package/dist/core/commands/gen-agent-md.cjs +126 -0
- package/dist/core/commands/gen-agent-md.d.mts +2 -0
- package/dist/core/commands/gen-agent-md.mjs +125 -0
- package/dist/core/commands/gen-agent-md.mjs.map +1 -0
- package/dist/core/commands/index.cjs +13 -1
- package/dist/core/commands/index.d.cts.map +1 -1
- package/dist/core/commands/index.d.mts +6 -0
- package/dist/core/commands/index.d.mts.map +1 -1
- package/dist/core/commands/index.mjs +13 -1
- package/dist/core/commands/index.mjs.map +1 -1
- package/dist/core/commands/install.cjs +91 -0
- package/dist/core/commands/install.d.mts +2 -0
- package/dist/core/commands/install.mjs +92 -0
- package/dist/core/commands/install.mjs.map +1 -0
- package/dist/core/commands/ls.cjs +14 -2
- package/dist/core/commands/ls.d.cts +2 -0
- package/dist/core/commands/ls.d.cts.map +1 -1
- package/dist/core/commands/ls.d.mts +2 -0
- package/dist/core/commands/ls.d.mts.map +1 -1
- package/dist/core/commands/ls.mjs +14 -2
- package/dist/core/commands/ls.mjs.map +1 -1
- package/dist/core/commands/mcp-bridge.cjs +201 -0
- package/dist/core/commands/mcp-bridge.d.mts +2 -0
- package/dist/core/commands/mcp-bridge.mjs +201 -0
- package/dist/core/commands/mcp-bridge.mjs.map +1 -0
- package/dist/core/commands/read.cjs +20 -7
- package/dist/core/commands/read.d.cts +2 -0
- package/dist/core/commands/read.d.cts.map +1 -1
- package/dist/core/commands/read.d.mts +2 -0
- package/dist/core/commands/read.d.mts.map +1 -1
- package/dist/core/commands/read.mjs +20 -7
- package/dist/core/commands/read.mjs.map +1 -1
- package/dist/core/commands/search.cjs +5 -1
- package/dist/core/commands/search.mjs +5 -1
- package/dist/core/commands/search.mjs.map +1 -1
- package/dist/core/commands/stat.mjs.map +1 -1
- package/dist/core/commands/types.d.cts +2 -0
- package/dist/core/commands/types.d.cts.map +1 -1
- package/dist/core/commands/types.d.mts +2 -0
- package/dist/core/commands/types.d.mts.map +1 -1
- package/dist/core/commands/types.mjs.map +1 -1
- package/dist/core/commands/vault.cjs +289 -0
- package/dist/core/commands/vault.d.mts +2 -0
- package/dist/core/commands/vault.mjs +289 -0
- package/dist/core/commands/vault.mjs.map +1 -0
- package/dist/core/commands/write.cjs +19 -6
- package/dist/core/commands/write.d.cts +2 -1
- package/dist/core/commands/write.d.cts.map +1 -1
- package/dist/core/commands/write.d.mts +2 -1
- package/dist/core/commands/write.d.mts.map +1 -1
- package/dist/core/commands/write.mjs +19 -6
- package/dist/core/commands/write.mjs.map +1 -1
- package/dist/core/executor/index.cjs +95 -19
- package/dist/core/executor/index.d.cts +4 -0
- package/dist/core/executor/index.d.cts.map +1 -1
- package/dist/core/executor/index.d.mts +4 -0
- package/dist/core/executor/index.d.mts.map +1 -1
- package/dist/core/executor/index.mjs +95 -19
- package/dist/core/executor/index.mjs.map +1 -1
- package/dist/core/formatters/index.d.mts +1 -0
- package/dist/core/formatters/install.cjs +21 -0
- package/dist/core/formatters/install.d.mts +1 -0
- package/dist/core/formatters/install.mjs +19 -0
- package/dist/core/formatters/install.mjs.map +1 -0
- package/dist/core/formatters/vault.cjs +36 -0
- package/dist/core/formatters/vault.mjs +32 -0
- package/dist/core/formatters/vault.mjs.map +1 -0
- package/dist/credential/index.d.mts +2 -1
- package/dist/credential/mcp-auth-context.cjs +21 -5
- package/dist/credential/mcp-auth-context.mjs +21 -5
- package/dist/credential/mcp-auth-context.mjs.map +1 -1
- package/dist/credential/resolver.cjs +7 -2
- package/dist/credential/resolver.mjs +7 -2
- package/dist/credential/resolver.mjs.map +1 -1
- package/dist/credential/vault-store.d.mts +1 -0
- package/dist/daemon/config-manager.cjs +279 -0
- package/dist/daemon/config-manager.mjs +279 -0
- package/dist/daemon/config-manager.mjs.map +1 -0
- package/dist/daemon/manager.cjs +164 -0
- package/dist/daemon/manager.mjs +157 -0
- package/dist/daemon/manager.mjs.map +1 -0
- package/dist/daemon/server.cjs +220 -0
- package/dist/daemon/server.mjs +220 -0
- package/dist/daemon/server.mjs.map +1 -0
- package/dist/mcp/http-transport.cjs +14 -1
- package/dist/mcp/http-transport.mjs +14 -1
- package/dist/mcp/http-transport.mjs.map +1 -1
- package/dist/mcp/server.cjs +4 -2
- package/dist/mcp/server.mjs +4 -2
- package/dist/mcp/server.mjs.map +1 -1
- package/dist/mcp/tools.cjs +62 -12
- package/dist/mcp/tools.mjs +62 -12
- package/dist/mcp/tools.mjs.map +1 -1
- package/dist/program/daemon-integration.cjs +46 -0
- package/dist/program/daemon-integration.mjs +45 -0
- package/dist/program/daemon-integration.mjs.map +1 -0
- package/dist/program/program-manager.cjs +162 -0
- package/dist/program/program-manager.mjs +162 -0
- package/dist/program/program-manager.mjs.map +1 -0
- package/dist/program/trigger-scanner.cjs +148 -0
- package/dist/program/trigger-scanner.mjs +148 -0
- package/dist/program/trigger-scanner.mjs.map +1 -0
- package/dist/providers/vault/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
- package/dist/providers/vault/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +11 -0
- package/dist/providers/vault/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs.map +1 -0
- package/dist/providers/vault/dist/encrypted-file.cjs +158 -0
- package/dist/providers/vault/dist/encrypted-file.mjs +153 -0
- package/dist/providers/vault/dist/encrypted-file.mjs.map +1 -0
- package/dist/providers/vault/dist/index.cjs +405 -0
- package/dist/providers/vault/dist/index.mjs +400 -0
- package/dist/providers/vault/dist/index.mjs.map +1 -0
- package/dist/providers/vault/dist/key-resolver.cjs +181 -0
- package/dist/providers/vault/dist/key-resolver.mjs +180 -0
- package/dist/providers/vault/dist/key-resolver.mjs.map +1 -0
- package/dist/repl.cjs +105 -14
- package/dist/repl.d.cts.map +1 -1
- package/dist/repl.d.mts.map +1 -1
- package/dist/repl.mjs +105 -14
- package/dist/repl.mjs.map +1 -1
- package/package.json +29 -22
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
|
|
2
|
+
const require_vault = require('../formatters/vault.cjs');
|
|
3
|
+
let node_os = require("node:os");
|
|
4
|
+
let node_path = require("node:path");
|
|
5
|
+
|
|
6
|
+
//#region src/core/commands/vault.ts
|
|
7
|
+
/**
|
|
8
|
+
* vault Command - Core Implementation
|
|
9
|
+
*
|
|
10
|
+
* Vault management commands for encrypted secret storage.
|
|
11
|
+
* Subcommands: init, get, set, list, delete.
|
|
12
|
+
*/
|
|
13
|
+
/** Default vault file location */
|
|
14
|
+
const DEFAULT_VAULT_DIR = ".afs-config";
|
|
15
|
+
const DEFAULT_VAULT_FILE = "vault.enc";
|
|
16
|
+
function defaultVaultPath() {
|
|
17
|
+
return (0, node_path.join)((0, node_os.homedir)(), DEFAULT_VAULT_DIR, DEFAULT_VAULT_FILE);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolve master key using the vault's key resolution chain:
|
|
21
|
+
* OS keychain → AFS_VAULT_KEY env var → passphrase prompt.
|
|
22
|
+
*/
|
|
23
|
+
async function loadMasterKey() {
|
|
24
|
+
const { resolveMasterKey } = await Promise.resolve().then(() => require("../../providers/vault/dist/index.cjs"));
|
|
25
|
+
return resolveMasterKey();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create vault command factory (with subcommands)
|
|
29
|
+
*/
|
|
30
|
+
function createVaultCommand(options) {
|
|
31
|
+
return {
|
|
32
|
+
command: "vault",
|
|
33
|
+
describe: "Encrypted secret storage",
|
|
34
|
+
builder: (yargs) => yargs.command(createVaultInitSubcommand(options)).command(createVaultGetSubcommand(options)).command(createVaultSetSubcommand(options)).command(createVaultListSubcommand(options)).command(createVaultDeleteSubcommand(options)).demandCommand(1, "Please specify a subcommand").alias("help", "h"),
|
|
35
|
+
handler: () => {}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function createVaultInitSubcommand(options) {
|
|
39
|
+
return {
|
|
40
|
+
command: "init",
|
|
41
|
+
describe: "Initialize a new encrypted vault",
|
|
42
|
+
builder: {
|
|
43
|
+
path: {
|
|
44
|
+
type: "string",
|
|
45
|
+
description: "Path for vault file",
|
|
46
|
+
default: defaultVaultPath()
|
|
47
|
+
},
|
|
48
|
+
migrate: {
|
|
49
|
+
type: "boolean",
|
|
50
|
+
description: "Migrate existing credentials.toml into vault",
|
|
51
|
+
default: true
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
handler: async (argv) => {
|
|
55
|
+
const vaultPath = argv.path ?? defaultVaultPath();
|
|
56
|
+
const { generateMasterKey, writeEncryptedVault, vaultFileExists } = await Promise.resolve().then(() => require("../../providers/vault/dist/index.cjs"));
|
|
57
|
+
if (await vaultFileExists(vaultPath)) throw new Error(`Vault already exists at ${vaultPath}. Delete it first to re-initialize.`);
|
|
58
|
+
const masterKey = generateMasterKey();
|
|
59
|
+
await writeEncryptedVault(vaultPath, { secrets: {} }, masterKey);
|
|
60
|
+
const { storeKeychain } = await Promise.resolve().then(() => require("../../providers/vault/dist/index.cjs"));
|
|
61
|
+
const stored = await storeKeychain(masterKey);
|
|
62
|
+
let migrated = 0;
|
|
63
|
+
if (argv.migrate !== false) migrated = await migrateFromToml(vaultPath, masterKey);
|
|
64
|
+
const hexKey = masterKey.toString("hex");
|
|
65
|
+
options.onResult({
|
|
66
|
+
command: "vault init",
|
|
67
|
+
result: {
|
|
68
|
+
success: true,
|
|
69
|
+
vaultPath,
|
|
70
|
+
migrated,
|
|
71
|
+
keychainStored: stored
|
|
72
|
+
},
|
|
73
|
+
format: (result, view) => {
|
|
74
|
+
const base = require_vault.formatVaultInitOutput(result, view);
|
|
75
|
+
if (view === "json") return base;
|
|
76
|
+
const lines = [base];
|
|
77
|
+
if (stored) lines.push("\nMaster key stored in OS keychain.");
|
|
78
|
+
else lines.push(`\nMaster key (save this securely — it cannot be recovered):\n${hexKey}`, `\nSet it as: export AFS_VAULT_KEY=${hexKey}`);
|
|
79
|
+
return lines.join("");
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Migrate credentials.toml entries into the vault.
|
|
87
|
+
* Returns count of migrated credential groups.
|
|
88
|
+
*/
|
|
89
|
+
async function migrateFromToml(vaultPath, masterKey) {
|
|
90
|
+
const { readFile } = await import("node:fs/promises");
|
|
91
|
+
const { parse } = await import("smol-toml");
|
|
92
|
+
const { AFSVault } = await Promise.resolve().then(() => require("../../providers/vault/dist/index.cjs"));
|
|
93
|
+
const tomlPath = (0, node_path.join)((0, node_os.homedir)(), DEFAULT_VAULT_DIR, "credentials.toml");
|
|
94
|
+
let content;
|
|
95
|
+
try {
|
|
96
|
+
content = await readFile(tomlPath, "utf-8");
|
|
97
|
+
} catch {
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
let data;
|
|
101
|
+
try {
|
|
102
|
+
data = parse(content);
|
|
103
|
+
} catch {
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
const vault = new AFSVault({
|
|
107
|
+
vaultPath,
|
|
108
|
+
masterKey,
|
|
109
|
+
accessMode: "readwrite"
|
|
110
|
+
});
|
|
111
|
+
let count = 0;
|
|
112
|
+
for (const [group, values] of Object.entries(data)) {
|
|
113
|
+
if (typeof values !== "object" || values === null || Array.isArray(values)) continue;
|
|
114
|
+
const safeGroup = group.replace(/^[a-z0-9]+:\/\//, "").replace(/[^a-zA-Z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
115
|
+
if (!safeGroup) continue;
|
|
116
|
+
for (const [key, val] of Object.entries(values)) if (typeof val === "string") await vault.setSecret(safeGroup, key, val);
|
|
117
|
+
count++;
|
|
118
|
+
}
|
|
119
|
+
return count;
|
|
120
|
+
}
|
|
121
|
+
function createVaultGetSubcommand(options) {
|
|
122
|
+
return {
|
|
123
|
+
command: "get <group> <name>",
|
|
124
|
+
describe: "Read a secret value",
|
|
125
|
+
builder: {
|
|
126
|
+
group: {
|
|
127
|
+
type: "string",
|
|
128
|
+
demandOption: true,
|
|
129
|
+
description: "Secret group (e.g., aws, github)"
|
|
130
|
+
},
|
|
131
|
+
name: {
|
|
132
|
+
type: "string",
|
|
133
|
+
demandOption: true,
|
|
134
|
+
description: "Secret name (e.g., token, access-key-id)"
|
|
135
|
+
},
|
|
136
|
+
"vault-path": {
|
|
137
|
+
type: "string",
|
|
138
|
+
description: "Path to vault file"
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
handler: async (argv) => {
|
|
142
|
+
const { AFSVault } = await Promise.resolve().then(() => require("../../providers/vault/dist/index.cjs"));
|
|
143
|
+
const value = await new AFSVault({
|
|
144
|
+
vaultPath: argv["vault-path"] ?? defaultVaultPath(),
|
|
145
|
+
masterKey: await loadMasterKey(),
|
|
146
|
+
accessMode: "readonly"
|
|
147
|
+
}).getSecret(argv.group, argv.name);
|
|
148
|
+
if (value === void 0) throw new Error(`Secret not found: ${argv.group}/${argv.name}`);
|
|
149
|
+
options.onResult({
|
|
150
|
+
command: "vault get",
|
|
151
|
+
result: {
|
|
152
|
+
group: argv.group,
|
|
153
|
+
name: argv.name,
|
|
154
|
+
value
|
|
155
|
+
},
|
|
156
|
+
format: require_vault.formatVaultGetOutput
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function createVaultSetSubcommand(options) {
|
|
162
|
+
return {
|
|
163
|
+
command: "set <group> <name> <value>",
|
|
164
|
+
describe: "Store a secret",
|
|
165
|
+
builder: {
|
|
166
|
+
group: {
|
|
167
|
+
type: "string",
|
|
168
|
+
demandOption: true,
|
|
169
|
+
description: "Secret group (e.g., aws, github)"
|
|
170
|
+
},
|
|
171
|
+
name: {
|
|
172
|
+
type: "string",
|
|
173
|
+
demandOption: true,
|
|
174
|
+
description: "Secret name (e.g., token, access-key-id)"
|
|
175
|
+
},
|
|
176
|
+
value: {
|
|
177
|
+
type: "string",
|
|
178
|
+
demandOption: true,
|
|
179
|
+
description: "Secret value"
|
|
180
|
+
},
|
|
181
|
+
"vault-path": {
|
|
182
|
+
type: "string",
|
|
183
|
+
description: "Path to vault file"
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
handler: async (argv) => {
|
|
187
|
+
const { AFSVault } = await Promise.resolve().then(() => require("../../providers/vault/dist/index.cjs"));
|
|
188
|
+
await new AFSVault({
|
|
189
|
+
vaultPath: argv["vault-path"] ?? defaultVaultPath(),
|
|
190
|
+
masterKey: await loadMasterKey(),
|
|
191
|
+
accessMode: "readwrite"
|
|
192
|
+
}).setSecret(argv.group, argv.name, argv.value);
|
|
193
|
+
options.onResult({
|
|
194
|
+
command: "vault set",
|
|
195
|
+
result: {
|
|
196
|
+
group: argv.group,
|
|
197
|
+
name: argv.name
|
|
198
|
+
},
|
|
199
|
+
format: require_vault.formatVaultSetOutput
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function createVaultListSubcommand(options) {
|
|
205
|
+
return {
|
|
206
|
+
command: ["list [group]", "ls [group]"],
|
|
207
|
+
describe: "List secrets",
|
|
208
|
+
builder: {
|
|
209
|
+
group: {
|
|
210
|
+
type: "string",
|
|
211
|
+
description: "Secret group to list (omit for all groups)"
|
|
212
|
+
},
|
|
213
|
+
"vault-path": {
|
|
214
|
+
type: "string",
|
|
215
|
+
description: "Path to vault file"
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
handler: async (argv) => {
|
|
219
|
+
const { AFSVault } = await Promise.resolve().then(() => require("../../providers/vault/dist/index.cjs"));
|
|
220
|
+
const secrets = await new AFSVault({
|
|
221
|
+
vaultPath: argv["vault-path"] ?? defaultVaultPath(),
|
|
222
|
+
masterKey: await loadMasterKey(),
|
|
223
|
+
accessMode: "readonly"
|
|
224
|
+
}).listSecrets(argv.group);
|
|
225
|
+
options.onResult({
|
|
226
|
+
command: "vault list",
|
|
227
|
+
result: {
|
|
228
|
+
group: argv.group,
|
|
229
|
+
secrets
|
|
230
|
+
},
|
|
231
|
+
format: require_vault.formatVaultListOutput
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function createVaultDeleteSubcommand(options) {
|
|
237
|
+
return {
|
|
238
|
+
command: ["delete <group> [name]", "rm <group> [name]"],
|
|
239
|
+
describe: "Delete a secret or group",
|
|
240
|
+
builder: {
|
|
241
|
+
group: {
|
|
242
|
+
type: "string",
|
|
243
|
+
demandOption: true,
|
|
244
|
+
description: "Secret group"
|
|
245
|
+
},
|
|
246
|
+
name: {
|
|
247
|
+
type: "string",
|
|
248
|
+
description: "Secret name (omit to delete entire group)"
|
|
249
|
+
},
|
|
250
|
+
"vault-path": {
|
|
251
|
+
type: "string",
|
|
252
|
+
description: "Path to vault file"
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
handler: async (argv) => {
|
|
256
|
+
const { AFSVault } = await Promise.resolve().then(() => require("../../providers/vault/dist/index.cjs"));
|
|
257
|
+
const vault = new AFSVault({
|
|
258
|
+
vaultPath: argv["vault-path"] ?? defaultVaultPath(),
|
|
259
|
+
masterKey: await loadMasterKey(),
|
|
260
|
+
accessMode: "readwrite"
|
|
261
|
+
});
|
|
262
|
+
let deleted;
|
|
263
|
+
if (argv.name) deleted = await vault.deleteSecret(argv.group, argv.name);
|
|
264
|
+
else {
|
|
265
|
+
const secrets = await vault.listSecrets(argv.group);
|
|
266
|
+
if (secrets.length === 0) deleted = false;
|
|
267
|
+
else {
|
|
268
|
+
for (const secretPath of secrets) {
|
|
269
|
+
const secretName = secretPath.split("/").pop();
|
|
270
|
+
await vault.deleteSecret(argv.group, secretName);
|
|
271
|
+
}
|
|
272
|
+
deleted = true;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
options.onResult({
|
|
276
|
+
command: "vault delete",
|
|
277
|
+
result: {
|
|
278
|
+
group: argv.group,
|
|
279
|
+
name: argv.name,
|
|
280
|
+
deleted
|
|
281
|
+
},
|
|
282
|
+
format: require_vault.formatVaultDeleteOutput
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
//#endregion
|
|
289
|
+
exports.createVaultCommand = createVaultCommand;
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { formatVaultDeleteOutput, formatVaultGetOutput, formatVaultInitOutput, formatVaultListOutput, formatVaultSetOutput } from "../formatters/vault.mjs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
//#region src/core/commands/vault.ts
|
|
6
|
+
/**
|
|
7
|
+
* vault Command - Core Implementation
|
|
8
|
+
*
|
|
9
|
+
* Vault management commands for encrypted secret storage.
|
|
10
|
+
* Subcommands: init, get, set, list, delete.
|
|
11
|
+
*/
|
|
12
|
+
/** Default vault file location */
|
|
13
|
+
const DEFAULT_VAULT_DIR = ".afs-config";
|
|
14
|
+
const DEFAULT_VAULT_FILE = "vault.enc";
|
|
15
|
+
function defaultVaultPath() {
|
|
16
|
+
return join(homedir(), DEFAULT_VAULT_DIR, DEFAULT_VAULT_FILE);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Resolve master key using the vault's key resolution chain:
|
|
20
|
+
* OS keychain → AFS_VAULT_KEY env var → passphrase prompt.
|
|
21
|
+
*/
|
|
22
|
+
async function loadMasterKey() {
|
|
23
|
+
const { resolveMasterKey } = await import("../../providers/vault/dist/index.mjs");
|
|
24
|
+
return resolveMasterKey();
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create vault command factory (with subcommands)
|
|
28
|
+
*/
|
|
29
|
+
function createVaultCommand(options) {
|
|
30
|
+
return {
|
|
31
|
+
command: "vault",
|
|
32
|
+
describe: "Encrypted secret storage",
|
|
33
|
+
builder: (yargs) => yargs.command(createVaultInitSubcommand(options)).command(createVaultGetSubcommand(options)).command(createVaultSetSubcommand(options)).command(createVaultListSubcommand(options)).command(createVaultDeleteSubcommand(options)).demandCommand(1, "Please specify a subcommand").alias("help", "h"),
|
|
34
|
+
handler: () => {}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function createVaultInitSubcommand(options) {
|
|
38
|
+
return {
|
|
39
|
+
command: "init",
|
|
40
|
+
describe: "Initialize a new encrypted vault",
|
|
41
|
+
builder: {
|
|
42
|
+
path: {
|
|
43
|
+
type: "string",
|
|
44
|
+
description: "Path for vault file",
|
|
45
|
+
default: defaultVaultPath()
|
|
46
|
+
},
|
|
47
|
+
migrate: {
|
|
48
|
+
type: "boolean",
|
|
49
|
+
description: "Migrate existing credentials.toml into vault",
|
|
50
|
+
default: true
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
handler: async (argv) => {
|
|
54
|
+
const vaultPath = argv.path ?? defaultVaultPath();
|
|
55
|
+
const { generateMasterKey, writeEncryptedVault, vaultFileExists } = await import("../../providers/vault/dist/index.mjs");
|
|
56
|
+
if (await vaultFileExists(vaultPath)) throw new Error(`Vault already exists at ${vaultPath}. Delete it first to re-initialize.`);
|
|
57
|
+
const masterKey = generateMasterKey();
|
|
58
|
+
await writeEncryptedVault(vaultPath, { secrets: {} }, masterKey);
|
|
59
|
+
const { storeKeychain } = await import("../../providers/vault/dist/index.mjs");
|
|
60
|
+
const stored = await storeKeychain(masterKey);
|
|
61
|
+
let migrated = 0;
|
|
62
|
+
if (argv.migrate !== false) migrated = await migrateFromToml(vaultPath, masterKey);
|
|
63
|
+
const hexKey = masterKey.toString("hex");
|
|
64
|
+
options.onResult({
|
|
65
|
+
command: "vault init",
|
|
66
|
+
result: {
|
|
67
|
+
success: true,
|
|
68
|
+
vaultPath,
|
|
69
|
+
migrated,
|
|
70
|
+
keychainStored: stored
|
|
71
|
+
},
|
|
72
|
+
format: (result, view) => {
|
|
73
|
+
const base = formatVaultInitOutput(result, view);
|
|
74
|
+
if (view === "json") return base;
|
|
75
|
+
const lines = [base];
|
|
76
|
+
if (stored) lines.push("\nMaster key stored in OS keychain.");
|
|
77
|
+
else lines.push(`\nMaster key (save this securely — it cannot be recovered):\n${hexKey}`, `\nSet it as: export AFS_VAULT_KEY=${hexKey}`);
|
|
78
|
+
return lines.join("");
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Migrate credentials.toml entries into the vault.
|
|
86
|
+
* Returns count of migrated credential groups.
|
|
87
|
+
*/
|
|
88
|
+
async function migrateFromToml(vaultPath, masterKey) {
|
|
89
|
+
const { readFile } = await import("node:fs/promises");
|
|
90
|
+
const { parse } = await import("smol-toml");
|
|
91
|
+
const { AFSVault } = await import("../../providers/vault/dist/index.mjs");
|
|
92
|
+
const tomlPath = join(homedir(), DEFAULT_VAULT_DIR, "credentials.toml");
|
|
93
|
+
let content;
|
|
94
|
+
try {
|
|
95
|
+
content = await readFile(tomlPath, "utf-8");
|
|
96
|
+
} catch {
|
|
97
|
+
return 0;
|
|
98
|
+
}
|
|
99
|
+
let data;
|
|
100
|
+
try {
|
|
101
|
+
data = parse(content);
|
|
102
|
+
} catch {
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
const vault = new AFSVault({
|
|
106
|
+
vaultPath,
|
|
107
|
+
masterKey,
|
|
108
|
+
accessMode: "readwrite"
|
|
109
|
+
});
|
|
110
|
+
let count = 0;
|
|
111
|
+
for (const [group, values] of Object.entries(data)) {
|
|
112
|
+
if (typeof values !== "object" || values === null || Array.isArray(values)) continue;
|
|
113
|
+
const safeGroup = group.replace(/^[a-z0-9]+:\/\//, "").replace(/[^a-zA-Z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
114
|
+
if (!safeGroup) continue;
|
|
115
|
+
for (const [key, val] of Object.entries(values)) if (typeof val === "string") await vault.setSecret(safeGroup, key, val);
|
|
116
|
+
count++;
|
|
117
|
+
}
|
|
118
|
+
return count;
|
|
119
|
+
}
|
|
120
|
+
function createVaultGetSubcommand(options) {
|
|
121
|
+
return {
|
|
122
|
+
command: "get <group> <name>",
|
|
123
|
+
describe: "Read a secret value",
|
|
124
|
+
builder: {
|
|
125
|
+
group: {
|
|
126
|
+
type: "string",
|
|
127
|
+
demandOption: true,
|
|
128
|
+
description: "Secret group (e.g., aws, github)"
|
|
129
|
+
},
|
|
130
|
+
name: {
|
|
131
|
+
type: "string",
|
|
132
|
+
demandOption: true,
|
|
133
|
+
description: "Secret name (e.g., token, access-key-id)"
|
|
134
|
+
},
|
|
135
|
+
"vault-path": {
|
|
136
|
+
type: "string",
|
|
137
|
+
description: "Path to vault file"
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
handler: async (argv) => {
|
|
141
|
+
const { AFSVault } = await import("../../providers/vault/dist/index.mjs");
|
|
142
|
+
const value = await new AFSVault({
|
|
143
|
+
vaultPath: argv["vault-path"] ?? defaultVaultPath(),
|
|
144
|
+
masterKey: await loadMasterKey(),
|
|
145
|
+
accessMode: "readonly"
|
|
146
|
+
}).getSecret(argv.group, argv.name);
|
|
147
|
+
if (value === void 0) throw new Error(`Secret not found: ${argv.group}/${argv.name}`);
|
|
148
|
+
options.onResult({
|
|
149
|
+
command: "vault get",
|
|
150
|
+
result: {
|
|
151
|
+
group: argv.group,
|
|
152
|
+
name: argv.name,
|
|
153
|
+
value
|
|
154
|
+
},
|
|
155
|
+
format: formatVaultGetOutput
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function createVaultSetSubcommand(options) {
|
|
161
|
+
return {
|
|
162
|
+
command: "set <group> <name> <value>",
|
|
163
|
+
describe: "Store a secret",
|
|
164
|
+
builder: {
|
|
165
|
+
group: {
|
|
166
|
+
type: "string",
|
|
167
|
+
demandOption: true,
|
|
168
|
+
description: "Secret group (e.g., aws, github)"
|
|
169
|
+
},
|
|
170
|
+
name: {
|
|
171
|
+
type: "string",
|
|
172
|
+
demandOption: true,
|
|
173
|
+
description: "Secret name (e.g., token, access-key-id)"
|
|
174
|
+
},
|
|
175
|
+
value: {
|
|
176
|
+
type: "string",
|
|
177
|
+
demandOption: true,
|
|
178
|
+
description: "Secret value"
|
|
179
|
+
},
|
|
180
|
+
"vault-path": {
|
|
181
|
+
type: "string",
|
|
182
|
+
description: "Path to vault file"
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
handler: async (argv) => {
|
|
186
|
+
const { AFSVault } = await import("../../providers/vault/dist/index.mjs");
|
|
187
|
+
await new AFSVault({
|
|
188
|
+
vaultPath: argv["vault-path"] ?? defaultVaultPath(),
|
|
189
|
+
masterKey: await loadMasterKey(),
|
|
190
|
+
accessMode: "readwrite"
|
|
191
|
+
}).setSecret(argv.group, argv.name, argv.value);
|
|
192
|
+
options.onResult({
|
|
193
|
+
command: "vault set",
|
|
194
|
+
result: {
|
|
195
|
+
group: argv.group,
|
|
196
|
+
name: argv.name
|
|
197
|
+
},
|
|
198
|
+
format: formatVaultSetOutput
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function createVaultListSubcommand(options) {
|
|
204
|
+
return {
|
|
205
|
+
command: ["list [group]", "ls [group]"],
|
|
206
|
+
describe: "List secrets",
|
|
207
|
+
builder: {
|
|
208
|
+
group: {
|
|
209
|
+
type: "string",
|
|
210
|
+
description: "Secret group to list (omit for all groups)"
|
|
211
|
+
},
|
|
212
|
+
"vault-path": {
|
|
213
|
+
type: "string",
|
|
214
|
+
description: "Path to vault file"
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
handler: async (argv) => {
|
|
218
|
+
const { AFSVault } = await import("../../providers/vault/dist/index.mjs");
|
|
219
|
+
const secrets = await new AFSVault({
|
|
220
|
+
vaultPath: argv["vault-path"] ?? defaultVaultPath(),
|
|
221
|
+
masterKey: await loadMasterKey(),
|
|
222
|
+
accessMode: "readonly"
|
|
223
|
+
}).listSecrets(argv.group);
|
|
224
|
+
options.onResult({
|
|
225
|
+
command: "vault list",
|
|
226
|
+
result: {
|
|
227
|
+
group: argv.group,
|
|
228
|
+
secrets
|
|
229
|
+
},
|
|
230
|
+
format: formatVaultListOutput
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function createVaultDeleteSubcommand(options) {
|
|
236
|
+
return {
|
|
237
|
+
command: ["delete <group> [name]", "rm <group> [name]"],
|
|
238
|
+
describe: "Delete a secret or group",
|
|
239
|
+
builder: {
|
|
240
|
+
group: {
|
|
241
|
+
type: "string",
|
|
242
|
+
demandOption: true,
|
|
243
|
+
description: "Secret group"
|
|
244
|
+
},
|
|
245
|
+
name: {
|
|
246
|
+
type: "string",
|
|
247
|
+
description: "Secret name (omit to delete entire group)"
|
|
248
|
+
},
|
|
249
|
+
"vault-path": {
|
|
250
|
+
type: "string",
|
|
251
|
+
description: "Path to vault file"
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
handler: async (argv) => {
|
|
255
|
+
const { AFSVault } = await import("../../providers/vault/dist/index.mjs");
|
|
256
|
+
const vault = new AFSVault({
|
|
257
|
+
vaultPath: argv["vault-path"] ?? defaultVaultPath(),
|
|
258
|
+
masterKey: await loadMasterKey(),
|
|
259
|
+
accessMode: "readwrite"
|
|
260
|
+
});
|
|
261
|
+
let deleted;
|
|
262
|
+
if (argv.name) deleted = await vault.deleteSecret(argv.group, argv.name);
|
|
263
|
+
else {
|
|
264
|
+
const secrets = await vault.listSecrets(argv.group);
|
|
265
|
+
if (secrets.length === 0) deleted = false;
|
|
266
|
+
else {
|
|
267
|
+
for (const secretPath of secrets) {
|
|
268
|
+
const secretName = secretPath.split("/").pop();
|
|
269
|
+
await vault.deleteSecret(argv.group, secretName);
|
|
270
|
+
}
|
|
271
|
+
deleted = true;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
options.onResult({
|
|
275
|
+
command: "vault delete",
|
|
276
|
+
result: {
|
|
277
|
+
group: argv.group,
|
|
278
|
+
name: argv.name,
|
|
279
|
+
deleted
|
|
280
|
+
},
|
|
281
|
+
format: formatVaultDeleteOutput
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
//#endregion
|
|
288
|
+
export { createVaultCommand };
|
|
289
|
+
//# sourceMappingURL=vault.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault.mjs","names":[],"sources":["../../../src/core/commands/vault.ts"],"sourcesContent":["/**\n * vault Command - Core Implementation\n *\n * Vault management commands for encrypted secret storage.\n * Subcommands: init, get, set, list, delete.\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { Argv, CommandModule } from \"yargs\";\nimport {\n formatVaultDeleteOutput,\n formatVaultGetOutput,\n formatVaultInitOutput,\n formatVaultListOutput,\n formatVaultSetOutput,\n} from \"../formatters/vault.js\";\nimport type { CommandFactoryOptions } from \"./types.js\";\n\n/** Default vault file location */\nconst DEFAULT_VAULT_DIR = \".afs-config\";\nconst DEFAULT_VAULT_FILE = \"vault.enc\";\n\nfunction defaultVaultPath(): string {\n return join(homedir(), DEFAULT_VAULT_DIR, DEFAULT_VAULT_FILE);\n}\n\n/**\n * Resolve master key using the vault's key resolution chain:\n * OS keychain → AFS_VAULT_KEY env var → passphrase prompt.\n */\nasync function loadMasterKey(): Promise<Buffer> {\n const { resolveMasterKey } = await import(\"@aigne/afs-vault\");\n return resolveMasterKey();\n}\n\n/**\n * Create vault command factory (with subcommands)\n */\nexport function createVaultCommand(options: CommandFactoryOptions): CommandModule {\n return {\n command: \"vault\",\n describe: \"Encrypted secret storage\",\n builder: (yargs: Argv) =>\n yargs\n .command(createVaultInitSubcommand(options))\n .command(createVaultGetSubcommand(options))\n .command(createVaultSetSubcommand(options))\n .command(createVaultListSubcommand(options))\n .command(createVaultDeleteSubcommand(options))\n .demandCommand(1, \"Please specify a subcommand\")\n .alias(\"help\", \"h\"),\n handler: () => {},\n };\n}\n\n// ── init ──────────────────────────────────────────────────────────────\n\ninterface VaultInitArgs {\n path?: string;\n migrate?: boolean;\n}\n\nfunction createVaultInitSubcommand(\n options: CommandFactoryOptions,\n): CommandModule<unknown, VaultInitArgs> {\n return {\n command: \"init\",\n describe: \"Initialize a new encrypted vault\",\n builder: {\n path: {\n type: \"string\",\n description: \"Path for vault file\",\n default: defaultVaultPath(),\n },\n migrate: {\n type: \"boolean\",\n description: \"Migrate existing credentials.toml into vault\",\n default: true,\n },\n },\n handler: async (argv) => {\n const vaultPath = argv.path ?? defaultVaultPath();\n\n const { generateMasterKey, writeEncryptedVault, vaultFileExists } = await import(\n \"@aigne/afs-vault\"\n );\n\n if (await vaultFileExists(vaultPath)) {\n throw new Error(`Vault already exists at ${vaultPath}. Delete it first to re-initialize.`);\n }\n\n const masterKey = generateMasterKey();\n\n // Create empty vault\n await writeEncryptedVault(vaultPath, { secrets: {} }, masterKey);\n\n // Try to store in OS keychain\n const { storeKeychain } = await import(\"@aigne/afs-vault\");\n const stored = await storeKeychain(masterKey);\n\n let migrated = 0;\n\n // Migrate from credentials.toml if requested\n if (argv.migrate !== false) {\n migrated = await migrateFromToml(vaultPath, masterKey);\n }\n\n const hexKey = masterKey.toString(\"hex\");\n\n options.onResult({\n command: \"vault init\",\n result: { success: true, vaultPath, migrated, keychainStored: stored },\n format: (result, view) => {\n const base = formatVaultInitOutput(result, view);\n if (view === \"json\") return base;\n const lines = [base];\n if (stored) {\n lines.push(\"\\nMaster key stored in OS keychain.\");\n } else {\n lines.push(\n `\\nMaster key (save this securely — it cannot be recovered):\\n${hexKey}`,\n `\\nSet it as: export AFS_VAULT_KEY=${hexKey}`,\n );\n }\n return lines.join(\"\");\n },\n });\n },\n };\n}\n\n/**\n * Migrate credentials.toml entries into the vault.\n * Returns count of migrated credential groups.\n */\nasync function migrateFromToml(vaultPath: string, masterKey: Buffer): Promise<number> {\n const { readFile } = await import(\"node:fs/promises\");\n const { parse } = await import(\"smol-toml\");\n const { AFSVault } = await import(\"@aigne/afs-vault\");\n\n const tomlPath = join(homedir(), DEFAULT_VAULT_DIR, \"credentials.toml\");\n\n let content: string;\n try {\n content = await readFile(tomlPath, \"utf-8\");\n } catch {\n return 0; // No credentials.toml — nothing to migrate\n }\n\n let data: Record<string, unknown>;\n try {\n data = parse(content) as Record<string, unknown>;\n } catch {\n return 0; // Corrupted file — skip migration\n }\n\n const vault = new AFSVault({ vaultPath, masterKey, accessMode: \"readwrite\" });\n let count = 0;\n\n for (const [group, values] of Object.entries(data)) {\n if (typeof values !== \"object\" || values === null || Array.isArray(values)) continue;\n // Sanitize group name for vault\n const safeGroup = group\n .replace(/^[a-z0-9]+:\\/\\//, \"\")\n .replace(/[^a-zA-Z0-9._-]/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n if (!safeGroup) continue;\n\n for (const [key, val] of Object.entries(values as Record<string, unknown>)) {\n if (typeof val === \"string\") {\n await vault.setSecret(safeGroup, key, val);\n }\n }\n count++;\n }\n\n return count;\n}\n\n// ── get ──────────────────────────────────────────────────────────────\n\ninterface VaultGetArgs {\n group: string;\n name: string;\n \"vault-path\"?: string;\n}\n\nfunction createVaultGetSubcommand(\n options: CommandFactoryOptions,\n): CommandModule<unknown, VaultGetArgs> {\n return {\n command: \"get <group> <name>\",\n describe: \"Read a secret value\",\n builder: {\n group: {\n type: \"string\",\n demandOption: true,\n description: \"Secret group (e.g., aws, github)\",\n },\n name: {\n type: \"string\",\n demandOption: true,\n description: \"Secret name (e.g., token, access-key-id)\",\n },\n \"vault-path\": {\n type: \"string\",\n description: \"Path to vault file\",\n },\n },\n handler: async (argv) => {\n const { AFSVault } = await import(\"@aigne/afs-vault\");\n const vaultPath = argv[\"vault-path\"] ?? defaultVaultPath();\n const masterKey = await loadMasterKey();\n const vault = new AFSVault({ vaultPath, masterKey, accessMode: \"readonly\" });\n\n const value = await vault.getSecret(argv.group, argv.name);\n if (value === undefined) {\n throw new Error(`Secret not found: ${argv.group}/${argv.name}`);\n }\n\n options.onResult({\n command: \"vault get\",\n result: { group: argv.group, name: argv.name, value },\n format: formatVaultGetOutput,\n });\n },\n };\n}\n\n// ── set ──────────────────────────────────────────────────────────────\n\ninterface VaultSetArgs {\n group: string;\n name: string;\n value: string;\n \"vault-path\"?: string;\n}\n\nfunction createVaultSetSubcommand(\n options: CommandFactoryOptions,\n): CommandModule<unknown, VaultSetArgs> {\n return {\n command: \"set <group> <name> <value>\",\n describe: \"Store a secret\",\n builder: {\n group: {\n type: \"string\",\n demandOption: true,\n description: \"Secret group (e.g., aws, github)\",\n },\n name: {\n type: \"string\",\n demandOption: true,\n description: \"Secret name (e.g., token, access-key-id)\",\n },\n value: {\n type: \"string\",\n demandOption: true,\n description: \"Secret value\",\n },\n \"vault-path\": {\n type: \"string\",\n description: \"Path to vault file\",\n },\n },\n handler: async (argv) => {\n const { AFSVault } = await import(\"@aigne/afs-vault\");\n const vaultPath = argv[\"vault-path\"] ?? defaultVaultPath();\n const masterKey = await loadMasterKey();\n const vault = new AFSVault({ vaultPath, masterKey, accessMode: \"readwrite\" });\n\n await vault.setSecret(argv.group, argv.name, argv.value);\n\n options.onResult({\n command: \"vault set\",\n result: { group: argv.group, name: argv.name },\n format: formatVaultSetOutput,\n });\n },\n };\n}\n\n// ── list ─────────────────────────────────────────────────────────────\n\ninterface VaultListArgs {\n group?: string;\n \"vault-path\"?: string;\n}\n\nfunction createVaultListSubcommand(\n options: CommandFactoryOptions,\n): CommandModule<unknown, VaultListArgs> {\n return {\n command: [\"list [group]\", \"ls [group]\"],\n describe: \"List secrets\",\n builder: {\n group: {\n type: \"string\",\n description: \"Secret group to list (omit for all groups)\",\n },\n \"vault-path\": {\n type: \"string\",\n description: \"Path to vault file\",\n },\n },\n handler: async (argv) => {\n const { AFSVault } = await import(\"@aigne/afs-vault\");\n const vaultPath = argv[\"vault-path\"] ?? defaultVaultPath();\n const masterKey = await loadMasterKey();\n const vault = new AFSVault({ vaultPath, masterKey, accessMode: \"readonly\" });\n\n const secrets = await vault.listSecrets(argv.group);\n\n options.onResult({\n command: \"vault list\",\n result: { group: argv.group, secrets },\n format: formatVaultListOutput,\n });\n },\n };\n}\n\n// ── delete ───────────────────────────────────────────────────────────\n\ninterface VaultDeleteArgs {\n group: string;\n name?: string;\n \"vault-path\"?: string;\n}\n\nfunction createVaultDeleteSubcommand(\n options: CommandFactoryOptions,\n): CommandModule<unknown, VaultDeleteArgs> {\n return {\n command: [\"delete <group> [name]\", \"rm <group> [name]\"],\n describe: \"Delete a secret or group\",\n builder: {\n group: {\n type: \"string\",\n demandOption: true,\n description: \"Secret group\",\n },\n name: {\n type: \"string\",\n description: \"Secret name (omit to delete entire group)\",\n },\n \"vault-path\": {\n type: \"string\",\n description: \"Path to vault file\",\n },\n },\n handler: async (argv) => {\n const { AFSVault } = await import(\"@aigne/afs-vault\");\n const vaultPath = argv[\"vault-path\"] ?? defaultVaultPath();\n const masterKey = await loadMasterKey();\n const vault = new AFSVault({ vaultPath, masterKey, accessMode: \"readwrite\" });\n\n let deleted: boolean;\n if (argv.name) {\n deleted = await vault.deleteSecret(argv.group, argv.name);\n } else {\n // Delete entire group by listing all secrets and deleting each\n const secrets = await vault.listSecrets(argv.group);\n if (secrets.length === 0) {\n deleted = false;\n } else {\n for (const secretPath of secrets) {\n const secretName = secretPath.split(\"/\").pop()!;\n await vault.deleteSecret(argv.group, secretName);\n }\n deleted = true;\n }\n }\n\n options.onResult({\n command: \"vault delete\",\n result: { group: argv.group, name: argv.name, deleted },\n format: formatVaultDeleteOutput,\n });\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -36,10 +36,22 @@ function createWriteCommand(options) {
|
|
|
36
36
|
type: "string",
|
|
37
37
|
description: "Content to write"
|
|
38
38
|
},
|
|
39
|
-
|
|
40
|
-
type: "
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
mode: {
|
|
40
|
+
type: "string",
|
|
41
|
+
choices: [
|
|
42
|
+
"replace",
|
|
43
|
+
"append",
|
|
44
|
+
"prepend",
|
|
45
|
+
"patch",
|
|
46
|
+
"create",
|
|
47
|
+
"update"
|
|
48
|
+
],
|
|
49
|
+
description: "Write mode",
|
|
50
|
+
default: "replace"
|
|
51
|
+
},
|
|
52
|
+
patch: {
|
|
53
|
+
type: "string",
|
|
54
|
+
description: "JSON array of patch operations (for mode=patch)"
|
|
43
55
|
},
|
|
44
56
|
meta: {
|
|
45
57
|
type: "string",
|
|
@@ -50,13 +62,14 @@ function createWriteCommand(options) {
|
|
|
50
62
|
handler: async (argv) => {
|
|
51
63
|
const metadata = parseMetaValues(argv.meta);
|
|
52
64
|
const fields = metadata ? Object.keys(metadata) : void 0;
|
|
53
|
-
if (argv.content === void 0 && !metadata) throw new Error("write requires content (use --content or provide as second argument)");
|
|
65
|
+
if (argv.content === void 0 && !metadata && argv.mode !== "patch") throw new Error("write requires content (use --content or provide as second argument)");
|
|
54
66
|
const afs = await require_types.resolveAFS(options);
|
|
55
67
|
const canonicalPath = require_path_utils.cliPathToCanonical(argv.path);
|
|
56
68
|
const writeData = {};
|
|
57
69
|
if (argv.content !== void 0) writeData.content = argv.content;
|
|
58
70
|
if (metadata) writeData.meta = metadata;
|
|
59
|
-
|
|
71
|
+
if (argv.patch) writeData.patches = JSON.parse(argv.patch);
|
|
72
|
+
const result = await afs.write(canonicalPath, writeData, { mode: argv.mode });
|
|
60
73
|
options.onResult({
|
|
61
74
|
command: "write",
|
|
62
75
|
result,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write.d.cts","names":[],"sources":["../../../src/core/commands/write.ts"],"mappings":";;;;;;;UAgBiB,SAAA;EACf,IAAA;EACA,OAAA;EACA,
|
|
1
|
+
{"version":3,"file":"write.d.cts","names":[],"sources":["../../../src/core/commands/write.ts"],"mappings":";;;;;;;UAgBiB,SAAA;EACf,IAAA;EACA,OAAA;EACA,IAAA;EACA,KAAA;EACA,IAAA;AAAA;;;;iBAwBc,kBAAA,CACd,OAAA,EAAS,qBAAA,GACR,aAAA,UAAuB,SAAA"}
|