vaulter 0.2.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 (145) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +490 -0
  3. package/dist/bin/minienv-mcp.cjs +17 -0
  4. package/dist/bin/minienv.cjs +146 -0
  5. package/dist/cli/commands/delete.d.ts +22 -0
  6. package/dist/cli/commands/delete.d.ts.map +1 -0
  7. package/dist/cli/commands/delete.js +85 -0
  8. package/dist/cli/commands/delete.js.map +1 -0
  9. package/dist/cli/commands/export.d.ts +21 -0
  10. package/dist/cli/commands/export.d.ts.map +1 -0
  11. package/dist/cli/commands/export.js +126 -0
  12. package/dist/cli/commands/export.js.map +1 -0
  13. package/dist/cli/commands/get.d.ts +21 -0
  14. package/dist/cli/commands/get.d.ts.map +1 -0
  15. package/dist/cli/commands/get.js +62 -0
  16. package/dist/cli/commands/get.js.map +1 -0
  17. package/dist/cli/commands/init.d.ts +20 -0
  18. package/dist/cli/commands/init.d.ts.map +1 -0
  19. package/dist/cli/commands/init.js +98 -0
  20. package/dist/cli/commands/init.js.map +1 -0
  21. package/dist/cli/commands/integrations/helm.d.ts +21 -0
  22. package/dist/cli/commands/integrations/helm.d.ts.map +1 -0
  23. package/dist/cli/commands/integrations/helm.js +113 -0
  24. package/dist/cli/commands/integrations/helm.js.map +1 -0
  25. package/dist/cli/commands/integrations/kubernetes.d.ts +25 -0
  26. package/dist/cli/commands/integrations/kubernetes.d.ts.map +1 -0
  27. package/dist/cli/commands/integrations/kubernetes.js +199 -0
  28. package/dist/cli/commands/integrations/kubernetes.js.map +1 -0
  29. package/dist/cli/commands/integrations/terraform.d.ts +25 -0
  30. package/dist/cli/commands/integrations/terraform.d.ts.map +1 -0
  31. package/dist/cli/commands/integrations/terraform.js +131 -0
  32. package/dist/cli/commands/integrations/terraform.js.map +1 -0
  33. package/dist/cli/commands/key.d.ts +19 -0
  34. package/dist/cli/commands/key.d.ts.map +1 -0
  35. package/dist/cli/commands/key.js +247 -0
  36. package/dist/cli/commands/key.js.map +1 -0
  37. package/dist/cli/commands/list.d.ts +21 -0
  38. package/dist/cli/commands/list.d.ts.map +1 -0
  39. package/dist/cli/commands/list.js +94 -0
  40. package/dist/cli/commands/list.js.map +1 -0
  41. package/dist/cli/commands/pull.d.ts +22 -0
  42. package/dist/cli/commands/pull.d.ts.map +1 -0
  43. package/dist/cli/commands/pull.js +142 -0
  44. package/dist/cli/commands/pull.js.map +1 -0
  45. package/dist/cli/commands/push.d.ts +22 -0
  46. package/dist/cli/commands/push.d.ts.map +1 -0
  47. package/dist/cli/commands/push.js +181 -0
  48. package/dist/cli/commands/push.js.map +1 -0
  49. package/dist/cli/commands/services.d.ts +18 -0
  50. package/dist/cli/commands/services.d.ts.map +1 -0
  51. package/dist/cli/commands/services.js +92 -0
  52. package/dist/cli/commands/services.js.map +1 -0
  53. package/dist/cli/commands/set.d.ts +22 -0
  54. package/dist/cli/commands/set.d.ts.map +1 -0
  55. package/dist/cli/commands/set.js +93 -0
  56. package/dist/cli/commands/set.js.map +1 -0
  57. package/dist/cli/commands/sync.d.ts +23 -0
  58. package/dist/cli/commands/sync.d.ts.map +1 -0
  59. package/dist/cli/commands/sync.js +362 -0
  60. package/dist/cli/commands/sync.js.map +1 -0
  61. package/dist/cli/index.d.ts +7 -0
  62. package/dist/cli/index.d.ts.map +1 -0
  63. package/dist/cli/index.js +266 -0
  64. package/dist/cli/index.js.map +1 -0
  65. package/dist/cli/lib/create-client.d.ts +21 -0
  66. package/dist/cli/lib/create-client.d.ts.map +1 -0
  67. package/dist/cli/lib/create-client.js +68 -0
  68. package/dist/cli/lib/create-client.js.map +1 -0
  69. package/dist/cli/lib/hooks.d.ts +5 -0
  70. package/dist/cli/lib/hooks.d.ts.map +1 -0
  71. package/dist/cli/lib/hooks.js +17 -0
  72. package/dist/cli/lib/hooks.js.map +1 -0
  73. package/dist/cli/preload.d.ts +6 -0
  74. package/dist/cli/preload.d.ts.map +1 -0
  75. package/dist/cli/preload.js +7 -0
  76. package/dist/cli/preload.js.map +1 -0
  77. package/dist/client.d.ts +89 -0
  78. package/dist/client.d.ts.map +1 -0
  79. package/dist/client.js +350 -0
  80. package/dist/client.js.map +1 -0
  81. package/dist/index.d.ts +20 -0
  82. package/dist/index.d.ts.map +1 -0
  83. package/dist/index.js +33 -0
  84. package/dist/index.js.map +1 -0
  85. package/dist/lib/batch-runner.d.ts +39 -0
  86. package/dist/lib/batch-runner.d.ts.map +1 -0
  87. package/dist/lib/batch-runner.js +117 -0
  88. package/dist/lib/batch-runner.js.map +1 -0
  89. package/dist/lib/config-loader.d.ts +45 -0
  90. package/dist/lib/config-loader.d.ts.map +1 -0
  91. package/dist/lib/config-loader.js +357 -0
  92. package/dist/lib/config-loader.js.map +1 -0
  93. package/dist/lib/env-parser.d.ts +38 -0
  94. package/dist/lib/env-parser.d.ts.map +1 -0
  95. package/dist/lib/env-parser.js +281 -0
  96. package/dist/lib/env-parser.js.map +1 -0
  97. package/dist/lib/monorepo.d.ts +39 -0
  98. package/dist/lib/monorepo.d.ts.map +1 -0
  99. package/dist/lib/monorepo.js +181 -0
  100. package/dist/lib/monorepo.js.map +1 -0
  101. package/dist/lib/pattern-matcher.d.ts +5 -0
  102. package/dist/lib/pattern-matcher.d.ts.map +1 -0
  103. package/dist/lib/pattern-matcher.js +18 -0
  104. package/dist/lib/pattern-matcher.js.map +1 -0
  105. package/dist/lib/s3-key-loader.d.ts +33 -0
  106. package/dist/lib/s3-key-loader.d.ts.map +1 -0
  107. package/dist/lib/s3-key-loader.js +83 -0
  108. package/dist/lib/s3-key-loader.js.map +1 -0
  109. package/dist/lib/secret-patterns.d.ts +10 -0
  110. package/dist/lib/secret-patterns.d.ts.map +1 -0
  111. package/dist/lib/secret-patterns.js +27 -0
  112. package/dist/lib/secret-patterns.js.map +1 -0
  113. package/dist/load.d.ts +14 -0
  114. package/dist/load.d.ts.map +1 -0
  115. package/dist/load.js +15 -0
  116. package/dist/load.js.map +1 -0
  117. package/dist/loader.d.ts +63 -0
  118. package/dist/loader.d.ts.map +1 -0
  119. package/dist/loader.js +50 -0
  120. package/dist/loader.js.map +1 -0
  121. package/dist/mcp/index.d.ts +8 -0
  122. package/dist/mcp/index.d.ts.map +1 -0
  123. package/dist/mcp/index.js +14 -0
  124. package/dist/mcp/index.js.map +1 -0
  125. package/dist/mcp/preload.d.ts +6 -0
  126. package/dist/mcp/preload.d.ts.map +1 -0
  127. package/dist/mcp/preload.js +7 -0
  128. package/dist/mcp/preload.js.map +1 -0
  129. package/dist/mcp/resources.d.ts +23 -0
  130. package/dist/mcp/resources.d.ts.map +1 -0
  131. package/dist/mcp/resources.js +112 -0
  132. package/dist/mcp/resources.js.map +1 -0
  133. package/dist/mcp/server.d.ts +16 -0
  134. package/dist/mcp/server.d.ts.map +1 -0
  135. package/dist/mcp/server.js +81 -0
  136. package/dist/mcp/server.js.map +1 -0
  137. package/dist/mcp/tools.d.ts +20 -0
  138. package/dist/mcp/tools.d.ts.map +1 -0
  139. package/dist/mcp/tools.js +405 -0
  140. package/dist/mcp/tools.js.map +1 -0
  141. package/dist/types.d.ts +182 -0
  142. package/dist/types.d.ts.map +1 -0
  143. package/dist/types.js +37 -0
  144. package/dist/types.js.map +1 -0
  145. package/package.json +114 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Forattini
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,490 @@
1
+ <div align="center">
2
+
3
+ # 🔐 vaulter
4
+
5
+ ### Multi-Backend Environment & Secrets Manager
6
+
7
+ **One CLI to manage all your environment variables.**
8
+
9
+ </div>
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ npm install -g vaulter
15
+ ```
16
+
17
+ ```bash
18
+ # Initialize project
19
+ vaulter init
20
+
21
+ # Set some secrets
22
+ vaulter set DATABASE_URL "postgres://localhost/mydb" -e dev
23
+ vaulter set API_KEY "sk-secret-key" -e dev
24
+
25
+ # Export to shell
26
+ eval $(vaulter export -e dev)
27
+
28
+ # Deploy to Kubernetes
29
+ vaulter k8s:secret -e prd | kubectl apply -f -
30
+ ```
31
+
32
+ ---
33
+
34
+ <div align="center">
35
+
36
+ [![npm version](https://img.shields.io/npm/v/vaulter.svg?style=flat-square&color=F5A623)](https://www.npmjs.com/package/vaulter)
37
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-3178C6?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
38
+ [![Node.js](https://img.shields.io/badge/Node.js-22+-339933?style=flat-square&logo=node.js&logoColor=white)](https://nodejs.org/)
39
+ [![License](https://img.shields.io/npm/l/vaulter.svg?style=flat-square&color=007AFF)](https://github.com/forattini-dev/vaulter/blob/main/LICENSE)
40
+
41
+ Store secrets anywhere: AWS S3, MinIO, R2, Spaces, B2, or local filesystem.
42
+ <br>
43
+ AES-256-GCM encryption. Native K8s, Helm & Terraform integration.
44
+ <br>
45
+ MCP server for Claude AI. Zero config for dev, production-ready.
46
+
47
+ [📖 Documentation](#configuration) · [🔧 CLI](#commands) · [🚀 Highlights](#highlights)
48
+
49
+ </div>
50
+
51
+ ---
52
+
53
+ ## What's Inside
54
+
55
+ | Category | Features |
56
+ |:---------|:---------|
57
+ | **Backends** | AWS S3, MinIO, Cloudflare R2, DigitalOcean Spaces, Backblaze B2, FileSystem, Memory |
58
+ | **Encryption** | AES-256-GCM via s3db.js, field-level encryption, key rotation |
59
+ | **Environments** | dev, stg, prd, sbx, dr (fully customizable) |
60
+ | **Integrations** | Kubernetes Secret/ConfigMap, Helm values.yaml, Terraform tfvars |
61
+ | **Monorepo** | Service discovery, batch operations, config inheritance |
62
+ | **MCP Server** | Claude AI integration via Model Context Protocol |
63
+ | **Unix Pipes** | Full stdin/stdout support for scripting |
64
+ | **Dotenv** | Drop-in compatible: `import 'vaulter/load'` |
65
+
66
+ ## Highlights
67
+
68
+ ### Multi-Backend with Fallback
69
+
70
+ Configure multiple backends - vaulter tries each until one succeeds:
71
+
72
+ ```yaml
73
+ backend:
74
+ urls:
75
+ - s3://bucket/envs?region=us-east-1 # Primary (CI/CD)
76
+ - file:///home/user/.vaulter-store # Fallback (local dev)
77
+ ```
78
+
79
+ ### Native Integrations
80
+
81
+ ```bash
82
+ # Kubernetes - deploy secrets directly
83
+ vaulter k8s:secret -e prd | kubectl apply -f -
84
+
85
+ # Helm - generate values file
86
+ vaulter helm:values -e prd | helm upgrade myapp ./chart -f -
87
+
88
+ # Terraform - export as tfvars
89
+ vaulter tf:vars -e prd > terraform.tfvars
90
+ ```
91
+
92
+ ### Unix Pipes
93
+
94
+ ```bash
95
+ # Import from Vault
96
+ vault kv get -format=json secret/app | \
97
+ jq -r '.data.data | to_entries | .[] | "\(.key)=\(.value)"' | \
98
+ vaulter sync -e prd
99
+
100
+ # Export to kubectl
101
+ vaulter export -e prd --format=env | \
102
+ kubectl create secret generic myapp --from-env-file=/dev/stdin
103
+ ```
104
+
105
+ ### MCP Server for Claude
106
+
107
+ ```bash
108
+ # Start MCP server
109
+ vaulter mcp
110
+ ```
111
+
112
+ ```json
113
+ {
114
+ "mcpServers": {
115
+ "vaulter": {
116
+ "command": "npx",
117
+ "args": ["vaulter", "mcp"]
118
+ }
119
+ }
120
+ }
121
+ ```
122
+
123
+ ### Dotenv Compatible
124
+
125
+ Drop-in replacement for dotenv - works with your existing setup:
126
+
127
+ ```typescript
128
+ // Auto-load .env into process.env
129
+ import 'vaulter/load'
130
+
131
+ // Or programmatically with options
132
+ import { loader } from 'vaulter'
133
+ loader({ path: '.env.local', override: true })
134
+ ```
135
+
136
+ ## Commands
137
+
138
+ ### Core
139
+
140
+ | Command | Description | Example |
141
+ |:--------|:------------|:--------|
142
+ | `init` | Initialize project | `vaulter init` |
143
+ | `get <key>` | Get a variable | `vaulter get DATABASE_URL -e prd` |
144
+ | `set <key> <value>` | Set a variable | `vaulter set API_KEY "sk-..." -e prd` |
145
+ | `delete <key>` | Delete a variable | `vaulter delete OLD_KEY -e dev` |
146
+ | `list` | List all variables | `vaulter list -e prd` |
147
+ | `export` | Export for shell | `eval $(vaulter export -e dev)` |
148
+
149
+ ### Sync
150
+
151
+ | Command | Description | Example |
152
+ |:--------|:------------|:--------|
153
+ | `sync` | Merge local .env and backend | `vaulter sync -f .env.local -e dev` |
154
+ | `pull` | Download to .env | `vaulter pull -e prd -o .env.prd` |
155
+ | `push` | Upload from .env | `vaulter push -f .env.local -e dev` |
156
+
157
+ ### Integrations
158
+
159
+ | Command | Description | Example |
160
+ |:--------|:------------|:--------|
161
+ | `k8s:secret` | Kubernetes Secret | `vaulter k8s:secret -e prd \| kubectl apply -f -` |
162
+ | `k8s:configmap` | Kubernetes ConfigMap | `vaulter k8s:configmap -e prd` |
163
+ | `helm:values` | Helm values.yaml | `vaulter helm:values -e prd` |
164
+ | `tf:vars` | Terraform .tfvars | `vaulter tf:vars -e prd > terraform.tfvars` |
165
+ | `tf:json` | Terraform JSON | `vaulter tf:json -e prd` |
166
+
167
+ ### Utilities
168
+
169
+ | Command | Description | Example |
170
+ |:--------|:------------|:--------|
171
+ | `key generate` | Generate encryption key | `vaulter key generate` |
172
+ | `services` | List monorepo services | `vaulter services` |
173
+ | `mcp` | Start MCP server | `vaulter mcp` |
174
+
175
+ ## Global Options
176
+
177
+ ```
178
+ -p, --project <name> Project name
179
+ -s, --service <name> Service name (monorepos)
180
+ -e, --env <env> Environment: dev, stg, prd, sbx, dr
181
+ -b, --backend <url> Backend URL override
182
+ -k, --key <path|value> Encryption key file path or raw key
183
+ -v, --verbose Verbose output
184
+ --all All services (monorepo batch)
185
+ --dry-run Preview without applying
186
+ --json JSON output
187
+ --force Skip confirmations
188
+ ```
189
+
190
+ ## Configuration
191
+
192
+ ### Basic Config
193
+
194
+ ```yaml
195
+ # .vaulter/config.yaml
196
+ version: "1"
197
+
198
+ project: my-project
199
+ service: api # optional
200
+
201
+ backend:
202
+ # Single URL
203
+ url: s3://bucket/envs?region=us-east-1
204
+
205
+ # Or multiple with fallback
206
+ urls:
207
+ - s3://bucket/envs?region=us-east-1
208
+ - file:///home/user/.vaulter-store
209
+
210
+ encryption:
211
+ key_source:
212
+ - env: VAULTER_KEY # 1. Environment variable
213
+ - file: .vaulter/.key # 2. Local file
214
+ - s3: s3://keys/vaulter.key # 3. Remote S3
215
+
216
+ environments:
217
+ - dev
218
+ - stg
219
+ - prd
220
+
221
+ default_environment: dev
222
+ ```
223
+
224
+ ### Sync Settings
225
+
226
+ Sync merges local and remote variables. Conflicts are resolved by `sync.conflict`.
227
+
228
+ ```yaml
229
+ sync:
230
+ conflict: local # local | remote | prompt | error
231
+ ignore:
232
+ - "PUBLIC_*"
233
+ required:
234
+ dev:
235
+ - DATABASE_URL
236
+ ```
237
+
238
+ Notes:
239
+ - `prompt` and `error` will stop the sync if conflicts are detected.
240
+ - When reading from stdin, sync only updates the backend (local file is not changed).
241
+
242
+ ### Hooks
243
+
244
+ ```yaml
245
+ hooks:
246
+ pre_sync: "echo pre sync"
247
+ post_sync: "echo post sync"
248
+ pre_pull: "echo pre pull"
249
+ post_pull: "echo post pull"
250
+ ```
251
+
252
+ ### Environment Variable Expansion
253
+
254
+ Config values support `${VAR}`, `${VAR:-default}`, and `$VAR`:
255
+
256
+ ```yaml
257
+ backend:
258
+ url: s3://${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}@bucket/envs
259
+ # Or
260
+ url: ${VAULTER_BACKEND_URL}
261
+ ```
262
+
263
+ ### Local Override (config.local.yaml)
264
+
265
+ For credentials that should **never** be committed:
266
+
267
+ ```yaml
268
+ # .vaulter/config.local.yaml (gitignored)
269
+ backend:
270
+ url: s3://real-key:real-secret@bucket/envs?region=us-east-1
271
+ ```
272
+
273
+ ## Backend URLs
274
+
275
+ | Provider | URL Format |
276
+ |:---------|:-----------|
277
+ | **AWS S3** | `s3://bucket/path?region=us-east-1` |
278
+ | **AWS S3 + Profile** | `s3://bucket/path?region=us-east-1&profile=myprofile` |
279
+ | **AWS S3 + Credentials** | `s3://${KEY}:${SECRET}@bucket/path` |
280
+ | **MinIO** | `http://${KEY}:${SECRET}@localhost:9000/bucket` |
281
+ | **Cloudflare R2** | `https://${KEY}:${SECRET}@${ACCOUNT}.r2.cloudflarestorage.com/bucket` |
282
+ | **DigitalOcean Spaces** | `https://${KEY}:${SECRET}@nyc3.digitaloceanspaces.com/bucket` |
283
+ | **Backblaze B2** | `https://${KEY}:${SECRET}@s3.us-west-002.backblazeb2.com/bucket` |
284
+ | **FileSystem** | `file:///path/to/storage` |
285
+ | **Memory** | `memory://bucket-name` |
286
+
287
+ ## Encryption
288
+
289
+ All secrets are encrypted with **AES-256-GCM** before storage.
290
+
291
+ ### Key Sources
292
+
293
+ ```bash
294
+ # 1. Environment variable (CI/CD)
295
+ export VAULTER_KEY="base64-encoded-32-byte-key"
296
+ vaulter export -e prd
297
+
298
+ # 2. Local file (development)
299
+ vaulter key generate -o .vaulter/.key
300
+
301
+ # 3. Remote S3 (production)
302
+ # Configured in config.yaml
303
+ ```
304
+
305
+ You can also pass a key directly:
306
+
307
+ ```bash
308
+ vaulter list -e prd --key .vaulter/.key
309
+ ```
310
+
311
+ ### Security Settings
312
+
313
+ ```yaml
314
+ security:
315
+ paranoid: true # Fail if no encryption key is found
316
+ auto_encrypt:
317
+ patterns:
318
+ - "*_KEY"
319
+ - "*_SECRET"
320
+ - "DATABASE_URL"
321
+ ```
322
+
323
+ `auto_encrypt.patterns` is used to classify secrets for integrations (K8s/Helm).
324
+
325
+ ## Monorepo Support
326
+
327
+ ```
328
+ my-monorepo/
329
+ ├── .vaulter/
330
+ │ ├── config.yaml # Root config
331
+ │ └── environments/
332
+ ├── services/
333
+ │ ├── api/
334
+ │ │ └── .vaulter/
335
+ │ │ ├── config.yaml # extends: ../../../.vaulter/config.yaml
336
+ │ │ └── environments/
337
+ │ └── worker/
338
+ │ └── .vaulter/
339
+ ```
340
+
341
+ ```bash
342
+ # List services
343
+ vaulter services
344
+
345
+ # Sync all services
346
+ vaulter sync -e dev --all
347
+
348
+ # Sync specific services
349
+ vaulter sync -e dev -s api,worker
350
+ ```
351
+
352
+ ## MCP Tools
353
+
354
+ | Tool | Description |
355
+ |:-----|:------------|
356
+ | `vaulter_get` | Get a single variable |
357
+ | `vaulter_set` | Set a variable |
358
+ | `vaulter_delete` | Delete a variable |
359
+ | `vaulter_list` | List all variables |
360
+ | `vaulter_export` | Export in various formats |
361
+ | `vaulter_sync` | Sync with .env file |
362
+
363
+ ## CI/CD
364
+
365
+ ### GitHub Actions
366
+
367
+ ```yaml
368
+ jobs:
369
+ deploy:
370
+ runs-on: ubuntu-latest
371
+ steps:
372
+ - uses: actions/checkout@v4
373
+ - name: Deploy secrets
374
+ env:
375
+ VAULTER_KEY: ${{ secrets.VAULTER_KEY }}
376
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
377
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
378
+ run: |
379
+ npx vaulter k8s:secret -e prd | kubectl apply -f -
380
+ ```
381
+
382
+ ### GitLab CI
383
+
384
+ ```yaml
385
+ deploy:
386
+ script:
387
+ - npx vaulter k8s:secret -e prd | kubectl apply -f -
388
+ variables:
389
+ VAULTER_KEY: $VAULTER_KEY
390
+ ```
391
+
392
+ ## Security Best Practices
393
+
394
+ | Practice | How |
395
+ |:---------|:----|
396
+ | Never commit credentials | Use `config.local.yaml` or env vars |
397
+ | Never commit encryption keys | Add `.vaulter/.key` to `.gitignore` |
398
+ | Use env var expansion | `${AWS_ACCESS_KEY_ID}` instead of hardcoding |
399
+ | Use AWS credential chain | No credentials in URL, use IAM roles |
400
+ | Separate keys per environment | Different keys for dev/stg/prd |
401
+ | Restrict S3 bucket access | IAM policies to limit readers |
402
+
403
+ ### Files to .gitignore
404
+
405
+ ```gitignore
406
+ .vaulter/.key
407
+ .vaulter/config.local.yaml
408
+ **/config.local.yaml
409
+ .env
410
+ .env.*
411
+ ```
412
+
413
+ ## API Usage
414
+
415
+ ```typescript
416
+ import { VaulterClient, loadConfig } from 'vaulter'
417
+
418
+ const config = loadConfig()
419
+ const client = new VaulterClient({ config })
420
+
421
+ await client.connect()
422
+
423
+ // Get
424
+ const value = await client.get('DATABASE_URL', 'my-project', 'prd')
425
+
426
+ // Set
427
+ await client.set({
428
+ key: 'API_KEY',
429
+ value: 'sk-secret',
430
+ project: 'my-project',
431
+ environment: 'prd'
432
+ })
433
+
434
+ // List
435
+ const vars = await client.list({
436
+ project: 'my-project',
437
+ environment: 'prd'
438
+ })
439
+
440
+ // Export
441
+ const envVars = await client.export('my-project', 'prd')
442
+
443
+ await client.disconnect()
444
+ ```
445
+
446
+ ## Comparison
447
+
448
+ | Feature | vaulter | dotenv | doppler | vault |
449
+ |:--------|:-------:|:------:|:-------:|:-----:|
450
+ | Multi-backend | ✅ | ❌ | ❌ | ❌ |
451
+ | Encryption | AES-256-GCM | ❌ | ✅ | ✅ |
452
+ | K8s integration | Native | ❌ | Plugin | Plugin |
453
+ | Self-hosted | ✅ | N/A | ❌ | ✅ |
454
+ | Monorepo | Native | ❌ | Limited | ❌ |
455
+ | MCP/AI | ✅ | ❌ | ❌ | ❌ |
456
+ | Complexity | Low | Low | Medium | High |
457
+
458
+ ## Numbers
459
+
460
+ | Metric | Value |
461
+ |:-------|:------|
462
+ | Backends | 7 (S3, MinIO, R2, Spaces, B2, FileSystem, Memory) |
463
+ | Environments | 5 (dev, stg, prd, sbx, dr) |
464
+ | Export Formats | 5 (shell, json, yaml, env, tfvars) |
465
+ | MCP Tools | 6 |
466
+ | Integrations | 5 (K8s Secret, K8s ConfigMap, Helm, Terraform, tfvars) |
467
+
468
+ ## Pre-built Binaries
469
+
470
+ Download from [Releases](https://github.com/forattini-dev/vaulter/releases):
471
+
472
+ | Platform | Binary |
473
+ |:---------|:-------|
474
+ | Linux x64 | `vaulter-linux` |
475
+ | Linux ARM64 | `vaulter-linux-arm64` |
476
+ | macOS x64 | `vaulter-macos` |
477
+ | macOS ARM64 | `vaulter-macos-arm64` |
478
+ | Windows | `vaulter-win.exe` |
479
+
480
+ ## License
481
+
482
+ MIT © [Forattini](https://github.com/forattini-dev)
483
+
484
+ ---
485
+
486
+ <div align="center">
487
+
488
+ **[Documentation](#configuration)** · **[Issues](https://github.com/forattini-dev/vaulter/issues)** · **[Releases](https://github.com/forattini-dev/vaulter/releases)**
489
+
490
+ </div>
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ "use strict";var Q=Object.create;var _=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var ee=Object.getPrototypeOf,ne=Object.prototype.hasOwnProperty;var te=(n,e,r,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of J(e))!ne.call(n,i)&&i!==r&&_(n,i,{get:()=>e[i],enumerable:!(t=X(e,i))||t.enumerable});return n};var E=(n,e,r)=>(r=n!=null?Q(ee(n)):{},te(e||!n||!n.__esModule?_(r,"default",{value:n,enumerable:!0}):r,n));process.setMaxListeners(50);var G=require("@modelcontextprotocol/sdk/server/index.js"),Z=require("@modelcontextprotocol/sdk/server/stdio.js"),p=require("@modelcontextprotocol/sdk/types.js");var I=require("s3db.js"),re=`file://${process.env.HOME||"/tmp"}/.minienv/store`,ie="minienv-default-dev-key",g=class{db=null;resource=null;connectionString;passphrase;initialized=!1;constructor(e={}){this.connectionString=e.connectionString||re,this.passphrase=e.passphrase||ie}async connect(){this.initialized||(this.db=new I.S3db({connectionString:this.connectionString,passphrase:this.passphrase}),await this.db.connect(),this.resource=await this.db.createResource({name:"environment-variables",attributes:{key:"string|required",value:"secret|required",project:"string|required",service:"string|optional",environment:"enum:dev,stg,prd,sbx,dr|required",tags:"array|items:string|optional",metadata:{description:"string|optional",owner:"string|optional",rotateAfter:"date|optional",source:"enum:manual,sync,import|optional"}},partitions:{byProject:{fields:{project:"string"}},byProjectEnv:{fields:{project:"string",environment:"string"}},byProjectServiceEnv:{fields:{project:"string",service:"string",environment:"string"}}},behavior:"body-overflow",timestamps:!0,asyncPartitions:!0}),this.initialized=!0)}ensureConnected(){if(!this.initialized||!this.resource)throw new Error("MiniEnvClient not initialized. Call connect() first.")}async get(e,r,t,i){this.ensureConnected();let s=i?"byProjectServiceEnv":"byProjectEnv",o=i?{project:r,service:i,environment:t}:{project:r,environment:t};return(await this.resource.list({partition:s,partitionValues:o})).find(l=>l.key===e)||null}async set(e){this.ensureConnected();let r=await this.get(e.key,e.project,e.environment,e.service);return r?await this.resource.update(r.id,{value:e.value,tags:e.tags,metadata:{...r.metadata,...e.metadata,source:e.metadata?.source||"manual"}}):await this.resource.insert({...e,metadata:{...e.metadata,source:e.metadata?.source||"manual"}})}async delete(e,r,t,i){this.ensureConnected();let s=await this.get(e,r,t,i);return s?(await this.resource.delete(s.id),!0):!1}async list(e={}){this.ensureConnected();let{project:r,service:t,environment:i,limit:s,offset:o}=e,a,c;r&&t&&i?(a="byProjectServiceEnv",c={project:r,service:t,environment:i}):r&&i?(a="byProjectEnv",c={project:r,environment:i}):r&&(a="byProject",c={project:r});let l={};return a&&c&&(l.partition=a,l.partitionValues=c),s&&(l.limit=s),o&&(l.offset=o),await this.resource.list(l)}async insertMany(e){this.ensureConnected();let r=[];for(let t of e){let i=await this.set(t);r.push(i)}return r}async deleteAll(e,r,t){this.ensureConnected();let i=await this.list({project:e,environment:r,service:t}),s=0;for(let o of i)await this.resource.delete(o.id),s++;return s}async export(e,r,t){let i=await this.list({project:e,environment:r,service:t}),s={};for(let o of i)s[o.key]=o.value;return s}async sync(e,r,t,i,s={}){this.ensureConnected();let o=await this.list({project:r,environment:t,service:i}),a=new Map(o.map(l=>[l.key,l])),c={added:[],updated:[],deleted:[],unchanged:[],conflicts:[]};for(let[l,d]of Object.entries(e)){let u=a.get(l);u?u.value!==d?(await this.set({key:l,value:d,project:r,environment:t,service:i,metadata:{source:s.source||"sync"}}),c.updated.push(l)):c.unchanged.push(l):(await this.set({key:l,value:d,project:r,environment:t,service:i,metadata:{source:s.source||"sync"}}),c.added.push(l)),a.delete(l)}for(let[l]of a)c.deleted.push(l);return c}async disconnect(){this.db&&(await this.db.disconnect(),this.db=null,this.resource=null,this.initialized=!1)}isConnected(){return this.initialized}getConnectionString(){return this.connectionString}};var v=E(require("node:fs"),1),f=E(require("node:path"),1),A=require("yaml");function se(n){let e=new URL(n);if(e.protocol==="s3:"){let r=e.hostname,t=e.pathname.slice(1),i=e.searchParams.get("region")||void 0;return{bucket:r,key:t,region:i}}if(e.protocol==="http:"||e.protocol==="https:"){let r=`${e.protocol}//${e.host}`,t=e.pathname.slice(1).split("/"),i=t[0],s=t.slice(1).join("/");return{bucket:i,key:s,endpoint:r,accessKeyId:e.username||void 0,secretAccessKey:e.password||void 0}}throw new Error(`Unsupported S3 URL format: ${n}`)}async function oe(n){try{let{S3Client:e,GetObjectCommand:r}=await import("@aws-sdk/client-s3"),t={};n.region&&(t.region=n.region),n.endpoint&&(t.endpoint=n.endpoint,t.forcePathStyle=!0),n.accessKeyId&&n.secretAccessKey&&(t.credentials={accessKeyId:n.accessKeyId,secretAccessKey:n.secretAccessKey});let i=new e(t),s=new r({Bucket:n.bucket,Key:n.key}),o=await i.send(s);if(!o.Body)throw new Error("Empty response from S3");return(await o.Body.transformToString()).trim()}catch(e){throw e.code==="ERR_MODULE_NOT_FOUND"||e.message?.includes("Cannot find module")?new Error("AWS SDK not installed. To use S3 key source, install: pnpm add @aws-sdk/client-s3"):new Error(`Failed to fetch key from S3: ${e.message}`)}}async function T(n){let e=se(n);return oe(e)}var ae=".minienv",O="config.yaml",ce=5,M=10,V={version:"1",project:"",environments:["dev","stg","prd","sbx","dr"],default_environment:"dev",sync:{conflict:"local"},security:{paranoid:!1,confirm_production:!0,auto_encrypt:{patterns:["*_KEY","*_SECRET","*_TOKEN","*_PASSWORD","*_CREDENTIAL","DATABASE_URL","REDIS_URL"]}}};function $(n=process.cwd()){let e=f.default.resolve(n),r=0;for(;r<ce;){let t=f.default.join(e,ae),i=f.default.join(t,O);if(v.default.existsSync(i))return t;let s=f.default.dirname(e);if(s===e)break;e=s,r++}return null}function le(n){if(!v.default.existsSync(n))throw new Error(`Config file not found: ${n}`);let e=v.default.readFileSync(n,"utf-8");return(0,A.parse)(e)||{}}function C(n,e){let r={...n};for(let t of Object.keys(e)){let i=e[t],s=r[t];i!=null&&typeof i=="object"&&!Array.isArray(i)&&s!==null&&s!==void 0&&typeof s=="object"&&!Array.isArray(s)?r[t]=C(s,i):i!==void 0&&(r[t]=i)}return r}function N(n,e=new Set,r=0){if(r>M)throw new Error(`Config inheritance depth exceeded (max ${M})`);let t=f.default.resolve(n);if(e.has(t))throw new Error(`Circular config inheritance detected: ${t}`);e.add(t);let i=le(t);if(i.extends){let s=f.default.resolve(f.default.dirname(t),i.extends),o=N(s,e,r+1),{extends:a,...c}=i;return C(o,c)}return C(V,i)}function w(n){let e=$(n);if(!e)return{...V};let r=f.default.join(e,O);return N(r)}function de(n){return f.default.join(n,"environments")}function D(n,e){return f.default.join(de(n),`${e}.env`)}async function j(n){let e=n.encryption?.key_source||[];for(let r of e)if("env"in r){let t=process.env[r.env];if(t)return t}else if("file"in r){let t=f.default.resolve(r.file);if(v.default.existsSync(t))return v.default.readFileSync(t,"utf-8").trim()}else if("s3"in r)try{let t=await T(r.s3);if(t)return t}catch(t){process.env.MINIENV_VERBOSE&&console.warn(`Failed to load key from S3: ${t.message}`)}return process.env.MINIENV_KEY?process.env.MINIENV_KEY:null}var K=E(require("node:fs"),1),P=100;function F(n,e={}){let r=K.default.readFileSync(n,"utf-8");return ue(r,e)}function ue(n,e={}){let{expand:r=!0,env:t=process.env}=e,i={},s=n.split(/\r?\n/),o=0;for(;o<s.length;){let a=s[o].trim();if(o++,!a||a.startsWith("#")||a.startsWith(";"))continue;let c=a.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$/);if(!c)continue;let l=c[1],d=c[2];if(d.startsWith('"')){let u=pe(d,s,o);d=u.value,o=u.nextIndex}else if(d.startsWith("'")){let u=fe(d,s,o);d=u.value,o=u.nextIndex}else if(d.startsWith("`")){let u=me(d,s,o);d=u.value,o=u.nextIndex}else{let u=d.indexOf("#");u>0&&(d=d.substring(0,u).trim())}r&&(d=ge(d,{...t,...i})),i[l]=d}return i}function pe(n,e,r){let t=n.substring(1),i=0,s="",o=0,a=r;for(;i<P;){for(;o<t.length;){let c=t[o];if(c==="\\"&&o+1<t.length){let l=t[o+1];switch(l){case"n":s+=`
3
+ `;break;case"r":s+="\r";break;case"t":s+=" ";break;case'"':s+='"';break;case"\\":s+="\\";break;case"$":s+="$";break;default:s+=c+l}o+=2}else{if(c==='"')return{value:s,nextIndex:a};s+=c,o++}}if(a>=e.length)break;s+=`
4
+ `,t=e[a],a++,o=0,i++}return{value:s,nextIndex:a}}function fe(n,e,r){let t=n.substring(1),i=0,s="",o=0,a=r;for(;i<P;){for(;o<t.length;){let c=t[o];if(c==="'"&&t[o+1]!=="'")return{value:s,nextIndex:a};c==="'"&&t[o+1]==="'"?(s+="'",o+=2):(s+=c,o++)}if(a>=e.length)break;s+=`
5
+ `,t=e[a],a++,o=0,i++}return{value:s,nextIndex:a}}function me(n,e,r){let t=n.substring(1),i=0,s="",o=0,a=r;for(;i<P;){let c=t.indexOf("`",o);if(c!==-1)return s+=t.substring(o,c),{value:s,nextIndex:a};if(s+=t.substring(o),a>=e.length)break;s+=`
6
+ `,t=e[a],a++,o=0,i++}return{value:s,nextIndex:a}}function ge(n,e){return n=n.replace(/\$\{([a-zA-Z_][a-zA-Z0-9_]*):-([^}]*)\}/g,(r,t,i)=>e[t]??i),n=n.replace(/\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g,(r,t)=>e[t]??""),n=n.replace(/\$([a-zA-Z_][a-zA-Z0-9_]*)/g,(r,t)=>e[t]??""),n}var L=E(require("node:fs"),1);async function ve(){let n=null;try{n=w()}catch{}let e=n?.backend?.url,r=n?await j(n):void 0;return{client:new g({connectionString:e||void 0,passphrase:r||void 0}),config:n}}function U(){return[{name:"minienv_get",description:"Get the value of an environment variable",inputSchema:{type:"object",properties:{key:{type:"string",description:"The name of the environment variable"},environment:{type:"string",description:"Environment (dev/stg/prd/sbx/dr)",enum:["dev","stg","prd","sbx","dr"],default:"dev"},project:{type:"string",description:"Project name (optional, defaults to config)"},service:{type:"string",description:"Service name for monorepos (optional)"}},required:["key"]}},{name:"minienv_set",description:"Set an environment variable value",inputSchema:{type:"object",properties:{key:{type:"string",description:"The name of the environment variable"},value:{type:"string",description:"The value to set"},environment:{type:"string",description:"Environment (dev/stg/prd/sbx/dr)",enum:["dev","stg","prd","sbx","dr"],default:"dev"},project:{type:"string",description:"Project name (optional, defaults to config)"},service:{type:"string",description:"Service name for monorepos (optional)"}},required:["key","value"]}},{name:"minienv_delete",description:"Delete an environment variable",inputSchema:{type:"object",properties:{key:{type:"string",description:"The name of the environment variable to delete"},environment:{type:"string",description:"Environment (dev/stg/prd/sbx/dr)",enum:["dev","stg","prd","sbx","dr"],default:"dev"},project:{type:"string",description:"Project name (optional, defaults to config)"},service:{type:"string",description:"Service name for monorepos (optional)"}},required:["key"]}},{name:"minienv_list",description:"List all environment variables for a project/environment",inputSchema:{type:"object",properties:{environment:{type:"string",description:"Environment (dev/stg/prd/sbx/dr)",enum:["dev","stg","prd","sbx","dr"],default:"dev"},project:{type:"string",description:"Project name (optional, defaults to config)"},service:{type:"string",description:"Service name for monorepos (optional)"},showValues:{type:"boolean",description:"Show values (default: false for security)",default:!1}}}},{name:"minienv_export",description:"Export environment variables in shell format",inputSchema:{type:"object",properties:{environment:{type:"string",description:"Environment (dev/stg/prd/sbx/dr)",enum:["dev","stg","prd","sbx","dr"],default:"dev"},project:{type:"string",description:"Project name (optional, defaults to config)"},service:{type:"string",description:"Service name for monorepos (optional)"},format:{type:"string",description:"Output format",enum:["shell","env","json","yaml"],default:"shell"}}}},{name:"minienv_sync",description:"Sync local .env file with backend storage",inputSchema:{type:"object",properties:{environment:{type:"string",description:"Environment (dev/stg/prd/sbx/dr)",enum:["dev","stg","prd","sbx","dr"],default:"dev"},project:{type:"string",description:"Project name (optional, defaults to config)"},service:{type:"string",description:"Service name for monorepos (optional)"},dryRun:{type:"boolean",description:"Show what would be changed without making changes",default:!1}}}}]}async function z(n,e){let{client:r,config:t}=await ve(),i=e.project||t?.project||"",s=e.environment||t?.default_environment||"dev",o=e.service;if(!i)return{content:[{type:"text",text:"Error: Project not specified. Either set project in args or run from a directory with .minienv/config.yaml"}]};try{switch(await r.connect(),n){case"minienv_get":{let a=e.key,c=await r.get(a,i,s,o);return{content:[{type:"text",text:c!==null?c.value:`Variable ${a} not found`}]}}case"minienv_set":{let a=e.key,c=e.value;return await r.set({key:a,value:c,project:i,environment:s,service:o,metadata:{source:"manual"}}),{content:[{type:"text",text:`\u2713 Set ${a} in ${i}/${s}`}]}}case"minienv_delete":{let a=e.key;return{content:[{type:"text",text:await r.delete(a,i,s,o)?`\u2713 Deleted ${a}`:`Variable ${a} not found`}]}}case"minienv_list":{let a=e.showValues||!1,c=await r.list({project:i,environment:s,service:o});if(c.length===0)return{content:[{type:"text",text:`No variables found for ${i}/${s}`}]};let l=c.map(d=>a?`${d.key}=${d.value}`:d.key);return{content:[{type:"text",text:`Variables in ${i}/${s}:
7
+ ${l.join(`
8
+ `)}`}]}}case"minienv_export":{let a=e.format||"shell",c=await r.export(i,s,o),l;switch(a){case"json":l=JSON.stringify(c,null,2);break;case"yaml":l=Object.entries(c).map(([d,u])=>`${d}: "${u.replace(/"/g,'\\"')}"`).join(`
9
+ `);break;case"env":l=Object.entries(c).map(([d,u])=>`${d}=${u}`).join(`
10
+ `);break;default:l=Object.entries(c).map(([d,u])=>`export ${d}="${u.replace(/"/g,'\\"')}"`).join(`
11
+ `)}return{content:[{type:"text",text:l}]}}case"minienv_sync":{let a=e.dryRun||!1,c=$();if(!c)return{content:[{type:"text",text:"Error: No .minienv directory found"}]};let l=D(c,s);if(!L.default.existsSync(l))return{content:[{type:"text",text:`Error: Environment file not found: ${l}`}]};let d=F(l);if(a){let k=await r.export(i,s,o),x=[],b=[],S=[];for(let[m,Y]of Object.entries(d))m in k?k[m]!==Y&&b.push(m):x.push(m);for(let m of Object.keys(k))m in d||S.push(m);let h=["Dry run - changes that would be made:"];return x.length>0&&h.push(` Add: ${x.join(", ")}`),b.length>0&&h.push(` Update: ${b.join(", ")}`),S.length>0&&h.push(` Delete: ${S.join(", ")}`),x.length===0&&b.length===0&&S.length===0&&h.push(" No changes needed"),{content:[{type:"text",text:h.join(`
12
+ `)}]}}let u=await r.sync(d,i,s,o,{source:"sync"}),y=[`\u2713 Synced ${i}/${s}`];return u.added.length>0&&y.push(` Added: ${u.added.length}`),u.updated.length>0&&y.push(` Updated: ${u.updated.length}`),u.deleted.length>0&&y.push(` Deleted: ${u.deleted.length}`),u.unchanged.length>0&&y.push(` Unchanged: ${u.unchanged.length}`),{content:[{type:"text",text:y.join(`
13
+ `)}]}}default:return{content:[{type:"text",text:`Unknown tool: ${n}`}]}}}finally{await r.disconnect()}}var q=["dev","stg","prd","sbx","dr"];async function W(){let n=null;try{n=w()}catch{}let e=n?.backend?.url,r=n?await j(n):void 0;return{client:new g({connectionString:e||void 0,passphrase:r||void 0}),config:n}}function ye(n){let e=n.match(/^minienv:\/\/([^/]+)\/([^/]+)(?:\/([^/]+))?$/);if(!e)return null;let[,r,t,i]=e;return q.includes(t)?{project:r,environment:t,service:i}:null}async function H(){let{config:n}=await W();if(!n?.project)return[];let e=n.project,r=n.environments||q,t=n.service,i=[];for(let s of r){let o=t?`minienv://${e}/${s}/${t}`:`minienv://${e}/${s}`;i.push({uri:o,name:`${e}/${s}${t?`/${t}`:""}`,description:`Environment variables for ${e} in ${s}`,mimeType:"text/plain"})}return i}async function B(n){let e=ye(n);if(!e)throw new Error(`Invalid resource URI: ${n}. Expected format: minienv://project/environment[/service]`);let{project:r,environment:t,service:i}=e,{client:s}=await W();try{await s.connect();let o=await s.export(r,t,i),a=Object.entries(o);if(a.length===0)return{contents:[{uri:n,mimeType:"text/plain",text:`# No variables found for ${r}/${t}`}]};let c=a.map(([l,d])=>`${l}=${d}`).join(`
14
+ `);return{contents:[{uri:n,mimeType:"text/plain",text:`# Environment: ${r}/${t}${i?`/${i}`:""}
15
+ # Variables: ${a.length}
16
+
17
+ ${c}`}]}}finally{await s.disconnect()}}var he="minienv",Ee="0.1.0";function xe(){let n=new G.Server({name:he,version:Ee},{capabilities:{tools:{},resources:{}}});return n.setRequestHandler(p.ListToolsRequestSchema,async()=>({tools:U()})),n.setRequestHandler(p.CallToolRequestSchema,async e=>{let{name:r,arguments:t}=e.params;try{return await z(r,t||{})}catch(i){let s=i instanceof Error?i.message:String(i);throw new p.McpError(p.ErrorCode.InternalError,s)}}),n.setRequestHandler(p.ListResourcesRequestSchema,async()=>({resources:await H()})),n.setRequestHandler(p.ReadResourceRequestSchema,async e=>{let{uri:r}=e.params;try{return await B(r)}catch(t){let i=t instanceof Error?t.message:String(t);throw new p.McpError(p.ErrorCode.InternalError,i)}}),n}async function R(){let n=xe(),e=new Z.StdioServerTransport;await n.connect(e),process.on("SIGINT",async()=>{await n.close(),process.exit(0)}),process.on("SIGTERM",async()=>{await n.close(),process.exit(0)})}(process.argv[1]?.endsWith("server.js")||process.argv[1]?.endsWith("server.ts"))&&R().catch(n=>{console.error("Failed to start MCP server:",n),process.exit(1)});R().catch(n=>{console.error("Failed to start MCP server:",n),process.exit(1)});