@dotenc/cli 0.6.0 → 0.7.0

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 (3) hide show
  1. package/README.md +50 -4
  2. package/dist/cli.js +528 -274
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ var package_default;
7
7
  var init_package = __esm(() => {
8
8
  package_default = {
9
9
  name: "@dotenc/cli",
10
- version: "0.6.0",
10
+ version: "0.7.0",
11
11
  description: "🔐 Git-native encrypted environments powered by your SSH keys",
12
12
  author: "Ivan Filho <i@ivanfilho.com>",
13
13
  license: "MIT",
@@ -901,11 +901,32 @@ var init_encryptEnvironment = __esm(() => {
901
901
  init_getPublicKeys();
902
902
  });
903
903
 
904
+ // src/helpers/validateKeyName.ts
905
+ var validateKeyName = (name) => {
906
+ if (!name) {
907
+ return { valid: false, reason: "Key name must not be empty." };
908
+ }
909
+ if (name === "." || name === "..") {
910
+ return { valid: false, reason: `Invalid key name "${name}".` };
911
+ }
912
+ if (!/^[a-zA-Z0-9._-]+$/.test(name)) {
913
+ return {
914
+ valid: false,
915
+ reason: `Invalid key name "${name}". Only letters, numbers, dots, hyphens, and underscores are allowed.`
916
+ };
917
+ }
918
+ return { valid: true };
919
+ };
920
+
904
921
  // src/helpers/getPublicKeyByName.ts
905
922
  import crypto8 from "node:crypto";
906
923
  import fs5 from "node:fs/promises";
907
924
  import path5 from "node:path";
908
925
  var getPublicKeyByName = async (name) => {
926
+ const keyNameValidation = validateKeyName(name);
927
+ if (!keyNameValidation.valid) {
928
+ throw new Error(keyNameValidation.reason);
929
+ }
909
930
  const filePath = path5.join(process.cwd(), ".dotenc", `${name}.pub`);
910
931
  let publicKeyInput;
911
932
  try {
@@ -949,12 +970,16 @@ var init_getEnvironments = () => {};
949
970
 
950
971
  // src/prompts/chooseEnvironment.ts
951
972
  import inquirer from "inquirer";
952
- var chooseEnvironmentPrompt = async (message) => {
953
- const environments = await getEnvironments();
973
+ var defaultDeps, _runChooseEnvironmentPrompt = async (message, depsOverrides = {}) => {
974
+ const deps = {
975
+ ...defaultDeps,
976
+ ...depsOverrides
977
+ };
978
+ const environments = await deps.getEnvironments();
954
979
  if (!environments.length) {
955
- console.log('No environment files found. To create a new environment, run "dotenc env create"');
980
+ deps.logInfo('No environment files found. To create a new environment, run "dotenc env create"');
956
981
  }
957
- const result = await inquirer.prompt([
982
+ const result = await deps.prompt([
958
983
  {
959
984
  type: "list",
960
985
  name: "environment",
@@ -963,25 +988,42 @@ var chooseEnvironmentPrompt = async (message) => {
963
988
  }
964
989
  ]);
965
990
  return result.environment;
991
+ }, chooseEnvironmentPrompt = async (message) => {
992
+ return _runChooseEnvironmentPrompt(message);
966
993
  };
967
994
  var init_chooseEnvironment = __esm(() => {
968
995
  init_getEnvironments();
996
+ defaultDeps = {
997
+ getEnvironments,
998
+ prompt: inquirer.prompt,
999
+ logInfo: (message) => console.log(message)
1000
+ };
969
1001
  });
970
1002
 
971
1003
  // src/prompts/choosePublicKey.ts
972
1004
  import inquirer2 from "inquirer";
973
1005
  async function _runChoosePublicKeyPrompt(message, multiple, depsOverrides = {}) {
974
1006
  const deps = {
975
- ...defaultDeps,
1007
+ ...defaultDeps2,
976
1008
  ...depsOverrides
977
1009
  };
978
1010
  const publicKeys = await deps.getPublicKeys();
979
- const result = await deps.prompt([
1011
+ const prompt = deps.prompt;
1012
+ const result = await prompt([
980
1013
  {
981
1014
  type: multiple ? "checkbox" : "list",
982
1015
  name: "key",
983
1016
  message,
984
- choices: publicKeys.map((key) => key.name.replace(".pub", ""))
1017
+ choices: publicKeys.map((key) => key.name.replace(".pub", "")),
1018
+ validate: (input) => {
1019
+ if (!multiple) {
1020
+ return true;
1021
+ }
1022
+ if (Array.isArray(input) && input.length > 0) {
1023
+ return true;
1024
+ }
1025
+ return "Select at least one public key.";
1026
+ }
985
1027
  }
986
1028
  ]);
987
1029
  if (multiple) {
@@ -998,10 +1040,10 @@ async function choosePublicKeyPrompt(message, multiple) {
998
1040
  }
999
1041
  return _runChoosePublicKeyPrompt(message, false);
1000
1042
  }
1001
- var defaultDeps;
1043
+ var defaultDeps2;
1002
1044
  var init_choosePublicKey = __esm(() => {
1003
1045
  init_getPublicKeys();
1004
- defaultDeps = {
1046
+ defaultDeps2 = {
1005
1047
  prompt: inquirer2.prompt,
1006
1048
  getPublicKeys
1007
1049
  };
@@ -1148,22 +1190,42 @@ var init_homeConfig = __esm(() => {
1148
1190
  });
1149
1191
 
1150
1192
  // src/commands/config.ts
1151
- var configCommand = async (key, value, options) => {
1152
- const config = await getHomeConfig();
1193
+ import chalk7 from "chalk";
1194
+ var SUPPORTED_CONFIG_KEYS, defaultDeps3, getSupportedConfigKey = (key) => {
1195
+ if (SUPPORTED_CONFIG_KEYS.includes(key)) {
1196
+ return { valid: true, key };
1197
+ }
1198
+ return { valid: false };
1199
+ }, configCommand = async (key, value, options, deps = defaultDeps3) => {
1200
+ const keyValidation = getSupportedConfigKey(key);
1201
+ if (!keyValidation.valid) {
1202
+ deps.logError(`${chalk7.red("Error:")} unsupported config key "${key}". Supported keys: ${SUPPORTED_CONFIG_KEYS.join(", ")}.`);
1203
+ deps.exit(1);
1204
+ }
1205
+ const config = await deps.getHomeConfig();
1206
+ const configKey = keyValidation.key;
1153
1207
  if (options.remove) {
1154
- delete config[key];
1155
- await setHomeConfig(config);
1208
+ delete config[configKey];
1209
+ await deps.setHomeConfig(config);
1156
1210
  return;
1157
1211
  }
1158
- if (value) {
1159
- config[key] = value;
1160
- await setHomeConfig(config);
1212
+ if (value !== undefined) {
1213
+ config[configKey] = value;
1214
+ await deps.setHomeConfig(config);
1161
1215
  return;
1162
1216
  }
1163
- console.log(config[key]);
1217
+ deps.log(config[configKey] ?? "");
1164
1218
  };
1165
1219
  var init_config = __esm(() => {
1166
1220
  init_homeConfig();
1221
+ SUPPORTED_CONFIG_KEYS = ["editor"];
1222
+ defaultDeps3 = {
1223
+ getHomeConfig,
1224
+ setHomeConfig,
1225
+ log: (message) => console.log(message),
1226
+ logError: (message) => console.error(message),
1227
+ exit: (code) => process.exit(code)
1228
+ };
1167
1229
  });
1168
1230
 
1169
1231
  // src/helpers/getCurrentKeyName.ts
@@ -1206,19 +1268,19 @@ var init_parseEnv = __esm(() => {
1206
1268
 
1207
1269
  // src/commands/run.ts
1208
1270
  import { spawn } from "node:child_process";
1209
- import chalk7 from "chalk";
1271
+ import chalk8 from "chalk";
1210
1272
  var defaultRunCommandDeps, runCommand = async (command, args, options, _command, deps = defaultRunCommandDeps) => {
1211
1273
  const environmentName = options.env || process.env.DOTENC_ENV;
1212
1274
  if (!environmentName) {
1213
1275
  deps.logError(`No environment provided. Use -e or set DOTENC_ENV to the environment you want to run the command in.
1214
- To start a new environment, use "dotenc init [environment]".`);
1276
+ To initialize dotenc, run "dotenc init --name <your-name>". To add environments later, use "dotenc env create <environment>".`);
1215
1277
  deps.exit(1);
1216
1278
  }
1217
1279
  const environments = environmentName.split(",");
1218
1280
  for (const env of environments) {
1219
1281
  const validation = deps.validateEnvironmentName(env);
1220
1282
  if (!validation.valid) {
1221
- deps.logError(`${chalk7.red("Error:")} ${validation.reason}`);
1283
+ deps.logError(`${chalk8.red("Error:")} ${validation.reason}`);
1222
1284
  deps.exit(1);
1223
1285
  }
1224
1286
  }
@@ -1236,11 +1298,15 @@ To start a new environment, use "dotenc init [environment]".`);
1236
1298
  return decryptedEnv2;
1237
1299
  }));
1238
1300
  if (failureCount === environments.length) {
1239
- deps.logError(`${chalk7.red("Error:")} All environments failed to load.`);
1301
+ deps.logError(`${chalk8.red("Error:")} All environments failed to load.`);
1240
1302
  deps.exit(1);
1241
1303
  }
1242
1304
  if (failureCount > 0) {
1243
- deps.logError(`${chalk7.yellow("Warning:")} ${failureCount} of ${environments.length} environment(s) failed to load.`);
1305
+ if (options.strict) {
1306
+ deps.logError(`${chalk8.red("Error:")} ${failureCount} of ${environments.length} environment(s) failed to load and strict mode is enabled.`);
1307
+ deps.exit(1);
1308
+ }
1309
+ deps.logError(`${chalk8.yellow("Warning:")} ${failureCount} of ${environments.length} environment(s) failed to load.`);
1244
1310
  }
1245
1311
  const decryptedEnv = decryptedEnvs.reduce((acc, env) => {
1246
1312
  return { ...acc, ...env };
@@ -1268,7 +1334,7 @@ var init_run = __esm(() => {
1268
1334
  });
1269
1335
 
1270
1336
  // src/commands/dev.ts
1271
- import chalk8 from "chalk";
1337
+ import chalk9 from "chalk";
1272
1338
  import inquirer3 from "inquirer";
1273
1339
  var defaultSelect = async (message, choices) => {
1274
1340
  const { selected } = await inquirer3.prompt([
@@ -1283,7 +1349,7 @@ var defaultSelect = async (message, choices) => {
1283
1349
  }, defaultDevCommandDeps, devCommand = async (command, args, deps = defaultDevCommandDeps) => {
1284
1350
  const keyNames = await deps.getCurrentKeyName();
1285
1351
  if (keyNames.length === 0) {
1286
- deps.logError(`${chalk8.red("Error:")} could not resolve your identity. Run ${chalk8.gray("dotenc init")} first.`);
1352
+ deps.logError(`${chalk9.red("Error:")} could not resolve your identity. Run ${chalk9.gray("dotenc init")} first.`);
1287
1353
  deps.exit(1);
1288
1354
  }
1289
1355
  let keyName;
@@ -1342,12 +1408,12 @@ var init_createEnvironment = () => {};
1342
1408
  // src/commands/env/create.ts
1343
1409
  import fs8 from "node:fs/promises";
1344
1410
  import path8 from "node:path";
1345
- import chalk9 from "chalk";
1411
+ import chalk10 from "chalk";
1346
1412
  var _getRunUsageHintForEnvironment = (environmentName) => {
1347
1413
  if (environmentName === "development") {
1348
- return chalk9.gray("dotenc dev <command> [args...]");
1414
+ return chalk10.gray("dotenc dev <command> [args...]");
1349
1415
  }
1350
- return `${chalk9.gray(`dotenc run -e ${environmentName} <command> [args...]`)} or ${chalk9.gray(`DOTENC_ENV=${environmentName} dotenc run <command> [args...]`)}`;
1416
+ return `${chalk10.gray(`dotenc run -e ${environmentName} <command> [args...]`)} or ${chalk10.gray(`DOTENC_ENV=${environmentName} dotenc run <command> [args...]`)}`;
1351
1417
  }, _normalizePublicKeyNamesForCreate = (selection) => {
1352
1418
  if (Array.isArray(selection)) {
1353
1419
  return selection;
@@ -1362,25 +1428,29 @@ var _getRunUsageHintForEnvironment = (environmentName) => {
1362
1428
  environmentName = await createEnvironmentPrompt("What should the environment be named?", getEnvironmentNameSuggestion());
1363
1429
  }
1364
1430
  if (!environmentName) {
1365
- console.error(`${chalk9.red("Error:")} no environment name provided`);
1431
+ console.error(`${chalk10.red("Error:")} no environment name provided`);
1366
1432
  process.exit(1);
1367
1433
  }
1368
1434
  const validation = validateEnvironmentName(environmentName);
1369
1435
  if (!validation.valid) {
1370
- console.error(`${chalk9.red("Error:")} ${validation.reason}`);
1436
+ console.error(`${chalk10.red("Error:")} ${validation.reason}`);
1371
1437
  process.exit(1);
1372
1438
  }
1373
1439
  if (environmentExists(environmentName)) {
1374
- console.error(`${chalk9.red("Error:")} environment ${environmentName} already exists. To edit it, use ${chalk9.gray(`dotenc env edit ${environmentName}`)}`);
1440
+ console.error(`${chalk10.red("Error:")} environment ${environmentName} already exists. To edit it, use ${chalk10.gray(`dotenc env edit ${environmentName}`)}`);
1375
1441
  process.exit(1);
1376
1442
  }
1377
1443
  const availablePublicKeys = await getPublicKeys();
1378
1444
  if (!availablePublicKeys.length) {
1379
- console.error(`${chalk9.red("Error:")} no public keys found. Please add a public key using ${chalk9.gray("dotenc key add")}.`);
1445
+ console.error(`${chalk10.red("Error:")} no public keys found. Please add a public key using ${chalk10.gray("dotenc key add")}.`);
1380
1446
  process.exit(1);
1381
1447
  }
1382
1448
  const publicKeySelection = publicKeyNameArg ? publicKeyNameArg : await choosePublicKeyPrompt("Which public key(s) do you want to grant access for this environment?", true);
1383
1449
  const publicKeys = _normalizePublicKeyNamesForCreate(publicKeySelection);
1450
+ if (publicKeys.length === 0) {
1451
+ console.error(`${chalk10.red("Error:")} select at least one public key before creating an environment.`);
1452
+ process.exit(1);
1453
+ }
1384
1454
  const dataKey = createDataKey();
1385
1455
  const content = initialContent ?? `# ${environmentName} environment
1386
1456
  `;
@@ -1392,7 +1462,7 @@ var _getRunUsageHintForEnvironment = (environmentName) => {
1392
1462
  for (const publicKeyName of publicKeys) {
1393
1463
  const publicKey = availablePublicKeys.find((key) => key.name === publicKeyName);
1394
1464
  if (!publicKey) {
1395
- console.error(`Public key ${chalk9.cyan(publicKeyName)} not found or invalid.`);
1465
+ console.error(`Public key ${chalk10.cyan(publicKeyName)} not found or invalid.`);
1396
1466
  continue;
1397
1467
  }
1398
1468
  const encrypted = encryptDataKey(publicKey, dataKey);
@@ -1403,11 +1473,15 @@ var _getRunUsageHintForEnvironment = (environmentName) => {
1403
1473
  algorithm: publicKey.algorithm
1404
1474
  });
1405
1475
  }
1476
+ if (environmentJson.keys.length === 0) {
1477
+ console.error(`${chalk10.red("Error:")} no valid public keys were selected. Environment creation aborted.`);
1478
+ process.exit(1);
1479
+ }
1406
1480
  await fs8.writeFile(path8.join(process.cwd(), `.env.${environmentName}.enc`), JSON.stringify(environmentJson, null, 2), "utf-8");
1407
- console.log(`${chalk9.green("✔")} Environment ${chalk9.cyan(environmentName)} created!`);
1481
+ console.log(`${chalk10.green("✔")} Environment ${chalk10.cyan(environmentName)} created!`);
1408
1482
  console.log(`
1409
1483
  Some useful tips:`);
1410
- const editCommand = chalk9.gray(`dotenc env edit ${environmentName}`);
1484
+ const editCommand = chalk10.gray(`dotenc env edit ${environmentName}`);
1411
1485
  console.log(`
1412
1486
  - To securely edit your environment: ${editCommand}`);
1413
1487
  console.log(`- To run your application: ${_getRunUsageHintForEnvironment(environmentName)}`);
@@ -1542,18 +1616,87 @@ var init_getDefaultEditor = __esm(() => {
1542
1616
  };
1543
1617
  });
1544
1618
 
1619
+ // src/helpers/splitCommand.ts
1620
+ var splitCommand = (input) => {
1621
+ const trimmed = input.trim();
1622
+ if (!trimmed) {
1623
+ return [];
1624
+ }
1625
+ const tokens = [];
1626
+ let current = "";
1627
+ let quote = null;
1628
+ let escaped = false;
1629
+ for (const char of trimmed) {
1630
+ if (escaped) {
1631
+ current += char;
1632
+ escaped = false;
1633
+ continue;
1634
+ }
1635
+ if (char === "\\") {
1636
+ if (quote === null) {
1637
+ escaped = true;
1638
+ } else {
1639
+ current += char;
1640
+ }
1641
+ continue;
1642
+ }
1643
+ if (quote === "single") {
1644
+ if (char === "'") {
1645
+ quote = null;
1646
+ } else {
1647
+ current += char;
1648
+ }
1649
+ continue;
1650
+ }
1651
+ if (quote === "double") {
1652
+ if (char === '"') {
1653
+ quote = null;
1654
+ } else {
1655
+ current += char;
1656
+ }
1657
+ continue;
1658
+ }
1659
+ if (char === "'") {
1660
+ quote = "single";
1661
+ continue;
1662
+ }
1663
+ if (char === '"') {
1664
+ quote = "double";
1665
+ continue;
1666
+ }
1667
+ if (/\s/.test(char)) {
1668
+ if (current.length > 0) {
1669
+ tokens.push(current);
1670
+ current = "";
1671
+ }
1672
+ continue;
1673
+ }
1674
+ current += char;
1675
+ }
1676
+ if (escaped) {
1677
+ current += "\\";
1678
+ }
1679
+ if (quote) {
1680
+ throw new Error("Unterminated quote in command.");
1681
+ }
1682
+ if (current.length > 0) {
1683
+ tokens.push(current);
1684
+ }
1685
+ return tokens;
1686
+ };
1687
+
1545
1688
  // src/commands/env/edit.ts
1546
1689
  import { spawnSync } from "node:child_process";
1547
1690
  import { existsSync as existsSync5 } from "node:fs";
1548
1691
  import fs9 from "node:fs/promises";
1549
1692
  import os3 from "node:os";
1550
1693
  import path9 from "node:path";
1551
- import chalk10 from "chalk";
1694
+ import chalk11 from "chalk";
1552
1695
  var editCommand = async (environmentNameArg) => {
1553
1696
  const environmentName = environmentNameArg || await chooseEnvironmentPrompt("What environment do you want to edit?");
1554
1697
  const nameValidation = validateEnvironmentName(environmentName);
1555
1698
  if (!nameValidation.valid) {
1556
- console.error(`${chalk10.red("Error:")} ${nameValidation.reason}`);
1699
+ console.error(`${chalk11.red("Error:")} ${nameValidation.reason}`);
1557
1700
  process.exit(1);
1558
1701
  }
1559
1702
  const environmentFile = `.env.${environmentName}.enc`;
@@ -1594,9 +1737,15 @@ ${separator}${content}`;
1594
1737
  };
1595
1738
  process.on("SIGINT", onSignal);
1596
1739
  process.on("SIGTERM", onSignal);
1597
- const editor = await getDefaultEditor();
1740
+ const editorCommand = await getDefaultEditor();
1598
1741
  try {
1599
- const result = spawnSync(editor, [tempFilePath], { stdio: "inherit" });
1742
+ const [editorExecutable, ...editorArgs] = splitCommand(editorCommand);
1743
+ if (!editorExecutable) {
1744
+ throw new Error("No editor command configured.");
1745
+ }
1746
+ const result = spawnSync(editorExecutable, [...editorArgs, tempFilePath], {
1747
+ stdio: "inherit"
1748
+ });
1600
1749
  if (result.error) {
1601
1750
  throw result.error;
1602
1751
  }
@@ -1609,7 +1758,7 @@ Editor exited with code ${result.status}`);
1609
1758
  const finalHash = createHash(newContent);
1610
1759
  if (initialHash === finalHash) {
1611
1760
  console.log(`
1612
- No changes were made to the ${chalk10.cyan(environmentName)} environment.`);
1761
+ No changes were made to the ${chalk11.cyan(environmentName)} environment.`);
1613
1762
  } else {
1614
1763
  const separatorIndex = newContent.indexOf(separator);
1615
1764
  if (separatorIndex !== -1) {
@@ -1618,11 +1767,11 @@ No changes were made to the ${chalk10.cyan(environmentName)} environment.`);
1618
1767
  }
1619
1768
  await encryptEnvironment(environmentName, newContent);
1620
1769
  console.log(`
1621
- Encrypted ${chalk10.cyan(environmentName)} environment and saved it to ${chalk10.gray(environmentFile)}.`);
1770
+ Encrypted ${chalk11.cyan(environmentName)} environment and saved it to ${chalk11.gray(environmentFile)}.`);
1622
1771
  }
1623
1772
  } catch (error) {
1624
1773
  console.error(`
1625
- Failed to open editor: ${editor}`);
1774
+ Failed to open editor: ${editorCommand}`);
1626
1775
  console.error(error instanceof Error ? error.message : error);
1627
1776
  } finally {
1628
1777
  process.removeListener("SIGINT", onSignal);
@@ -1766,12 +1915,12 @@ var init_list2 = __esm(() => {
1766
1915
  });
1767
1916
 
1768
1917
  // src/commands/env/rotate.ts
1769
- import chalk11 from "chalk";
1918
+ import chalk12 from "chalk";
1770
1919
  var rotateCommand = async (environmentNameArg) => {
1771
1920
  const environmentName = environmentNameArg || await chooseEnvironmentPrompt("What environment do you want to rotate the data key for?");
1772
1921
  const validation = validateEnvironmentName(environmentName);
1773
1922
  if (!validation.valid) {
1774
- console.error(`${chalk11.red("Error:")} ${validation.reason}`);
1923
+ console.error(`${chalk12.red("Error:")} ${validation.reason}`);
1775
1924
  process.exit(1);
1776
1925
  }
1777
1926
  let currentContent;
@@ -1866,9 +2015,81 @@ var init_createEd25519SshKey = __esm(() => {
1866
2015
  };
1867
2016
  });
1868
2017
 
2018
+ // src/helpers/validatePublicKey.ts
2019
+ function getRsaModulusLength(key) {
2020
+ const der = key.export({ type: "spki", format: "der" });
2021
+ let i = 0;
2022
+ i += 1;
2023
+ i += der[i] & 128 ? (der[i] & 127) + 1 : 1;
2024
+ i += 1;
2025
+ let algLen = 0;
2026
+ if (der[i] & 128) {
2027
+ const n = der[i] & 127;
2028
+ for (let j = 1;j <= n; j++)
2029
+ algLen = algLen << 8 | der[i + j];
2030
+ i += n + 1;
2031
+ } else {
2032
+ algLen = der[i];
2033
+ i += 1;
2034
+ }
2035
+ i += algLen;
2036
+ i += 1;
2037
+ i += der[i] & 128 ? (der[i] & 127) + 1 : 1;
2038
+ i += 1;
2039
+ i += 1;
2040
+ i += der[i] & 128 ? (der[i] & 127) + 1 : 1;
2041
+ i += 1;
2042
+ let modLen = 0;
2043
+ if (der[i] & 128) {
2044
+ const n = der[i] & 127;
2045
+ for (let j = 1;j <= n; j++)
2046
+ modLen = modLen << 8 | der[i + j];
2047
+ i += n + 1;
2048
+ } else {
2049
+ modLen = der[i];
2050
+ i += 1;
2051
+ }
2052
+ if (der[i] === 0)
2053
+ modLen -= 1;
2054
+ return modLen * 8;
2055
+ }
2056
+ function validatePublicKey(key) {
2057
+ const keyType = key.asymmetricKeyType;
2058
+ switch (keyType) {
2059
+ case "rsa": {
2060
+ const modulusLength = getRsaModulusLength(key);
2061
+ if (modulusLength < 2048) {
2062
+ return {
2063
+ valid: false,
2064
+ reason: `RSA key is ${modulusLength} bits, minimum is 2048 bits.`
2065
+ };
2066
+ }
2067
+ return { valid: true };
2068
+ }
2069
+ case "ed25519":
2070
+ return { valid: true };
2071
+ case "dsa":
2072
+ return {
2073
+ valid: false,
2074
+ reason: "DSA keys are not supported. Use Ed25519 or RSA (2048+ bits)."
2075
+ };
2076
+ case "ec":
2077
+ return {
2078
+ valid: false,
2079
+ reason: "ECDSA keys are not supported. Use Ed25519 or RSA (2048+ bits)."
2080
+ };
2081
+ default:
2082
+ return {
2083
+ valid: false,
2084
+ reason: `Unsupported key type: ${keyType}. Use Ed25519 or RSA (2048+ bits).`
2085
+ };
2086
+ }
2087
+ }
2088
+
1869
2089
  // src/prompts/choosePrivateKey.ts
2090
+ import crypto10 from "node:crypto";
1870
2091
  import path12 from "node:path";
1871
- import chalk12 from "chalk";
2092
+ import chalk13 from "chalk";
1872
2093
  import inquirer5 from "inquirer";
1873
2094
  function toSupportedChoice(key) {
1874
2095
  return {
@@ -1878,23 +2099,48 @@ function toSupportedChoice(key) {
1878
2099
  }
1879
2100
  function toUnsupportedChoice(key, index) {
1880
2101
  return {
1881
- name: `${chalk12.gray(key.name)} (${chalk12.yellow(key.reason)})`,
2102
+ name: `${chalk13.gray(key.name)} (${chalk13.yellow(key.reason)})`,
1882
2103
  value: `__unsupported_${index}__`,
1883
2104
  disabled: true
1884
2105
  };
1885
2106
  }
2107
+ function classifySupportedKeys(keys) {
2108
+ const supportedKeys = [];
2109
+ const policyUnsupportedKeys = [];
2110
+ for (const key of keys) {
2111
+ try {
2112
+ const publicKey = crypto10.createPublicKey(key.privateKey);
2113
+ const validation = validatePublicKey(publicKey);
2114
+ if (!validation.valid) {
2115
+ policyUnsupportedKeys.push({
2116
+ name: key.name,
2117
+ reason: validation.reason
2118
+ });
2119
+ continue;
2120
+ }
2121
+ } catch {
2122
+ policyUnsupportedKeys.push({
2123
+ name: key.name,
2124
+ reason: "invalid private key format"
2125
+ });
2126
+ continue;
2127
+ }
2128
+ supportedKeys.push(key);
2129
+ }
2130
+ return { supportedKeys, policyUnsupportedKeys };
2131
+ }
1886
2132
  var CREATE_NEW_PRIVATE_KEY_CHOICE = "__dotenc_create_new_private_key__", defaultChoosePrivateKeyPromptDeps, buildPromptChoices = (keys, unsupportedKeys) => {
1887
2133
  const choices = keys.map(toSupportedChoice);
1888
2134
  if (unsupportedKeys.length > 0) {
1889
2135
  if (choices.length > 0) {
1890
2136
  choices.push({
1891
- name: chalk12.gray("────────"),
2137
+ name: chalk13.gray("────────"),
1892
2138
  value: "__separator_supported_unsupported__",
1893
2139
  disabled: true
1894
2140
  });
1895
2141
  }
1896
2142
  choices.push({
1897
- name: chalk12.gray("Unsupported keys (ignored)"),
2143
+ name: chalk13.gray("Unsupported keys (ignored)"),
1898
2144
  value: "__unsupported_header__",
1899
2145
  disabled: true
1900
2146
  });
@@ -1902,7 +2148,7 @@ var CREATE_NEW_PRIVATE_KEY_CHOICE = "__dotenc_create_new_private_key__", default
1902
2148
  }
1903
2149
  if (choices.length > 0) {
1904
2150
  choices.push({
1905
- name: chalk12.gray("────────"),
2151
+ name: chalk13.gray("────────"),
1906
2152
  value: "__separator_create__",
1907
2153
  disabled: true
1908
2154
  });
@@ -1919,16 +2165,18 @@ var CREATE_NEW_PRIVATE_KEY_CHOICE = "__dotenc_create_new_private_key__", default
1919
2165
  passphraseProtectedKeys,
1920
2166
  unsupportedKeys = []
1921
2167
  } = await deps.getPrivateKeys();
1922
- const privateKeyMap = new Map(keys.map((key) => [key.name, key]));
2168
+ const { supportedKeys, policyUnsupportedKeys } = classifySupportedKeys(keys);
2169
+ const allUnsupportedKeys = [...unsupportedKeys, ...policyUnsupportedKeys];
2170
+ const privateKeyMap = new Map(supportedKeys.map((key) => [key.name, key]));
1923
2171
  if (!deps.isInteractive()) {
1924
- if (keys.length > 0) {
1925
- return keys[0];
2172
+ if (supportedKeys.length > 0) {
2173
+ return supportedKeys[0];
1926
2174
  }
1927
2175
  if (passphraseProtectedKeys.length > 0) {
1928
2176
  throw new Error(passphraseProtectedKeyError(passphraseProtectedKeys));
1929
2177
  }
1930
- if (unsupportedKeys.length > 0) {
1931
- const unsupportedList = unsupportedKeys.map((key) => ` - ${key.name}: ${key.reason}`).join(`
2178
+ if (allUnsupportedKeys.length > 0) {
2179
+ const unsupportedList = allUnsupportedKeys.map((key) => ` - ${key.name}: ${key.reason}`).join(`
1932
2180
  `);
1933
2181
  throw new Error(`No supported SSH keys found.
1934
2182
 
@@ -1945,16 +2193,16 @@ Generate a new key with:
1945
2193
  type: "list",
1946
2194
  name: "key",
1947
2195
  message,
1948
- choices: buildPromptChoices(keys, unsupportedKeys)
2196
+ choices: buildPromptChoices(supportedKeys, allUnsupportedKeys)
1949
2197
  }
1950
2198
  ]);
1951
2199
  const selected = String(result.key || "");
1952
2200
  if (selected === CREATE_NEW_PRIVATE_KEY_CHOICE) {
1953
2201
  try {
1954
2202
  const createdPath = await deps.createEd25519SshKey();
1955
- deps.logInfo(`${chalk12.green("✔")} Created ${chalk12.cyan(path12.basename(createdPath))} at ${chalk12.gray(createdPath)}.`);
2203
+ deps.logInfo(`${chalk13.green("✔")} Created ${chalk13.cyan(path12.basename(createdPath))} at ${chalk13.gray(createdPath)}.`);
1956
2204
  } catch (error) {
1957
- deps.logWarn(`${chalk12.yellow("Warning:")} failed to create a new SSH key. ${error instanceof Error ? error.message : String(error)}`);
2205
+ deps.logWarn(`${chalk13.yellow("Warning:")} failed to create a new SSH key. ${error instanceof Error ? error.message : String(error)}`);
1958
2206
  }
1959
2207
  continue;
1960
2208
  }
@@ -1994,77 +2242,6 @@ var inputNamePrompt = async (message, defaultValue) => {
1994
2242
  };
1995
2243
  var init_inputName = () => {};
1996
2244
 
1997
- // src/helpers/validatePublicKey.ts
1998
- function getRsaModulusLength(key) {
1999
- const der = key.export({ type: "spki", format: "der" });
2000
- let i = 0;
2001
- i += 1;
2002
- i += der[i] & 128 ? (der[i] & 127) + 1 : 1;
2003
- i += 1;
2004
- let algLen = 0;
2005
- if (der[i] & 128) {
2006
- const n = der[i] & 127;
2007
- for (let j = 1;j <= n; j++)
2008
- algLen = algLen << 8 | der[i + j];
2009
- i += n + 1;
2010
- } else {
2011
- algLen = der[i];
2012
- i += 1;
2013
- }
2014
- i += algLen;
2015
- i += 1;
2016
- i += der[i] & 128 ? (der[i] & 127) + 1 : 1;
2017
- i += 1;
2018
- i += 1;
2019
- i += der[i] & 128 ? (der[i] & 127) + 1 : 1;
2020
- i += 1;
2021
- let modLen = 0;
2022
- if (der[i] & 128) {
2023
- const n = der[i] & 127;
2024
- for (let j = 1;j <= n; j++)
2025
- modLen = modLen << 8 | der[i + j];
2026
- i += n + 1;
2027
- } else {
2028
- modLen = der[i];
2029
- i += 1;
2030
- }
2031
- if (der[i] === 0)
2032
- modLen -= 1;
2033
- return modLen * 8;
2034
- }
2035
- function validatePublicKey(key) {
2036
- const keyType = key.asymmetricKeyType;
2037
- switch (keyType) {
2038
- case "rsa": {
2039
- const modulusLength = getRsaModulusLength(key);
2040
- if (modulusLength < 2048) {
2041
- return {
2042
- valid: false,
2043
- reason: `RSA key is ${modulusLength} bits, minimum is 2048 bits.`
2044
- };
2045
- }
2046
- return { valid: true };
2047
- }
2048
- case "ed25519":
2049
- return { valid: true };
2050
- case "dsa":
2051
- return {
2052
- valid: false,
2053
- reason: "DSA keys are not supported. Use Ed25519 or RSA (2048+ bits)."
2054
- };
2055
- case "ec":
2056
- return {
2057
- valid: false,
2058
- reason: "ECDSA keys are not supported. Use Ed25519 or RSA (2048+ bits)."
2059
- };
2060
- default:
2061
- return {
2062
- valid: false,
2063
- reason: `Unsupported key type: ${keyType}. Use Ed25519 or RSA (2048+ bits).`
2064
- };
2065
- }
2066
- }
2067
-
2068
2245
  // src/prompts/inputKey.ts
2069
2246
  import inquirer7 from "inquirer";
2070
2247
  var inputKeyPrompt = async (message, defaultValue) => {
@@ -2082,95 +2259,99 @@ var inputKeyPrompt = async (message, defaultValue) => {
2082
2259
  var init_inputKey = () => {};
2083
2260
 
2084
2261
  // src/commands/key/add.ts
2085
- import crypto10 from "node:crypto";
2262
+ import crypto11 from "node:crypto";
2086
2263
  import { existsSync as existsSync7 } from "node:fs";
2087
2264
  import fs12 from "node:fs/promises";
2088
2265
  import os5 from "node:os";
2089
2266
  import path13 from "node:path";
2090
- import chalk13 from "chalk";
2267
+ import chalk14 from "chalk";
2091
2268
  import inquirer8 from "inquirer";
2092
- var keyAddCommand = async (nameArg, options) => {
2269
+ var defaultDeps4, _runKeyAddCommand = async (nameArg, options, depsOverrides = {}) => {
2270
+ const deps = {
2271
+ ...defaultDeps4,
2272
+ ...depsOverrides
2273
+ };
2093
2274
  let publicKey;
2094
2275
  if (options?.fromSsh) {
2095
- const sshPath = options.fromSsh.startsWith("~") ? path13.join(os5.homedir(), options.fromSsh.slice(1)) : options.fromSsh;
2096
- if (!existsSync7(sshPath)) {
2097
- console.error(`File ${chalk13.cyan(sshPath)} does not exist. Please provide a valid SSH key path.`);
2098
- process.exit(1);
2276
+ const sshPath = options.fromSsh.startsWith("~") ? path13.join(deps.homedir(), options.fromSsh.slice(1)) : options.fromSsh;
2277
+ if (!deps.existsSync(sshPath)) {
2278
+ deps.logError(`File ${chalk14.cyan(sshPath)} does not exist. Please provide a valid SSH key path.`);
2279
+ deps.exit(1);
2099
2280
  }
2100
- const keyContent = await fs12.readFile(sshPath, "utf-8");
2101
- if (isPassphraseProtected(keyContent)) {
2102
- console.error(`${chalk13.red("Error:")} the provided key is passphrase-protected, which is not currently supported by dotenc.`);
2103
- process.exit(1);
2281
+ const keyContent = await deps.readFile(sshPath, "utf-8");
2282
+ if (deps.isPassphraseProtected(keyContent)) {
2283
+ deps.logError(`${chalk14.red("Error:")} the provided key is passphrase-protected, which is not currently supported by dotenc.`);
2284
+ deps.exit(1);
2104
2285
  }
2105
2286
  try {
2106
- const privateKey = crypto10.createPrivateKey(keyContent);
2107
- publicKey = crypto10.createPublicKey(privateKey);
2287
+ const privateKey = deps.createPrivateKey(keyContent);
2288
+ publicKey = deps.createPublicKey(privateKey);
2108
2289
  } catch {
2109
- const parsed = parseOpenSSHPrivateKey(keyContent);
2290
+ const parsed = deps.parseOpenSSHPrivateKey(keyContent);
2110
2291
  if (parsed) {
2111
- publicKey = crypto10.createPublicKey(parsed);
2292
+ publicKey = deps.createPublicKey(parsed);
2112
2293
  } else {
2113
2294
  try {
2114
- publicKey = crypto10.createPublicKey(keyContent);
2295
+ publicKey = deps.createPublicKey(keyContent);
2115
2296
  } catch (error) {
2116
- console.error("Invalid SSH key format. Please provide a valid SSH key file.");
2117
- console.error(`Details: ${error instanceof Error ? error.message : error}`);
2118
- process.exit(1);
2297
+ deps.logError("Invalid SSH key format. Please provide a valid SSH key file.");
2298
+ deps.logError(`Details: ${error instanceof Error ? error.message : error}`);
2299
+ deps.exit(1);
2119
2300
  }
2120
2301
  }
2121
2302
  }
2122
2303
  }
2123
2304
  if (options?.fromFile) {
2124
- if (!existsSync7(options.fromFile)) {
2125
- console.error(`File ${chalk13.cyan(options.fromFile)} does not exist. Please provide a valid file path.`);
2126
- process.exit(1);
2305
+ if (!deps.existsSync(options.fromFile)) {
2306
+ deps.logError(`File ${chalk14.cyan(options.fromFile)} does not exist. Please provide a valid file path.`);
2307
+ deps.exit(1);
2127
2308
  }
2128
- const keyContent = await fs12.readFile(options.fromFile, "utf-8");
2129
- if (isPassphraseProtected(keyContent)) {
2130
- console.error(`${chalk13.red("Error:")} the provided key is passphrase-protected, which is not currently supported by dotenc.`);
2131
- process.exit(1);
2309
+ const keyContent = await deps.readFile(options.fromFile, "utf-8");
2310
+ if (deps.isPassphraseProtected(keyContent)) {
2311
+ deps.logError(`${chalk14.red("Error:")} the provided key is passphrase-protected, which is not currently supported by dotenc.`);
2312
+ deps.exit(1);
2132
2313
  }
2133
2314
  try {
2134
- publicKey = crypto10.createPublicKey(keyContent);
2315
+ publicKey = deps.createPublicKey(keyContent);
2135
2316
  } catch {
2136
2317
  try {
2137
- const privateKey = crypto10.createPrivateKey(keyContent);
2138
- publicKey = crypto10.createPublicKey(privateKey);
2318
+ const privateKey = deps.createPrivateKey(keyContent);
2319
+ publicKey = deps.createPublicKey(privateKey);
2139
2320
  } catch {
2140
- const parsed = parseOpenSSHPrivateKey(keyContent);
2321
+ const parsed = deps.parseOpenSSHPrivateKey(keyContent);
2141
2322
  if (parsed) {
2142
- publicKey = crypto10.createPublicKey(parsed);
2323
+ publicKey = deps.createPublicKey(parsed);
2143
2324
  } else {
2144
- console.error("Invalid key format. Please provide a valid PEM formatted public or private key.");
2145
- process.exit(1);
2325
+ deps.logError("Invalid key format. Please provide a valid PEM formatted public or private key.");
2326
+ deps.exit(1);
2146
2327
  }
2147
2328
  }
2148
2329
  }
2149
2330
  }
2150
2331
  if (options?.fromString) {
2151
- if (isPassphraseProtected(options.fromString)) {
2152
- console.error(`${chalk13.red("Error:")} the provided key is passphrase-protected, which is not currently supported by dotenc.`);
2153
- process.exit(1);
2332
+ if (deps.isPassphraseProtected(options.fromString)) {
2333
+ deps.logError(`${chalk14.red("Error:")} the provided key is passphrase-protected, which is not currently supported by dotenc.`);
2334
+ deps.exit(1);
2154
2335
  }
2155
2336
  try {
2156
- publicKey = crypto10.createPublicKey(options.fromString);
2337
+ publicKey = deps.createPublicKey(options.fromString);
2157
2338
  } catch {
2158
2339
  try {
2159
- const privateKey = crypto10.createPrivateKey(options.fromString);
2160
- publicKey = crypto10.createPublicKey(privateKey);
2340
+ const privateKey = deps.createPrivateKey(options.fromString);
2341
+ publicKey = deps.createPublicKey(privateKey);
2161
2342
  } catch {
2162
- const parsed = parseOpenSSHPrivateKey(options.fromString);
2343
+ const parsed = deps.parseOpenSSHPrivateKey(options.fromString);
2163
2344
  if (parsed) {
2164
- publicKey = crypto10.createPublicKey(parsed);
2345
+ publicKey = deps.createPublicKey(parsed);
2165
2346
  } else {
2166
- console.error("Invalid key format. Please provide a valid PEM formatted public or private key.");
2167
- process.exit(1);
2347
+ deps.logError("Invalid key format. Please provide a valid PEM formatted public or private key.");
2348
+ deps.exit(1);
2168
2349
  }
2169
2350
  }
2170
2351
  }
2171
2352
  }
2172
2353
  if (!publicKey) {
2173
- const modePrompt = await inquirer8.prompt({
2354
+ const modePrompt = await deps.prompt({
2174
2355
  type: "list",
2175
2356
  name: "mode",
2176
2357
  message: "Would you like to add one of your SSH keys or paste a public key?",
@@ -2180,74 +2361,103 @@ var keyAddCommand = async (nameArg, options) => {
2180
2361
  ]
2181
2362
  });
2182
2363
  if (modePrompt.mode === "paste") {
2183
- const publicKeyInput = await inputKeyPrompt("Please paste your public key (PEM format):");
2364
+ const publicKeyInput = await deps.inputKeyPrompt("Please paste your public key (PEM format):");
2184
2365
  if (!publicKeyInput) {
2185
- console.error("No public key provided. Add operation cancelled.");
2186
- process.exit(1);
2366
+ deps.logError("No public key provided. Add operation cancelled.");
2367
+ deps.exit(1);
2187
2368
  }
2188
2369
  try {
2189
- publicKey = crypto10.createPublicKey(publicKeyInput);
2370
+ publicKey = deps.createPublicKey(publicKeyInput);
2190
2371
  } catch (error) {
2191
- console.error("Invalid public key format. Please provide a valid PEM formatted public key.");
2192
- console.error(`Details: ${error instanceof Error ? error.message : error}`);
2193
- process.exit(1);
2372
+ deps.logError("Invalid public key format. Please provide a valid PEM formatted public key.");
2373
+ deps.logError(`Details: ${error instanceof Error ? error.message : error}`);
2374
+ deps.exit(1);
2194
2375
  }
2195
2376
  } else {
2196
2377
  let selectedKey;
2197
2378
  try {
2198
- selectedKey = await choosePrivateKeyPrompt("Which SSH key do you want to add?");
2379
+ selectedKey = await deps.choosePrivateKeyPrompt("Which SSH key do you want to add?");
2199
2380
  } catch (error) {
2200
- console.error(error instanceof Error ? error.message : String(error));
2201
- process.exit(1);
2381
+ deps.logError(error instanceof Error ? error.message : String(error));
2382
+ deps.exit(1);
2202
2383
  }
2203
- publicKey = crypto10.createPublicKey(selectedKey.privateKey);
2384
+ publicKey = deps.createPublicKey(selectedKey.privateKey);
2204
2385
  if (!nameArg) {
2205
2386
  nameArg = selectedKey.name;
2206
2387
  }
2207
2388
  }
2208
2389
  }
2209
2390
  if (!publicKey) {
2210
- console.error("An unexpected error occurred. No public key was inferred from the provided input.");
2211
- process.exit(1);
2391
+ deps.logError("An unexpected error occurred. No public key was inferred from the provided input.");
2392
+ deps.exit(1);
2212
2393
  }
2213
- const validation = validatePublicKey(publicKey);
2394
+ const validation = deps.validatePublicKey(publicKey);
2214
2395
  if (!validation.valid) {
2215
- console.error(validation.reason);
2216
- process.exit(1);
2396
+ deps.logError(validation.reason);
2397
+ deps.exit(1);
2217
2398
  }
2218
2399
  const publicKeyOutput = publicKey.export({
2219
2400
  type: "spki",
2220
2401
  format: "pem"
2221
2402
  });
2222
- if (!existsSync7(path13.join(process.cwd(), ".dotenc"))) {
2223
- await fs12.mkdir(path13.join(process.cwd(), ".dotenc"));
2403
+ if (!deps.existsSync(path13.join(deps.cwd(), ".dotenc"))) {
2404
+ await deps.mkdir(path13.join(deps.cwd(), ".dotenc"));
2224
2405
  }
2225
2406
  let name = nameArg;
2226
2407
  if (!name) {
2227
- name = await inputNamePrompt("What name do you want to give to the new public key?");
2228
- if (existsSync7(path13.join(process.cwd(), ".dotenc", `${name}.pub`))) {
2229
- console.error(`A public key with name ${chalk13.cyan(name)} already exists. Please choose a different name.`);
2230
- process.exit(1);
2231
- }
2408
+ name = await deps.inputNamePrompt("What name do you want to give to the new public key?");
2232
2409
  }
2233
- await fs12.writeFile(path13.join(process.cwd(), ".dotenc", `${name}.pub`), publicKeyOutput, "utf-8");
2234
- console.log(`
2235
- Public key ${chalk13.cyan(name)} added successfully!`);
2410
+ const keyNameValidation = deps.validateKeyName(name);
2411
+ if (!keyNameValidation.valid) {
2412
+ deps.logError(`${chalk14.red("Error:")} ${keyNameValidation.reason}`);
2413
+ deps.exit(1);
2414
+ }
2415
+ const keyOutputPath = path13.join(deps.cwd(), ".dotenc", `${name}.pub`);
2416
+ if (deps.existsSync(keyOutputPath)) {
2417
+ deps.logError(`A public key with name ${chalk14.cyan(name)} already exists. Please choose a different name.`);
2418
+ deps.exit(1);
2419
+ }
2420
+ await deps.writeFile(keyOutputPath, publicKeyOutput, "utf-8");
2421
+ deps.logInfo(`
2422
+ Public key ${chalk14.cyan(name)} added successfully!`);
2423
+ }, keyAddCommand = async (nameArg, options) => {
2424
+ return _runKeyAddCommand(nameArg, options);
2236
2425
  };
2237
2426
  var init_add = __esm(() => {
2238
2427
  init_parseOpenSSHKey();
2239
2428
  init_choosePrivateKey();
2240
2429
  init_inputKey();
2241
2430
  init_inputName();
2431
+ defaultDeps4 = {
2432
+ createPrivateKey: crypto11.createPrivateKey,
2433
+ createPublicKey: crypto11.createPublicKey,
2434
+ existsSync: existsSync7,
2435
+ readFile: fs12.readFile,
2436
+ mkdir: fs12.mkdir,
2437
+ writeFile: fs12.writeFile,
2438
+ homedir: os5.homedir,
2439
+ cwd: process.cwd,
2440
+ prompt: inquirer8.prompt,
2441
+ isPassphraseProtected,
2442
+ parseOpenSSHPrivateKey,
2443
+ validatePublicKey,
2444
+ validateKeyName,
2445
+ choosePrivateKeyPrompt,
2446
+ inputKeyPrompt,
2447
+ inputNamePrompt,
2448
+ logError: (message) => console.error(message),
2449
+ logInfo: (message) => console.log(message),
2450
+ exit: (code) => process.exit(code)
2451
+ };
2242
2452
  });
2243
2453
 
2244
2454
  // src/commands/init.ts
2245
- import crypto11 from "node:crypto";
2455
+ import crypto12 from "node:crypto";
2246
2456
  import { existsSync as existsSync8 } from "node:fs";
2247
2457
  import fs13 from "node:fs/promises";
2248
2458
  import os6 from "node:os";
2249
2459
  import path14 from "node:path";
2250
- import chalk14 from "chalk";
2460
+ import chalk15 from "chalk";
2251
2461
  var _resolveDocsUrl = () => {
2252
2462
  if (typeof package_default.homepage === "string" && package_default.homepage.trim().length > 0) {
2253
2463
  return package_default.homepage;
@@ -2257,64 +2467,70 @@ var _resolveDocsUrl = () => {
2257
2467
  return;
2258
2468
  }
2259
2469
  return repositoryUrl.replace(/^git\+/, "").replace(/\.git$/, "");
2260
- }, initCommand = async (options) => {
2261
- const username = options.name || await inputNamePrompt("What's your name?", os6.userInfo().username);
2470
+ }, defaultDeps5, _runInitCommand = async (options, depsOverrides = {}) => {
2471
+ const deps = {
2472
+ ...defaultDeps5,
2473
+ ...depsOverrides
2474
+ };
2475
+ const username = options.name || await deps.inputNamePrompt("What's your name?", deps.userInfo().username);
2262
2476
  if (!username) {
2263
- console.error(`${chalk14.red("Error:")} no name provided.`);
2264
- process.exit(1);
2477
+ deps.logError(`${chalk15.red("Error:")} no name provided.`);
2478
+ deps.exit(1);
2265
2479
  }
2266
2480
  let keyEntry;
2267
2481
  try {
2268
- keyEntry = await choosePrivateKeyPrompt("Which SSH key would you like to use?");
2482
+ keyEntry = await deps.choosePrivateKeyPrompt("Which SSH key would you like to use?");
2269
2483
  } catch (error) {
2270
- console.error(error instanceof Error ? error.message : String(error));
2271
- process.exit(1);
2484
+ deps.logError(error instanceof Error ? error.message : String(error));
2485
+ deps.exit(1);
2272
2486
  }
2273
- console.log(`Adding key: ${chalk14.cyan(username)} (${keyEntry.algorithm})`);
2274
- const publicKey = crypto11.createPublicKey(keyEntry.privateKey);
2487
+ deps.logInfo(`Adding key: ${chalk15.cyan(username)} (${keyEntry.algorithm})`);
2488
+ const publicKey = deps.createPublicKey(keyEntry.privateKey);
2275
2489
  const publicKeyPem = publicKey.export({ type: "spki", format: "pem" }).toString();
2276
- await keyAddCommand(username, {
2490
+ await deps.keyAddCommand(username, {
2277
2491
  fromString: publicKeyPem
2278
2492
  });
2279
2493
  try {
2280
- setupGitDiff();
2494
+ deps.setupGitDiff();
2281
2495
  } catch (_error) {
2282
- console.warn(`${chalk14.yellow("Warning:")} could not set up git diff driver. You can run ${chalk14.gray("dotenc init")} again inside a git repository.`);
2496
+ deps.logWarn(`${chalk15.yellow("Warning:")} could not set up git diff driver. You can run ${chalk15.gray("dotenc init")} again inside a git repository.`);
2283
2497
  }
2284
2498
  let initialContent;
2285
- const envPath = path14.join(process.cwd(), ".env");
2286
- if (existsSync8(envPath)) {
2287
- initialContent = await fs13.readFile(envPath, "utf-8");
2288
- await fs13.unlink(envPath);
2289
- console.log(`Migrated ${chalk14.gray(".env")} contents to ${chalk14.cyan("development")} environment.`);
2499
+ const envPath = path14.join(deps.cwd(), ".env");
2500
+ if (deps.existsSync(envPath)) {
2501
+ initialContent = await deps.readFile(envPath, "utf-8");
2502
+ await deps.unlink(envPath);
2503
+ deps.logInfo(`Migrated ${chalk15.gray(".env")} contents to ${chalk15.cyan("development")} environment.`);
2290
2504
  }
2291
- await createCommand("development", username, initialContent);
2505
+ await deps.createCommand("development", username, initialContent);
2292
2506
  if (username !== "development") {
2293
- await createCommand(username, username);
2507
+ await deps.createCommand(username, username);
2294
2508
  }
2295
- console.log(`
2296
- ${chalk14.green("✔")} Initialization complete!`);
2297
- console.log(`
2509
+ deps.logInfo(`
2510
+ ${chalk15.green("✔")} Initialization complete!`);
2511
+ deps.logInfo(`
2298
2512
  Some useful tips:`);
2299
- const developmentEditCmd = chalk14.gray("dotenc env edit development");
2300
- const personalEditCmd = chalk14.gray(`dotenc env edit ${username}`);
2301
- console.log(`- Edit the development environment: ${developmentEditCmd}`);
2513
+ const developmentEditCmd = chalk15.gray("dotenc env edit development");
2514
+ const personalEditCmd = chalk15.gray(`dotenc env edit ${username}`);
2515
+ deps.logInfo(`- Edit the development environment: ${developmentEditCmd}`);
2302
2516
  if (username !== "development") {
2303
- console.log(`- Edit your personal environment: ${personalEditCmd}`);
2517
+ deps.logInfo(`- Edit your personal environment: ${personalEditCmd}`);
2304
2518
  }
2305
2519
  const devEnvironmentChain = username === "development" ? "development" : `development,${username}`;
2306
- const devCmd = chalk14.gray("dotenc dev <command>");
2307
- console.log(`- Run in development mode: ${devCmd} ${chalk14.gray(`(loads ${devEnvironmentChain})`)}`);
2308
- const docsUrl = _resolveDocsUrl();
2520
+ const devCmd = chalk15.gray("dotenc dev <command>");
2521
+ deps.logInfo(`- Run in development mode: ${devCmd} ${chalk15.gray(`(loads ${devEnvironmentChain})`)}`);
2522
+ const docsUrl = deps.resolveDocsUrl();
2309
2523
  if (docsUrl) {
2310
- console.log(`- Full docs: ${chalk14.gray(docsUrl)}`);
2524
+ deps.logInfo(`- Full docs: ${chalk15.gray(docsUrl)}`);
2311
2525
  }
2312
- if (existsSync8(".claude") || existsSync8("CLAUDE.md")) {
2313
- console.log(`- Install the agent skill: ${chalk14.gray("dotenc tools install-agent-skill")}`);
2526
+ if (deps.existsSync(".claude") || deps.existsSync("CLAUDE.md")) {
2527
+ deps.logInfo(`- Install the agent skill: ${chalk15.gray("dotenc tools install-agent-skill")}`);
2314
2528
  }
2315
- if (existsSync8(".vscode") || existsSync8(".cursor") || existsSync8(".windsurf")) {
2316
- console.log(`- Add the editor extension: ${chalk14.gray("dotenc tools install-vscode-extension")}`);
2529
+ if (deps.existsSync(".vscode") || deps.existsSync(".cursor") || deps.existsSync(".windsurf")) {
2530
+ deps.logInfo(`- Add the editor extension: ${chalk15.gray("dotenc tools install-vscode-extension")}`);
2317
2531
  }
2532
+ }, initCommand = async (options) => {
2533
+ return _runInitCommand(options);
2318
2534
  };
2319
2535
  var init_init = __esm(() => {
2320
2536
  init_package();
@@ -2323,6 +2539,24 @@ var init_init = __esm(() => {
2323
2539
  init_inputName();
2324
2540
  init_create();
2325
2541
  init_add();
2542
+ defaultDeps5 = {
2543
+ inputNamePrompt,
2544
+ userInfo: os6.userInfo,
2545
+ choosePrivateKeyPrompt,
2546
+ createPublicKey: crypto12.createPublicKey,
2547
+ keyAddCommand,
2548
+ setupGitDiff,
2549
+ existsSync: existsSync8,
2550
+ readFile: fs13.readFile,
2551
+ unlink: fs13.unlink,
2552
+ cwd: process.cwd,
2553
+ createCommand,
2554
+ logInfo: (message) => console.log(message),
2555
+ logWarn: (message) => console.warn(message),
2556
+ logError: (message) => console.error(message),
2557
+ resolveDocsUrl: _resolveDocsUrl,
2558
+ exit: (code) => process.exit(code)
2559
+ };
2326
2560
  });
2327
2561
 
2328
2562
  // src/commands/key/list.ts
@@ -2358,15 +2592,20 @@ var init_confirm = () => {};
2358
2592
  import { existsSync as existsSync9 } from "node:fs";
2359
2593
  import fs14 from "node:fs/promises";
2360
2594
  import path15 from "node:path";
2361
- import chalk15 from "chalk";
2595
+ import chalk16 from "chalk";
2362
2596
  var keyRemoveCommand = async (nameArg) => {
2363
2597
  let name = nameArg;
2364
2598
  if (!name) {
2365
2599
  name = await choosePublicKeyPrompt("Which public key do you want to remove?");
2366
2600
  }
2601
+ const keyNameValidation = validateKeyName(name);
2602
+ if (!keyNameValidation.valid) {
2603
+ console.error(`${chalk16.red("Error:")} ${keyNameValidation.reason}`);
2604
+ process.exit(1);
2605
+ }
2367
2606
  const filePath = path15.join(process.cwd(), ".dotenc", `${name}.pub`);
2368
2607
  if (!existsSync9(filePath)) {
2369
- console.error(`Public key ${chalk15.cyan(name)} not found.`);
2608
+ console.error(`Public key ${chalk16.cyan(name)} not found.`);
2370
2609
  process.exit(1);
2371
2610
  }
2372
2611
  const allEnvironments = await getEnvironments();
@@ -2380,7 +2619,7 @@ var keyRemoveCommand = async (nameArg) => {
2380
2619
  } catch {}
2381
2620
  }
2382
2621
  if (affectedEnvironments.length > 0) {
2383
- console.log(`Key ${chalk15.cyan(name)} has access to the following environments:`);
2622
+ console.log(`Key ${chalk16.cyan(name)} has access to the following environments:`);
2384
2623
  for (const env of affectedEnvironments) {
2385
2624
  console.log(` - ${env}`);
2386
2625
  }
@@ -2393,7 +2632,7 @@ Access will be revoked from these environments automatically.`);
2393
2632
  return;
2394
2633
  }
2395
2634
  await fs14.unlink(filePath);
2396
- console.log(`Public key ${chalk15.cyan(name)} removed successfully.`);
2635
+ console.log(`Public key ${chalk16.cyan(name)} removed successfully.`);
2397
2636
  for (const envName of affectedEnvironments) {
2398
2637
  try {
2399
2638
  const envJson = await getEnvironmentByName(envName);
@@ -2401,9 +2640,9 @@ Access will be revoked from these environments automatically.`);
2401
2640
  await encryptEnvironment(envName, content, {
2402
2641
  revokePublicKeys: [name]
2403
2642
  });
2404
- console.log(`Revoked access from ${chalk15.cyan(envName)} environment.`);
2643
+ console.log(`Revoked access from ${chalk16.cyan(envName)} environment.`);
2405
2644
  } catch {
2406
- console.warn(`${chalk15.yellow("Warning:")} could not revoke access from ${chalk15.cyan(envName)}. You may need to run ${chalk15.gray(`dotenc auth revoke ${envName} ${name}`)} manually or rotate the environment.`);
2645
+ console.warn(`${chalk16.yellow("Warning:")} could not revoke access from ${chalk16.cyan(envName)}. You may need to run ${chalk16.gray(`dotenc auth revoke ${envName} ${name}`)} manually or rotate the environment.`);
2407
2646
  }
2408
2647
  }
2409
2648
  };
@@ -2437,7 +2676,7 @@ var init_textconv = __esm(() => {
2437
2676
 
2438
2677
  // src/commands/tools/install-agent-skill.ts
2439
2678
  import { spawn as spawn2 } from "node:child_process";
2440
- import chalk16 from "chalk";
2679
+ import chalk17 from "chalk";
2441
2680
  import inquirer10 from "inquirer";
2442
2681
  var SKILL_SOURCE = "ivanfilhoz/dotenc", SKILL_NAME = "dotenc", runNpx = (args, spawnImpl = spawn2) => new Promise((resolve, reject) => {
2443
2682
  const child = spawnImpl("npx", args, {
@@ -2446,9 +2685,9 @@ var SKILL_SOURCE = "ivanfilhoz/dotenc", SKILL_NAME = "dotenc", runNpx = (args, s
2446
2685
  });
2447
2686
  child.on("error", reject);
2448
2687
  child.on("exit", (code) => resolve(code ?? 1));
2449
- }), defaultDeps2, _runInstallAgentSkillCommand = async (options, depsOverrides = {}) => {
2688
+ }), defaultDeps6, _runInstallAgentSkillCommand = async (options, depsOverrides = {}) => {
2450
2689
  const deps = {
2451
- ...defaultDeps2,
2690
+ ...defaultDeps6,
2452
2691
  ...depsOverrides
2453
2692
  };
2454
2693
  const { scope } = await deps.prompt([
@@ -2474,21 +2713,21 @@ var SKILL_SOURCE = "ivanfilhoz/dotenc", SKILL_NAME = "dotenc", runNpx = (args, s
2474
2713
  try {
2475
2714
  exitCode = await deps.runNpx(args);
2476
2715
  } catch (error) {
2477
- deps.logError(`${chalk16.red("Error:")} failed to run ${chalk16.gray(npxCommand)}.`);
2478
- deps.logError(`${chalk16.red("Details:")} ${error instanceof Error ? error.message : String(error)}`);
2716
+ deps.logError(`${chalk17.red("Error:")} failed to run ${chalk17.gray(npxCommand)}.`);
2717
+ deps.logError(`${chalk17.red("Details:")} ${error instanceof Error ? error.message : String(error)}`);
2479
2718
  deps.exit(1);
2480
2719
  }
2481
2720
  if (exitCode !== 0) {
2482
- deps.logError(`${chalk16.red("Error:")} skill installation command exited with code ${exitCode}.`);
2721
+ deps.logError(`${chalk17.red("Error:")} skill installation command exited with code ${exitCode}.`);
2483
2722
  deps.exit(exitCode);
2484
2723
  }
2485
- deps.log(`${chalk16.green("✓")} Agent skill installation completed via ${chalk16.gray(npxCommand)}.`);
2486
- deps.log(`Run ${chalk16.gray("/dotenc")} in your agent to use it.`);
2724
+ deps.log(`${chalk17.green("✓")} Agent skill installation completed via ${chalk17.gray(npxCommand)}.`);
2725
+ deps.log(`Run ${chalk17.gray("/dotenc")} in your agent to use it.`);
2487
2726
  }, installAgentSkillCommand = async (options) => {
2488
2727
  await _runInstallAgentSkillCommand(options);
2489
2728
  };
2490
2729
  var init_install_agent_skill = __esm(() => {
2491
- defaultDeps2 = {
2730
+ defaultDeps6 = {
2492
2731
  prompt: inquirer10.prompt,
2493
2732
  runNpx,
2494
2733
  log: console.log,
@@ -2503,7 +2742,7 @@ import { existsSync as existsSync10 } from "node:fs";
2503
2742
  import fs16 from "node:fs/promises";
2504
2743
  import path17 from "node:path";
2505
2744
  import { promisify } from "node:util";
2506
- import chalk17 from "chalk";
2745
+ import chalk18 from "chalk";
2507
2746
  import inquirer11 from "inquirer";
2508
2747
  async function addToExtensionsJson() {
2509
2748
  const extensionsJsonPath = path17.join(process.cwd(), ".vscode", "extensions.json");
@@ -2524,9 +2763,9 @@ async function addToExtensionsJson() {
2524
2763
  if (!json.recommendations.includes(EXTENSION_ID)) {
2525
2764
  json.recommendations.push(EXTENSION_ID);
2526
2765
  await fs16.writeFile(extensionsJsonPath, JSON.stringify(json, null, 2), "utf-8");
2527
- console.log(`${chalk17.green("✓")} Added dotenc to ${chalk17.gray(".vscode/extensions.json")}`);
2766
+ console.log(`${chalk18.green("✓")} Added dotenc to ${chalk18.gray(".vscode/extensions.json")}`);
2528
2767
  } else {
2529
- console.log(`${chalk17.green("✓")} dotenc already in ${chalk17.gray(".vscode/extensions.json")}`);
2768
+ console.log(`${chalk18.green("✓")} dotenc already in ${chalk18.gray(".vscode/extensions.json")}`);
2530
2769
  }
2531
2770
  }
2532
2771
  async function which(bin) {
@@ -2590,7 +2829,7 @@ async function _runInstallVscodeExtension(getEditors = detectEditors, _openUrl =
2590
2829
  await addToExtensionsJson();
2591
2830
  if (editors.length === 0) {
2592
2831
  console.log(`
2593
- Install the extension in VS Code: ${chalk17.cyan(EDITOR_PROTOCOL_URLS.vscode)}`);
2832
+ Install the extension in VS Code: ${chalk18.cyan(EDITOR_PROTOCOL_URLS.vscode)}`);
2594
2833
  return;
2595
2834
  }
2596
2835
  if (editors.length === 1) {
@@ -2609,10 +2848,10 @@ Install the extension in VS Code: ${chalk17.cyan(EDITOR_PROTOCOL_URLS.vscode)}`)
2609
2848
  try {
2610
2849
  await _openUrl(url);
2611
2850
  } catch {
2612
- console.log(`Open manually: ${chalk17.cyan(url)}`);
2851
+ console.log(`Open manually: ${chalk18.cyan(url)}`);
2613
2852
  }
2614
2853
  } else {
2615
- console.log(`Install manually: ${chalk17.cyan(url)}`);
2854
+ console.log(`Install manually: ${chalk18.cyan(url)}`);
2616
2855
  }
2617
2856
  return;
2618
2857
  }
@@ -2621,7 +2860,7 @@ Install the extension in your editor:`);
2621
2860
  for (const editor of editors) {
2622
2861
  const name = EDITOR_NAMES[editor] ?? editor;
2623
2862
  const url = EDITOR_PROTOCOL_URLS[editor];
2624
- console.log(` ${name}: ${chalk17.cyan(url)}`);
2863
+ console.log(` ${name}: ${chalk18.cyan(url)}`);
2625
2864
  }
2626
2865
  }
2627
2866
  var execFileAsync, execAsync, EXTENSION_ID = "dotenc.dotenc", EDITOR_PROTOCOL_URLS, EDITOR_NAMES, installVscodeExtensionCommand = async () => {
@@ -2728,7 +2967,7 @@ var init_update = () => {};
2728
2967
 
2729
2968
  // src/commands/update.ts
2730
2969
  import { spawn as spawn3 } from "node:child_process";
2731
- import chalk18 from "chalk";
2970
+ import chalk19 from "chalk";
2732
2971
  var runPackageManagerCommand = (command, args, spawnImpl = spawn3) => new Promise((resolve, reject) => {
2733
2972
  const child = spawnImpl(command, args, {
2734
2973
  stdio: "inherit",
@@ -2736,37 +2975,52 @@ var runPackageManagerCommand = (command, args, spawnImpl = spawn3) => new Promis
2736
2975
  });
2737
2976
  child.on("error", reject);
2738
2977
  child.on("exit", (code) => resolve(code ?? 1));
2739
- }), defaultDeps3, updateCommands, _runUpdateCommand = async (depsOverrides = {}) => {
2978
+ }), defaultDeps7, updateCommands, _runUpdateCommand = async (depsOverrides = {}) => {
2740
2979
  const deps = {
2741
- ...defaultDeps3,
2980
+ ...defaultDeps7,
2742
2981
  ...depsOverrides
2743
2982
  };
2744
2983
  const method = deps.detectInstallMethod();
2745
2984
  if (method === "binary") {
2746
- deps.log(`Standalone binary detected. Download the latest release at ${chalk18.cyan(GITHUB_RELEASES_URL)}.`);
2985
+ deps.log(`Standalone binary detected. Download the latest release at ${chalk19.cyan(GITHUB_RELEASES_URL)}.`);
2747
2986
  return;
2748
2987
  }
2749
2988
  if (method === "unknown") {
2750
2989
  deps.log("Could not determine installation method automatically.");
2751
2990
  deps.log(`Try one of these commands:`);
2752
- deps.log(` ${chalk18.gray("brew upgrade dotenc")}`);
2753
- deps.log(` ${chalk18.gray("scoop update dotenc")}`);
2754
- deps.log(` ${chalk18.gray("npm install -g @dotenc/cli")}`);
2755
- deps.log(`Or download from ${chalk18.cyan(GITHUB_RELEASES_URL)}.`);
2991
+ deps.log(` ${chalk19.gray("brew update && brew upgrade dotenc")}`);
2992
+ deps.log(` ${chalk19.gray("scoop update dotenc")}`);
2993
+ deps.log(` ${chalk19.gray("npm install -g @dotenc/cli")}`);
2994
+ deps.log(`Or download from ${chalk19.cyan(GITHUB_RELEASES_URL)}.`);
2756
2995
  return;
2757
2996
  }
2758
2997
  const updater = updateCommands[method];
2759
2998
  deps.log(`Updating dotenc via ${updater.label}...`);
2999
+ if (method === "homebrew") {
3000
+ try {
3001
+ const brewUpdateCode = await deps.runPackageManagerCommand("brew", [
3002
+ "update"
3003
+ ]);
3004
+ if (brewUpdateCode !== 0) {
3005
+ deps.logError(`${chalk19.red("Error:")} update command exited with code ${brewUpdateCode}.`);
3006
+ deps.exit(brewUpdateCode);
3007
+ }
3008
+ } catch (error) {
3009
+ deps.logError(`${chalk19.red("Error:")} failed to run ${chalk19.gray("brew update")}.`);
3010
+ deps.logError(`${chalk19.red("Details:")} ${error instanceof Error ? error.message : String(error)}`);
3011
+ deps.exit(1);
3012
+ }
3013
+ }
2760
3014
  let exitCode = 0;
2761
3015
  try {
2762
3016
  exitCode = await deps.runPackageManagerCommand(updater.command, updater.args);
2763
3017
  } catch (error) {
2764
- deps.logError(`${chalk18.red("Error:")} failed to run ${chalk18.gray([updater.command, ...updater.args].join(" "))}.`);
2765
- deps.logError(`${chalk18.red("Details:")} ${error instanceof Error ? error.message : String(error)}`);
3018
+ deps.logError(`${chalk19.red("Error:")} failed to run ${chalk19.gray([updater.command, ...updater.args].join(" "))}.`);
3019
+ deps.logError(`${chalk19.red("Details:")} ${error instanceof Error ? error.message : String(error)}`);
2766
3020
  deps.exit(1);
2767
3021
  }
2768
3022
  if (exitCode !== 0) {
2769
- deps.logError(`${chalk18.red("Error:")} update command exited with code ${exitCode}.`);
3023
+ deps.logError(`${chalk19.red("Error:")} update command exited with code ${exitCode}.`);
2770
3024
  deps.exit(exitCode);
2771
3025
  }
2772
3026
  }, updateCommand = async () => {
@@ -2774,7 +3028,7 @@ var runPackageManagerCommand = (command, args, spawnImpl = spawn3) => new Promis
2774
3028
  };
2775
3029
  var init_update2 = __esm(() => {
2776
3030
  init_update();
2777
- defaultDeps3 = {
3031
+ defaultDeps7 = {
2778
3032
  detectInstallMethod,
2779
3033
  runPackageManagerCommand,
2780
3034
  log: console.log,
@@ -2853,8 +3107,8 @@ var init_whoami = __esm(() => {
2853
3107
  });
2854
3108
 
2855
3109
  // src/helpers/updateNotifier.ts
2856
- import chalk19 from "chalk";
2857
- var CHECK_INTERVAL_MS, defaultDeps4, shouldSkipCheck = (args, env) => {
3110
+ import chalk20 from "chalk";
3111
+ var CHECK_INTERVAL_MS, defaultDeps8, shouldSkipCheck = (args, env) => {
2858
3112
  if (env.DOTENC_SKIP_UPDATE_CHECK === "1") {
2859
3113
  return true;
2860
3114
  }
@@ -2876,7 +3130,7 @@ var CHECK_INTERVAL_MS, defaultDeps4, shouldSkipCheck = (args, env) => {
2876
3130
  } catch {}
2877
3131
  }, maybeNotifyAboutUpdate = async (depsOverrides = {}) => {
2878
3132
  const deps = {
2879
- ...defaultDeps4,
3133
+ ...defaultDeps8,
2880
3134
  ...depsOverrides
2881
3135
  };
2882
3136
  if (shouldSkipCheck(deps.args, deps.env)) {
@@ -2909,7 +3163,7 @@ var CHECK_INTERVAL_MS, defaultDeps4, shouldSkipCheck = (args, env) => {
2909
3163
  if (updateState.notifiedVersion === latestVersion) {
2910
3164
  return;
2911
3165
  }
2912
- deps.log(`${chalk19.yellow("Update available:")} ${chalk19.gray(`dotenc ${deps.currentVersion}`)} -> ${chalk19.cyan(`dotenc ${latestVersion}`)}. Run ${chalk19.gray("dotenc update")}.`);
3166
+ deps.log(`${chalk20.yellow("Update available:")} ${chalk20.gray(`dotenc ${deps.currentVersion}`)} -> ${chalk20.cyan(`dotenc ${latestVersion}`)}. Run ${chalk20.gray("dotenc update")}.`);
2913
3167
  updateState = {
2914
3168
  ...updateState,
2915
3169
  notifiedVersion: latestVersion
@@ -2921,7 +3175,7 @@ var init_updateNotifier = __esm(() => {
2921
3175
  init_homeConfig();
2922
3176
  init_update();
2923
3177
  CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000;
2924
- defaultDeps4 = {
3178
+ defaultDeps8 = {
2925
3179
  getHomeConfig,
2926
3180
  setHomeConfig,
2927
3181
  fetchLatestVersion,
@@ -2975,7 +3229,7 @@ var init_program = __esm(async () => {
2975
3229
  auth.command("grant").argument("[environment]", "the environment to grant access to").argument("[publicKey]", "the name of the public key to grant access to the environment").description("grant access to an environment").action(grantCommand);
2976
3230
  auth.command("revoke").argument("[environment]", "the environment to revoke access from").argument("[publicKey]", "the name of the public key to revoke access from the environment").description("revoke access from an environment").action(revokeCommand);
2977
3231
  auth.command("list").argument("[environment]", "the environment to list access for").description("list keys with access to an environment").action(authListCommand);
2978
- program.command("run").argument("<command>", "the command to run").argument("[args...]", "the arguments to pass to the command").addOption(new Option("-e, --env <env1>[,env2[,...]]", "the environments to run the command in")).description("run a command in an environment").action(runCommand);
3232
+ program.command("run").argument("<command>", "the command to run").argument("[args...]", "the arguments to pass to the command").addOption(new Option("-e, --env <env1>[,env2[,...]]", "the environments to run the command in")).addOption(new Option("--strict", "fail if any selected environment fails to load")).description("run a command in an environment").action(runCommand);
2979
3233
  program.command("dev").argument("<command>", "the command to run").argument("[args...]", "the arguments to pass to the command").description("shortcut for 'run -e development,<yourname> <command>'").action((command, args) => devCommand(command, args));
2980
3234
  key = program.command("key").description("manage keys");
2981
3235
  key.command("add").argument("[name]", "the name of the public key in the project").addOption(new Option("--from-ssh <path>", "add a public key derived from an SSH key file")).addOption(new Option("-f, --from-file <file>", "add the key from a PEM file")).addOption(new Option("-s, --from-string <string>", "add a public key from a string")).description("add a public key to the project").action(keyAddCommand);