@envsave/cli 1.0.51 → 1.0.53

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/README.md CHANGED
@@ -117,6 +117,13 @@ envsave set OPENAI_API_KEY sk-abc123
117
117
  # envsave.get("es_7a3f2b1e9c8d4f6b")
118
118
  ```
119
119
 
120
+ If the value contains characters your shell interprets — `!` (history expansion in zsh/bash), `$`, backticks, spaces — either single-quote it (`envsave set DB_PASS 'p@ss!word'`) or omit it and you'll be prompted with hidden input:
121
+
122
+ ```bash
123
+ envsave set DB_PASS
124
+ # Value for DB_PASS: ********
125
+ ```
126
+
120
127
  The opaque key (`es_...`) is an HMAC-SHA256 hash — no trace of the original key name. Re-running `envsave set` on an existing key updates the value in place; the opaque key stays the same, so you don't need to touch any code.
121
128
 
122
129
  ## Retrieving Secrets
package/dist/SKILL.md CHANGED
@@ -167,7 +167,7 @@ The command automatically:
167
167
 
168
168
  | Task | User Command |
169
169
  |------|-------------|
170
- | Add a secret | `envsave set <NAME> <VALUE>` |
170
+ | Add a secret | `envsave set <NAME> <VALUE>` (quote values with `!`/`$`; omit VALUE to be prompted) |
171
171
  | Get opaque key | `envsave reveal <NAME>` |
172
172
  | Rename a secret | `envsave rename <OLD_NAME> <NEW_NAME>` (creates new opaque key, deletes old — output prints OLD → NEW for code search-and-replace) |
173
173
  | Unlock vault | `envsave list` |
package/dist/cli.js CHANGED
@@ -1,19 +1,15 @@
1
1
  #!/usr/bin/env node
2
- var __defProp = Object.defineProperty;
3
- var __returnValue = (v) => v;
4
- function __exportSetter(name, newValue) {
5
- this[name] = __returnValue.bind(null, newValue);
6
- }
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, {
10
- get: all[name],
11
- enumerable: true,
12
- configurable: true,
13
- set: __exportSetter.bind(all, name)
14
- });
2
+ var __esm = (fn, res, err) => () => {
3
+ if (fn)
4
+ try {
5
+ res = fn(fn = 0);
6
+ } catch (e) {
7
+ err = [e];
8
+ }
9
+ if (err)
10
+ throw err[0];
11
+ return res;
15
12
  };
16
- var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
13
 
18
14
  // src/crypto.ts
19
15
  import { createCipheriv, createDecipheriv, createHmac, randomBytes, pbkdf2Sync } from "node:crypto";
@@ -92,29 +88,6 @@ function parseEnvFile(filePath) {
92
88
  var init_env_parser = () => {};
93
89
 
94
90
  // src/vault.ts
95
- var exports_vault = {};
96
- __export(exports_vault, {
97
- vaultExists: () => vaultExists,
98
- showSecret: () => showSecret,
99
- setSecret: () => setSecret,
100
- searchKeys: () => searchKeys,
101
- revealKeysByPattern: () => revealKeysByPattern,
102
- revealKey: () => revealKey,
103
- renameSecret: () => renameSecret,
104
- listKeys: () => listKeys,
105
- initVault: () => initVault,
106
- importEnv: () => importEnv,
107
- hasSecretWithSession: () => hasSecretWithSession,
108
- getSecretWithSession: () => getSecretWithSession,
109
- getSecretFromCache: () => getSecretFromCache,
110
- getSecret: () => getSecret,
111
- getRateLimitConfig: () => getRateLimitConfig,
112
- getDeploySecrets: () => getDeploySecrets,
113
- exportEnv: () => exportEnv,
114
- deleteSecret: () => deleteSecret,
115
- configureRateLimit: () => configureRateLimit,
116
- changePassword: () => changePassword
117
- });
118
91
  import { readFileSync as readFileSync2, writeFileSync, existsSync, mkdirSync, chmodSync, appendFileSync } from "node:fs";
119
92
  import { join } from "node:path";
120
93
  import { homedir } from "node:os";
@@ -527,19 +500,6 @@ function getDeploySecrets(keys, password) {
527
500
  }
528
501
  return results;
529
502
  }
530
- function getSecretWithSession(opaqueKey) {
531
- return getSecretFromCache(opaqueKey);
532
- }
533
- function hasSecretWithSession(opaqueKey) {
534
- try {
535
- const cache = readSessionCache();
536
- if (!cache)
537
- return false;
538
- return opaqueKey in cache.secrets;
539
- } catch {
540
- return false;
541
- }
542
- }
543
503
  var DEFAULT_RATE_LIMIT_WINDOW_MS, DEFAULT_RATE_LIMIT_MAX = 30, BASE_CHARS_V3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@$&*=-!", BASE_CHARS_V4 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", BASE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
544
504
  var init_vault = __esm(() => {
545
505
  init_crypto();
@@ -548,10 +508,6 @@ var init_vault = __esm(() => {
548
508
  });
549
509
 
550
510
  // src/scanner.ts
551
- var exports_scanner = {};
552
- __export(exports_scanner, {
553
- scanProject: () => scanProject
554
- });
555
511
  import { readdirSync, readFileSync as readFileSync4, statSync } from "node:fs";
556
512
  import { join as join3, relative } from "node:path";
557
513
  function walkDir(dir, base, results) {
@@ -860,7 +816,7 @@ async function checkLicense() {
860
816
  // package.json
861
817
  var package_default = {
862
818
  name: "@envsave/cli",
863
- version: "1.0.51",
819
+ version: "1.0.53",
864
820
  description: "Local secret vault for LLM-safe environments — encrypted secrets that agents can't bulk-discover",
865
821
  type: "module",
866
822
  main: "dist/index.js",
@@ -873,8 +829,8 @@ var package_default = {
873
829
  "python"
874
830
  ],
875
831
  scripts: {
876
- prebuild: `bun -e "const p=require('./package.json');const v=p.version.split('.');v[2]=+v[2]+1;p.version=v.join('.');require('fs').writeFileSync('package.json',JSON.stringify(p,null,2)+'\\n')"`,
877
832
  build: "bun build src/cli.ts --outdir dist --target node --format esm && bun build src/index.ts --outdir dist --target node --format esm --outfile dist/index.js && cp .claude/skills/envsave/SKILL.md dist/SKILL.md",
833
+ release: "bun run scripts/release.ts",
878
834
  dev: "bun run src/cli.ts",
879
835
  webapp: "bun run webapp/server.ts",
880
836
  "lint:claude": "claude -p 'you are a linter. please look at the changes vs. main and report any issues related to typos. report the filename and line number on one line, and a description of the issue on the second line. do not return any other text.'"
@@ -944,7 +900,7 @@ EnvSave — Local Secret Vault for LLM-Safe Environments
944
900
  Usage:
945
901
  envsave activate <token> [--offline] Activate license (--offline skips server)
946
902
  envsave init [--env <f>] [--ssh-key <f>] Initialize vault
947
- envsave set <key> <value> Add/update a secret (outputs opaque key)
903
+ envsave set <key> [value] Add/update a secret (omit value to be prompted — safe for !, $, spaces)
948
904
  envsave get <opaque_key> Retrieve a value (no password needed)
949
905
  envsave show <key> Show real value by key name (password required)
950
906
  envsave reveal <keyword> [keyword2]... Show opaque key(s) for any name matching keyword (password required)
@@ -991,24 +947,26 @@ async function promptPassword(prompt = "Master password: ") {
991
947
  stdin.setRawMode(true);
992
948
  }
993
949
  let password = "";
994
- const onData = (ch) => {
995
- const c = ch.toString();
996
- if (c === `
950
+ const onData = (chunk) => {
951
+ for (const c of chunk.toString()) {
952
+ if (c === `
997
953
  ` || c === "\r" || c === "\x04") {
998
- if (stdin.isTTY && stdin.setRawMode) {
999
- stdin.setRawMode(wasRaw ?? false);
1000
- }
1001
- stdin.removeListener("data", onData);
1002
- process.stderr.write(`
954
+ if (stdin.isTTY && stdin.setRawMode) {
955
+ stdin.setRawMode(wasRaw ?? false);
956
+ }
957
+ stdin.removeListener("data", onData);
958
+ process.stderr.write(`
1003
959
  `);
1004
- rl.close();
1005
- resolve(password);
1006
- } else if (c === "\x03") {
1007
- process.exit(1);
1008
- } else if (c === "" || c === "\b") {
1009
- password = password.slice(0, -1);
1010
- } else {
1011
- password += c;
960
+ rl.close();
961
+ resolve(password);
962
+ return;
963
+ } else if (c === "\x03") {
964
+ process.exit(1);
965
+ } else if (c === "" || c === "\b") {
966
+ password = password.slice(0, -1);
967
+ } else {
968
+ password += c;
969
+ }
1012
970
  }
1013
971
  };
1014
972
  stdin.on("data", onData);
@@ -1098,11 +1056,18 @@ Use 'envsave reveal <KEY>' to get the opaque key for each secret.`);
1098
1056
  }
1099
1057
  case "set": {
1100
1058
  const key = args[1];
1101
- const value = args[2];
1102
- if (!key || value === undefined) {
1103
- console.error("Usage: envsave set <key> <value>");
1059
+ let value = args[2];
1060
+ if (!key) {
1061
+ console.error("Usage: envsave set <key> [value]");
1104
1062
  process.exit(1);
1105
1063
  }
1064
+ if (value === undefined) {
1065
+ value = await promptPassword(`Value for ${key}: `);
1066
+ if (!value) {
1067
+ console.error("Error: value cannot be empty.");
1068
+ process.exit(1);
1069
+ }
1070
+ }
1106
1071
  const password = await requirePassword();
1107
1072
  const { opaque, updated } = setSecret(key, value, password);
1108
1073
  console.log(`
@@ -1224,9 +1189,9 @@ Use 'envsave reveal <KEY>' to get the opaque key for each secret.`);
1224
1189
  case "config": {
1225
1190
  const maxGets = getFlag("--max-gets");
1226
1191
  if (!maxGets) {
1227
- const config2 = getRateLimitConfig();
1192
+ const config = getRateLimitConfig();
1228
1193
  console.log(`Rate limit config:`);
1229
- console.log(` Max gets per minute: ${config2.maxGetsPerMinute}`);
1194
+ console.log(` Max gets per minute: ${config.maxGetsPerMinute}`);
1230
1195
  break;
1231
1196
  }
1232
1197
  const password = await requirePassword();
@@ -1264,8 +1229,8 @@ Use 'envsave reveal <KEY>' to get the opaque key for each secret.`);
1264
1229
  console.error("Passwords do not match.");
1265
1230
  process.exit(1);
1266
1231
  }
1267
- const { changePassword: changePassword2 } = await Promise.resolve().then(() => (init_vault(), exports_vault));
1268
- changePassword2(oldPassword, newPassword);
1232
+ await Promise.resolve().then(() => init_vault());
1233
+ changePassword(oldPassword, newPassword);
1269
1234
  console.log("Password changed successfully!");
1270
1235
  break;
1271
1236
  }
@@ -1296,8 +1261,8 @@ ${matches.length} key(s) matching "${keyword}".`);
1296
1261
  }
1297
1262
  case "scan": {
1298
1263
  const scanDir = args.slice(1).find((a) => !a.startsWith("--")) || process.cwd();
1299
- const { scanProject: scanProject2 } = await Promise.resolve().then(() => (init_scanner(), exports_scanner));
1300
- const results = scanProject2(scanDir);
1264
+ await Promise.resolve().then(() => init_scanner());
1265
+ const results = scanProject(scanDir);
1301
1266
  if (results.length === 0) {
1302
1267
  console.log("No env variable usage found.");
1303
1268
  } else {
@@ -1585,16 +1550,12 @@ Use 'envsave list' to verify your secrets.`);
1585
1550
  case "vercel": {
1586
1551
  const vercelDir = join4(process.cwd(), ".vercel");
1587
1552
  const vercelProjectJson = join4(vercelDir, "project.json");
1588
- if (!existsSync3(vercelProjectJson)) {
1553
+ const vercelRepoJson = join4(vercelDir, "repo.json");
1554
+ if (!existsSync3(vercelProjectJson) && !existsSync3(vercelRepoJson)) {
1589
1555
  console.error("This project is not linked to Vercel.");
1590
1556
  console.error("Run 'vercel link' first to connect this directory to a Vercel project.");
1591
1557
  process.exit(1);
1592
1558
  }
1593
- let projectName = "unknown";
1594
- try {
1595
- const proj = JSON.parse(readFileSync5(vercelProjectJson, "utf8"));
1596
- projectName = proj.projectId || "unknown";
1597
- } catch {}
1598
1559
  const envArg = getFlag("--env");
1599
1560
  const environments = envArg ? envArg.split(",") : ["production", "preview", "development"];
1600
1561
  const keyArgs = args.slice(1).filter((a) => !a.startsWith("--"));
package/dist/index.js CHANGED
@@ -138,7 +138,7 @@ function has(key) {
138
138
  }
139
139
  var src_default = { get, has };
140
140
  export {
141
- has,
141
+ src_default as default,
142
142
  get,
143
- src_default as default
143
+ has
144
144
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@envsave/cli",
3
- "version": "1.0.51",
3
+ "version": "1.0.53",
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",
@@ -13,8 +13,8 @@
13
13
  "python"
14
14
  ],
15
15
  "scripts": {
16
- "prebuild": "bun -e \"const p=require('./package.json');const v=p.version.split('.');v[2]=+v[2]+1;p.version=v.join('.');require('fs').writeFileSync('package.json',JSON.stringify(p,null,2)+'\\n')\"",
17
16
  "build": "bun build src/cli.ts --outdir dist --target node --format esm && bun build src/index.ts --outdir dist --target node --format esm --outfile dist/index.js && cp .claude/skills/envsave/SKILL.md dist/SKILL.md",
17
+ "release": "bun run scripts/release.ts",
18
18
  "dev": "bun run src/cli.ts",
19
19
  "webapp": "bun run webapp/server.ts",
20
20
  "lint:claude": "claude -p 'you are a linter. please look at the changes vs. main and report any issues related to typos. report the filename and line number on one line, and a description of the issue on the second line. do not return any other text.'"