envsec 0.1.4 → 0.1.5

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
@@ -1,4 +1,4 @@
1
- # envsec
1
+ # secenv
2
2
 
3
3
  Secure environment secrets management for macOS using the native Keychain.
4
4
 
@@ -8,6 +8,8 @@ Secure environment secrets management for macOS using the native Keychain.
8
8
  - Organize secrets by environment (dev, staging, prod, etc.)
9
9
  - Track secret types (string, number, boolean) and metadata via SQLite
10
10
  - Search secrets with glob patterns
11
+ - Run commands with secret interpolation
12
+ - Export secrets to `.env` files
11
13
 
12
14
  ## Requirements
13
15
 
@@ -17,11 +19,11 @@ Secure environment secrets management for macOS using the native Keychain.
17
19
  ## Installation
18
20
 
19
21
  ```bash
20
- npm install -g envsec
22
+ npm install -g secenv
21
23
  ```
22
24
 
23
25
  ```bash
24
- npx envsec
26
+ npx secenv
25
27
  ```
26
28
 
27
29
  ## Usage
@@ -59,6 +61,28 @@ secenv -e dev list
59
61
  secenv -e dev search "api.*"
60
62
  ```
61
63
 
64
+ ### Generate a .env file
65
+
66
+ ```bash
67
+ # Creates .env with all secrets from the environment
68
+ secenv -e dev env-file
69
+
70
+ # Specify a custom output path
71
+ secenv -e dev env-file --output .env.local
72
+ ```
73
+
74
+ Keys are converted to `UPPER_SNAKE_CASE` (e.g. `api.token` → `API_TOKEN`).
75
+
76
+ ### Run a command with secrets
77
+
78
+ ```bash
79
+ # Placeholders {key} are resolved with secret values before execution
80
+ secenv -e dev run 'curl {api.url} -H "Authorization: Bearer {api.token}"'
81
+
82
+ # Any {dotted.key} in the command string is replaced with its value
83
+ secenv -e prod run 'psql {db.connection_string}'
84
+ ```
85
+
62
86
  ### Delete a secret
63
87
 
64
88
  ```bash
@@ -0,0 +1,22 @@
1
+ import { Command, Options } from "@effect/cli";
2
+ import { Effect } from "effect";
3
+ import { SecretStore } from "../services/SecretStore.js";
4
+ import { rootCommand } from "./root.js";
5
+ import { writeFileSync } from "node:fs";
6
+ const output = Options.text("output").pipe(Options.withAlias("o"), Options.withDescription("Output file path (default: .env)"), Options.withDefault(".env"));
7
+ export const envFileCommand = Command.make("env-file", { output }, ({ output }) => Effect.gen(function* () {
8
+ const { env } = yield* rootCommand;
9
+ const secrets = yield* SecretStore.list(env);
10
+ if (secrets.length === 0) {
11
+ yield* Effect.log(`No secrets found for env "${env}"`);
12
+ return;
13
+ }
14
+ const lines = [];
15
+ for (const item of secrets) {
16
+ const value = yield* SecretStore.get(env, item.key);
17
+ const envKey = item.key.toUpperCase().replaceAll(".", "_");
18
+ lines.push(`${envKey}=${String(value)}`);
19
+ }
20
+ writeFileSync(output, lines.join("\n") + "\n", "utf-8");
21
+ yield* Effect.log(`Written ${secrets.length} secret(s) to ${output}`);
22
+ }));
@@ -0,0 +1,26 @@
1
+ import { Command, Args } from "@effect/cli";
2
+ import { Effect } from "effect";
3
+ import { SecretStore } from "../services/SecretStore.js";
4
+ import { rootCommand } from "./root.js";
5
+ import { execSync } from "node:child_process";
6
+ const cmd = Args.text({ name: "command" }).pipe(Args.withDescription("Command to execute. Use {key} placeholders for secret interpolation"));
7
+ export const runCommand = Command.make("run", { cmd }, ({ cmd }) => Effect.gen(function* () {
8
+ const { env } = yield* rootCommand;
9
+ // Find all {key} placeholders
10
+ const placeholders = [...cmd.matchAll(/\{([^}]+)\}/g)];
11
+ let resolved = cmd;
12
+ for (const match of placeholders) {
13
+ const key = match[1];
14
+ const value = yield* SecretStore.get(env, key);
15
+ resolved = resolved.replaceAll(`{${key}}`, String(value));
16
+ }
17
+ if (placeholders.length > 0) {
18
+ yield* Effect.log(`Resolved ${placeholders.length} secret(s)`);
19
+ }
20
+ try {
21
+ execSync(resolved, { stdio: "inherit", shell: "/bin/bash" });
22
+ }
23
+ catch (e) {
24
+ yield* Effect.fail(new Error(`Command exited with code ${e.status ?? 1}`));
25
+ }
26
+ }));
package/dist/main.js CHANGED
@@ -9,10 +9,12 @@ import { getCommand } from "./cli/read.js";
9
9
  import { deleteCommand, delCommand } from "./cli/delete.js";
10
10
  import { searchCommand } from "./cli/search.js";
11
11
  import { listCommand } from "./cli/list.js";
12
+ import { runCommand } from "./cli/run.js";
13
+ import { envFileCommand } from "./cli/env-file.js";
12
14
  import { SecretStore } from "./services/SecretStore.js";
13
15
  const require = createRequire(import.meta.url);
14
16
  const pkg = require("../package.json");
15
- const command = rootCommand.pipe(Command.withSubcommands([addCommand, getCommand, deleteCommand, delCommand, searchCommand, listCommand]));
17
+ const command = rootCommand.pipe(Command.withSubcommands([addCommand, getCommand, deleteCommand, delCommand, searchCommand, listCommand, runCommand, envFileCommand]));
16
18
  const cli = Command.run(command, {
17
19
  name: "secenv",
18
20
  version: pkg.version,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envsec",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Secure environment secrets management using macOS Keychain",
5
5
  "type": "module",
6
6
  "bin": {