@envsave/cli 1.0.37 → 1.0.39

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.js CHANGED
@@ -274,7 +274,7 @@ __export(exports_vault, {
274
274
  configureRateLimit: () => configureRateLimit,
275
275
  changePassword: () => changePassword
276
276
  });
277
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, chmodSync, appendFileSync, statSync } from "node:fs";
277
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, chmodSync, appendFileSync } from "node:fs";
278
278
  import { join as join2 } from "node:path";
279
279
  import { homedir as homedir2 } from "node:os";
280
280
  import { randomBytes as randomBytes2 } from "node:crypto";
@@ -304,12 +304,12 @@ function guardRealVault() {
304
304
  }
305
305
  function readConfig() {
306
306
  if (!existsSync2(getConfigFile())) {
307
- return { maxGetsPerMinute: DEFAULT_RATE_LIMIT_MAX, sessionTtlHours: DEFAULT_SESSION_TTL_MS / 3600000 };
307
+ return { maxGetsPerMinute: DEFAULT_RATE_LIMIT_MAX };
308
308
  }
309
309
  try {
310
310
  return JSON.parse(readFileSync3(getConfigFile(), "utf8"));
311
311
  } catch {
312
- return { maxGetsPerMinute: DEFAULT_RATE_LIMIT_MAX, sessionTtlHours: DEFAULT_SESSION_TTL_MS / 3600000 };
312
+ return { maxGetsPerMinute: DEFAULT_RATE_LIMIT_MAX };
313
313
  }
314
314
  }
315
315
  function writeConfig(config) {
@@ -321,8 +321,6 @@ function configureRateLimit(password, options) {
321
321
  const config = readConfig();
322
322
  if (options.maxGetsPerMinute !== undefined)
323
323
  config.maxGetsPerMinute = options.maxGetsPerMinute;
324
- if (options.sessionTtlHours !== undefined)
325
- config.sessionTtlHours = options.sessionTtlHours;
326
324
  writeConfig(config);
327
325
  return config;
328
326
  }
@@ -459,12 +457,6 @@ function readSessionCache() {
459
457
  if (!existsSync2(getSessionFile()) || !existsSync2(getCacheFile()))
460
458
  return null;
461
459
  try {
462
- const sessionStat = statSync(getSessionFile());
463
- const age = Date.now() - sessionStat.mtimeMs;
464
- const config = readConfig();
465
- if (age > config.sessionTtlHours * 3600000) {
466
- return null;
467
- }
468
460
  const sessionKeyHex = readFileSync3(getSessionFile(), "utf8").trim();
469
461
  const sessionKey = Buffer.from(sessionKeyHex, "hex");
470
462
  const payload = readFileSync3(getCacheFile());
@@ -522,7 +514,7 @@ function initVault(password, options) {
522
514
  const hmacSeed = randomBytes2(32).toString("hex");
523
515
  const sshKeyContent = options?.sshKeyPath ? readFileSync3(options.sshKeyPath, "utf8") : undefined;
524
516
  const vault = {
525
- version: 4,
517
+ version: 5,
526
518
  hmacSeed,
527
519
  sshKeyPath: options?.sshKeyPath,
528
520
  secrets: {},
@@ -552,7 +544,8 @@ function setSecret(key, value, password) {
552
544
  const vault = readMainVault(password);
553
545
  const sshKeyContent = loadSshKey(vault);
554
546
  const opaque = computeOpaqueName(vault.hmacSeed, key, sshKeyContent);
555
- if (!(opaque in vault.secrets) && !hasValidLicense()) {
547
+ const updated = opaque in vault.secrets;
548
+ if (!updated && !hasValidLicense()) {
556
549
  if (vault.registry.length >= FREE_TIER_MAX_SECRETS) {
557
550
  throw new Error(`Free tier limit: max ${FREE_TIER_MAX_SECRETS} secrets. Run 'envsave activate <token>' to unlock unlimited secrets.
558
551
  Get your license at https://api.envsave.com`);
@@ -564,7 +557,7 @@ Get your license at https://api.envsave.com`);
564
557
  }
565
558
  writeMainVault(vault, password);
566
559
  writeSessionCache(vault.secrets, vault.registry);
567
- return opaque;
560
+ return { opaque, updated };
568
561
  }
569
562
  function showSecret(cleanName, password) {
570
563
  const vault = readMainVault(password);
@@ -689,12 +682,11 @@ function hasSecretWithSession(opaqueKey) {
689
682
  return false;
690
683
  }
691
684
  }
692
- var FREE_TIER_MAX_SECRETS = 5, DEFAULT_SESSION_TTL_MS, DEFAULT_RATE_LIMIT_WINDOW_MS, DEFAULT_RATE_LIMIT_MAX = 30, BASE_CHARS_V3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@$&*=-!", BASE_CHARS_V4 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", BASE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
685
+ var FREE_TIER_MAX_SECRETS = 5, DEFAULT_RATE_LIMIT_WINDOW_MS, DEFAULT_RATE_LIMIT_MAX = 30, BASE_CHARS_V3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@$&*=-!", BASE_CHARS_V4 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", BASE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
693
686
  var init_vault = __esm(() => {
694
687
  init_crypto();
695
688
  init_env_parser();
696
689
  init_license();
697
- DEFAULT_SESSION_TTL_MS = 4 * 60 * 60 * 1000;
698
690
  DEFAULT_RATE_LIMIT_WINDOW_MS = 60 * 1000;
699
691
  });
700
692
 
@@ -703,7 +695,7 @@ var exports_scanner = {};
703
695
  __export(exports_scanner, {
704
696
  scanProject: () => scanProject
705
697
  });
706
- import { readdirSync, readFileSync as readFileSync4, statSync as statSync2 } from "node:fs";
698
+ import { readdirSync, readFileSync as readFileSync4, statSync } from "node:fs";
707
699
  import { join as join3, relative } from "node:path";
708
700
  function walkDir(dir, base, results) {
709
701
  let entries;
@@ -718,7 +710,7 @@ function walkDir(dir, base, results) {
718
710
  const fullPath = join3(dir, entry);
719
711
  let stat;
720
712
  try {
721
- stat = statSync2(fullPath);
713
+ stat = statSync(fullPath);
722
714
  } catch {
723
715
  continue;
724
716
  }
@@ -860,7 +852,7 @@ var init_scanner = __esm(() => {
860
852
  init_vault();
861
853
  init_license();
862
854
  import { createInterface } from "node:readline";
863
- import { existsSync as existsSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
855
+ import { existsSync as existsSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
864
856
  import { spawnSync } from "node:child_process";
865
857
  import { createHash } from "node:crypto";
866
858
  import { join as join4 } from "node:path";
@@ -908,7 +900,6 @@ Usage:
908
900
  envsave passwd Change master password
909
901
  envsave config Show rate limit config
910
902
  envsave config --max-gets <n> Set max gets per minute (password required)
911
- envsave config --session-ttl <hours> Set session TTL in hours (password required)
912
903
  envsave list List clean key names (password required)
913
904
  envsave import <file> Import from .env file (password required)
914
905
  envsave export Export as .env format (password required)
@@ -1051,9 +1042,9 @@ Use 'envsave reveal <KEY>' to get the opaque key for each secret.`);
1051
1042
  process.exit(1);
1052
1043
  }
1053
1044
  const password = await requirePassword();
1054
- const opaque = setSecret(key, value, password);
1045
+ const { opaque, updated } = setSecret(key, value, password);
1055
1046
  console.log(`
1056
- ${key} → ${opaque}
1047
+ ${updated ? "Updated" : "Added"}: ${key} → ${opaque}
1057
1048
  `);
1058
1049
  console.log(` Use this in your code to access ${key}:`);
1059
1050
  console.log(` envsave.get("${opaque}")
@@ -1144,32 +1135,23 @@ Use 'envsave reveal <KEY>' to get the opaque key for each secret.`);
1144
1135
  }
1145
1136
  case "config": {
1146
1137
  const maxGets = getFlag("--max-gets");
1147
- const sessionTtl = getFlag("--session-ttl");
1148
- if (!maxGets && !sessionTtl) {
1138
+ if (!maxGets) {
1149
1139
  const config2 = getRateLimitConfig();
1150
1140
  console.log(`Rate limit config:`);
1151
1141
  console.log(` Max gets per minute: ${config2.maxGetsPerMinute}`);
1152
- console.log(` Session TTL: ${config2.sessionTtlHours} hours`);
1153
1142
  break;
1154
1143
  }
1155
1144
  const password = await requirePassword();
1156
- const options = {};
1157
- if (maxGets)
1158
- options.maxGetsPerMinute = parseInt(maxGets, 10);
1159
- if (sessionTtl)
1160
- options.sessionTtlHours = parseFloat(sessionTtl);
1145
+ const options = {
1146
+ maxGetsPerMinute: parseInt(maxGets, 10)
1147
+ };
1161
1148
  if (options.maxGetsPerMinute !== undefined && (isNaN(options.maxGetsPerMinute) || options.maxGetsPerMinute < 1)) {
1162
1149
  console.error("--max-gets must be a positive number.");
1163
1150
  process.exit(1);
1164
1151
  }
1165
- if (options.sessionTtlHours !== undefined && (isNaN(options.sessionTtlHours) || options.sessionTtlHours <= 0)) {
1166
- console.error("--session-ttl must be a positive number.");
1167
- process.exit(1);
1168
- }
1169
1152
  const config = configureRateLimit(password, options);
1170
1153
  console.log(`Config updated:`);
1171
1154
  console.log(` Max gets per minute: ${config.maxGetsPerMinute}`);
1172
- console.log(` Session TTL: ${config.sessionTtlHours} hours`);
1173
1155
  break;
1174
1156
  }
1175
1157
  case "passwd": {
@@ -1468,7 +1450,7 @@ Use 'envsave list' to verify your secrets.`);
1468
1450
  break;
1469
1451
  }
1470
1452
  if (cloudStatus.hasBackup && hasLocal && cloudStatus.checksum !== localChecksum) {
1471
- const localMtime = statSync3(vaultFile).mtimeMs;
1453
+ const localMtime = statSync2(vaultFile).mtimeMs;
1472
1454
  const cloudDate = cloudStatus.updatedAt ? new Date(cloudStatus.updatedAt).getTime() : 0;
1473
1455
  console.log("Local and cloud vaults differ:");
1474
1456
  console.log(` Local: ${localSize} bytes, modified ${new Date(localMtime).toISOString()}`);
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/vault.ts
2
- import { readFileSync, writeFileSync, existsSync, mkdirSync, chmodSync, appendFileSync, statSync } from "node:fs";
2
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, chmodSync, appendFileSync } from "node:fs";
3
3
  import { join as join2 } from "node:path";
4
4
  import { homedir as homedir2 } from "node:os";
5
5
 
@@ -40,7 +40,6 @@ function getRateFile() {
40
40
  function getAccessLog() {
41
41
  return join2(getEnvsaveDir(), "access.log");
42
42
  }
43
- var DEFAULT_SESSION_TTL_MS = 4 * 60 * 60 * 1000;
44
43
  var DEFAULT_RATE_LIMIT_WINDOW_MS = 60 * 1000;
45
44
  var DEFAULT_RATE_LIMIT_MAX = 30;
46
45
  function getConfigFile() {
@@ -51,12 +50,12 @@ function guardRealVault() {
51
50
  }
52
51
  function readConfig() {
53
52
  if (!existsSync(getConfigFile())) {
54
- return { maxGetsPerMinute: DEFAULT_RATE_LIMIT_MAX, sessionTtlHours: DEFAULT_SESSION_TTL_MS / 3600000 };
53
+ return { maxGetsPerMinute: DEFAULT_RATE_LIMIT_MAX };
55
54
  }
56
55
  try {
57
56
  return JSON.parse(readFileSync(getConfigFile(), "utf8"));
58
57
  } catch {
59
- return { maxGetsPerMinute: DEFAULT_RATE_LIMIT_MAX, sessionTtlHours: DEFAULT_SESSION_TTL_MS / 3600000 };
58
+ return { maxGetsPerMinute: DEFAULT_RATE_LIMIT_MAX };
60
59
  }
61
60
  }
62
61
  function ensureDir() {
@@ -69,12 +68,6 @@ function readSessionCache() {
69
68
  if (!existsSync(getSessionFile()) || !existsSync(getCacheFile()))
70
69
  return null;
71
70
  try {
72
- const sessionStat = statSync(getSessionFile());
73
- const age = Date.now() - sessionStat.mtimeMs;
74
- const config = readConfig();
75
- if (age > config.sessionTtlHours * 3600000) {
76
- return null;
77
- }
78
71
  const sessionKeyHex = readFileSync(getSessionFile(), "utf8").trim();
79
72
  const sessionKey = Buffer.from(sessionKeyHex, "hex");
80
73
  const payload = readFileSync(getCacheFile());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@envsave/cli",
3
- "version": "1.0.37",
3
+ "version": "1.0.39",
4
4
  "description": "Local secret vault for LLM-safe environments — encrypted secrets that agents can't bulk-discover",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",