@fentz26/envcp 1.0.1 → 1.0.3
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 +82 -133
- package/dist/adapters/base.d.ts +1 -2
- package/dist/adapters/base.d.ts.map +1 -1
- package/dist/adapters/base.js +139 -14
- package/dist/adapters/base.js.map +1 -1
- package/dist/adapters/gemini.d.ts +1 -0
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +13 -99
- package/dist/adapters/gemini.js.map +1 -1
- package/dist/adapters/openai.d.ts +1 -0
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +13 -99
- package/dist/adapters/openai.js.map +1 -1
- package/dist/adapters/rest.d.ts +1 -0
- package/dist/adapters/rest.d.ts.map +1 -1
- package/dist/adapters/rest.js +16 -13
- package/dist/adapters/rest.js.map +1 -1
- package/dist/cli/index.js +510 -197
- package/dist/cli/index.js.map +1 -1
- package/dist/config/manager.d.ts +6 -0
- package/dist/config/manager.d.ts.map +1 -1
- package/dist/config/manager.js +81 -1
- package/dist/config/manager.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -16
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +23 -511
- package/dist/mcp/server.js.map +1 -1
- package/dist/server/unified.d.ts +1 -0
- package/dist/server/unified.d.ts.map +1 -1
- package/dist/server/unified.js +31 -19
- package/dist/server/unified.js.map +1 -1
- package/dist/storage/index.d.ts +12 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +107 -10
- package/dist/storage/index.js.map +1 -1
- package/dist/types.d.ts +28 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/crypto.d.ts +3 -0
- package/dist/utils/crypto.d.ts.map +1 -1
- package/dist/utils/crypto.js +12 -0
- package/dist/utils/crypto.js.map +1 -1
- package/dist/utils/http.d.ts +13 -1
- package/dist/utils/http.d.ts.map +1 -1
- package/dist/utils/http.js +65 -2
- package/dist/utils/http.js.map +1 -1
- package/dist/utils/session.d.ts.map +1 -1
- package/dist/utils/session.js +8 -3
- package/dist/utils/session.js.map +1 -1
- package/package.json +9 -3
- package/.github/workflows/publish.yml +0 -48
- package/src/adapters/base.ts +0 -411
- package/src/adapters/gemini.ts +0 -314
- package/src/adapters/index.ts +0 -4
- package/src/adapters/openai.ts +0 -324
- package/src/adapters/rest.ts +0 -294
- package/src/cli/index.ts +0 -640
- package/src/cli.ts +0 -2
- package/src/config/manager.ts +0 -134
- package/src/index.ts +0 -4
- package/src/mcp/index.ts +0 -1
- package/src/mcp/server.ts +0 -623
- package/src/server/index.ts +0 -1
- package/src/server/unified.ts +0 -460
- package/src/storage/index.ts +0 -112
- package/src/types.ts +0 -181
- package/src/utils/crypto.ts +0 -100
- package/src/utils/http.ts +0 -45
- package/src/utils/session.ts +0 -141
- package/tsconfig.json +0 -20
package/README.md
CHANGED
|
@@ -2,104 +2,76 @@
|
|
|
2
2
|
|
|
3
3
|
**Secure Environment Variable Management for AI-Assisted Coding**
|
|
4
4
|
|
|
5
|
-
EnvCP
|
|
6
|
-
|
|
7
|
-
## Why EnvCP?
|
|
8
|
-
|
|
9
|
-
When using AI coding assistants, you often need to reference environment variables, API keys, or other secrets. But you don't want to share these with the AI. EnvCP solves this by:
|
|
10
|
-
|
|
11
|
-
- **Local-only storage** - Your secrets never leave your machine
|
|
12
|
-
- **Encrypted at rest** - AES-256-GCM encryption with PBKDF2 key derivation (100,000 iterations)
|
|
13
|
-
- **Reference-based access** - AI references variables by name, never sees the actual values
|
|
14
|
-
- **Automatic .env injection** - Values can be automatically injected into your .env files
|
|
15
|
-
- **AI Access Control** - Block AI from proactively checking or listing your secrets
|
|
16
|
-
- **Universal Compatibility** - Works with any AI tool via multiple protocols
|
|
17
|
-
|
|
18
|
-
## Platform Compatibility
|
|
19
|
-
|
|
20
|
-
| Platform | Support | Protocol |
|
|
21
|
-
|----------|---------|----------|
|
|
22
|
-
| Claude Desktop | Native | MCP |
|
|
23
|
-
| Claude Code | Native | MCP |
|
|
24
|
-
| Cursor | Native | MCP |
|
|
25
|
-
| Cline (VS Code) | Native | MCP |
|
|
26
|
-
| Continue.dev | Native | MCP |
|
|
27
|
-
| Zed Editor | Native | MCP |
|
|
28
|
-
| ChatGPT | Via API | OpenAI Function Calling |
|
|
29
|
-
| GPT-4 API | Via API | OpenAI Function Calling |
|
|
30
|
-
| Gemini | Via API | Google Function Calling |
|
|
31
|
-
| Gemini API | Via API | Google Function Calling |
|
|
32
|
-
| Local LLMs (Ollama) | Via API | REST / OpenAI-compatible |
|
|
33
|
-
| LM Studio | Via API | REST / OpenAI-compatible |
|
|
34
|
-
| Open WebUI | Via API | REST |
|
|
35
|
-
| Any HTTP Client | Via API | REST |
|
|
36
|
-
|
|
37
|
-
## Features
|
|
38
|
-
|
|
39
|
-
- **Multi-Protocol Support** - MCP, REST API, OpenAI, and Gemini protocols
|
|
40
|
-
- **Auto-Detection** - Automatically detects client type from request headers
|
|
41
|
-
- **AES-256-GCM Encryption** - Military-grade encryption with PBKDF2-SHA512 key derivation
|
|
42
|
-
- **Flexible Passwords** - No complexity requirements - use any password you want
|
|
43
|
-
- **Session Management** - Quick password mode with configurable session duration
|
|
44
|
-
- **AI Access Control** - Prevent AI from actively checking variables without permission
|
|
45
|
-
- **Blacklist Patterns** - Block AI access to sensitive variables matching patterns
|
|
46
|
-
- **Project-based Organization** - Separate environments per project
|
|
47
|
-
- **Auto .env Sync** - Automatically sync to .env files
|
|
48
|
-
- **Audit Logging** - All operations logged for security review
|
|
5
|
+
EnvCP lets you safely use AI assistants (Claude, ChatGPT, Gemini, Cursor, etc.) without exposing your secrets. Your API keys and environment variables stay encrypted on your machine — AI only references them by name.
|
|
49
6
|
|
|
50
7
|
## Installation
|
|
51
8
|
|
|
52
9
|
```bash
|
|
53
|
-
npm install -g envcp
|
|
10
|
+
npm install -g @fentz26/envcp
|
|
54
11
|
```
|
|
55
12
|
|
|
56
|
-
Or use
|
|
13
|
+
Or use without installing:
|
|
57
14
|
|
|
58
15
|
```bash
|
|
59
|
-
npx envcp init
|
|
16
|
+
npx @fentz26/envcp init
|
|
60
17
|
```
|
|
61
18
|
|
|
62
19
|
## Quick Start
|
|
63
20
|
|
|
64
|
-
### 1. Initialize EnvCP in your project
|
|
65
|
-
|
|
66
21
|
```bash
|
|
22
|
+
# 1. Initialize in your project
|
|
67
23
|
envcp init
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### 2. Add your secrets
|
|
71
24
|
|
|
72
|
-
|
|
25
|
+
# 2. Add your secrets
|
|
73
26
|
envcp add API_KEY --value "your-secret-key"
|
|
74
27
|
envcp add DATABASE_URL --value "postgres://..."
|
|
28
|
+
|
|
29
|
+
# 3. Start the server (auto-detects client type)
|
|
30
|
+
envcp serve --mode auto --port 3456
|
|
75
31
|
```
|
|
76
32
|
|
|
77
|
-
|
|
33
|
+
## Basic CLI Commands
|
|
78
34
|
|
|
79
35
|
```bash
|
|
80
|
-
#
|
|
81
|
-
envcp
|
|
36
|
+
# Variable Management
|
|
37
|
+
envcp add <name> [options] # Add a variable
|
|
38
|
+
envcp list [--show-values] # List variables
|
|
39
|
+
envcp get <name> # Get a variable
|
|
40
|
+
envcp remove <name> # Remove a variable
|
|
41
|
+
|
|
42
|
+
# Session Management
|
|
43
|
+
envcp unlock # Unlock with password
|
|
44
|
+
envcp lock # Lock immediately
|
|
45
|
+
envcp status # Check session status
|
|
82
46
|
|
|
83
|
-
#
|
|
84
|
-
envcp
|
|
85
|
-
envcp
|
|
86
|
-
envcp serve --mode openai # For ChatGPT/OpenAI-compatible
|
|
87
|
-
envcp serve --mode gemini # For Google Gemini
|
|
88
|
-
envcp serve --mode all # All protocols on same port
|
|
47
|
+
# Sync and Export
|
|
48
|
+
envcp sync # Sync to .env file
|
|
49
|
+
envcp export [--format env|json|yaml]
|
|
89
50
|
```
|
|
90
51
|
|
|
52
|
+
## Why EnvCP?
|
|
53
|
+
|
|
54
|
+
- **Local-only storage** — Your secrets never leave your machine
|
|
55
|
+
- **Encrypted at rest** — AES-256-GCM with PBKDF2 key derivation (100,000 iterations)
|
|
56
|
+
- **Reference-based access** — AI references variables by name, never sees the actual values
|
|
57
|
+
- **Automatic .env injection** — Values can be automatically injected into your .env files
|
|
58
|
+
- **AI Access Control** — Block AI from proactively listing or checking your secrets
|
|
59
|
+
- **Universal Compatibility** — Works with any AI tool via MCP, OpenAI, Gemini, or REST protocols
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
91
63
|
## Integration Guides
|
|
92
64
|
|
|
93
65
|
### Claude Desktop / Cursor / Cline (MCP)
|
|
94
66
|
|
|
95
|
-
Add to your config file:
|
|
67
|
+
Add to your MCP config file:
|
|
96
68
|
|
|
97
69
|
```json
|
|
98
70
|
{
|
|
99
71
|
"mcpServers": {
|
|
100
72
|
"envcp": {
|
|
101
73
|
"command": "npx",
|
|
102
|
-
"args": ["envcp", "serve", "--mode", "mcp"]
|
|
74
|
+
"args": ["@fentz26/envcp", "serve", "--mode", "mcp"]
|
|
103
75
|
}
|
|
104
76
|
}
|
|
105
77
|
}
|
|
@@ -107,26 +79,18 @@ Add to your config file:
|
|
|
107
79
|
|
|
108
80
|
### ChatGPT / OpenAI API
|
|
109
81
|
|
|
110
|
-
Start the server in OpenAI mode:
|
|
111
|
-
|
|
112
82
|
```bash
|
|
113
83
|
envcp serve --mode openai --port 3456 --api-key your-secret-key
|
|
114
84
|
```
|
|
115
85
|
|
|
116
|
-
Use with OpenAI client:
|
|
117
|
-
|
|
118
86
|
```python
|
|
119
87
|
import openai
|
|
120
88
|
|
|
121
|
-
# Point to EnvCP server
|
|
122
89
|
client = openai.OpenAI(
|
|
123
90
|
base_url="http://localhost:3456/v1",
|
|
124
91
|
api_key="your-secret-key"
|
|
125
92
|
)
|
|
126
93
|
|
|
127
|
-
# Get available functions
|
|
128
|
-
functions = client.get("/functions")
|
|
129
|
-
|
|
130
94
|
# Call a function
|
|
131
95
|
result = client.post("/functions/call", json={
|
|
132
96
|
"name": "envcp_get",
|
|
@@ -136,14 +100,10 @@ result = client.post("/functions/call", json={
|
|
|
136
100
|
|
|
137
101
|
### Gemini / Google AI
|
|
138
102
|
|
|
139
|
-
Start the server in Gemini mode:
|
|
140
|
-
|
|
141
103
|
```bash
|
|
142
104
|
envcp serve --mode gemini --port 3456 --api-key your-secret-key
|
|
143
105
|
```
|
|
144
106
|
|
|
145
|
-
Use with Gemini:
|
|
146
|
-
|
|
147
107
|
```python
|
|
148
108
|
import requests
|
|
149
109
|
|
|
@@ -163,22 +123,18 @@ result = requests.post(
|
|
|
163
123
|
|
|
164
124
|
### Local LLMs (Ollama, LM Studio)
|
|
165
125
|
|
|
166
|
-
For local LLMs, use REST API mode or OpenAI-compatible mode:
|
|
167
|
-
|
|
168
126
|
```bash
|
|
169
|
-
# REST API (universal)
|
|
170
|
-
envcp serve --mode rest --port 3456
|
|
171
|
-
|
|
172
127
|
# OpenAI-compatible (works with most local LLM tools)
|
|
173
128
|
envcp serve --mode openai --port 3456
|
|
129
|
+
|
|
130
|
+
# Or universal REST
|
|
131
|
+
envcp serve --mode rest --port 3456
|
|
174
132
|
```
|
|
175
133
|
|
|
176
|
-
|
|
134
|
+
Configure your LLM tool to use `http://localhost:3456` as the tool server.
|
|
177
135
|
|
|
178
136
|
### REST API (Universal)
|
|
179
137
|
|
|
180
|
-
Start the server:
|
|
181
|
-
|
|
182
138
|
```bash
|
|
183
139
|
envcp serve --mode rest --port 3456 --api-key your-secret-key
|
|
184
140
|
```
|
|
@@ -198,8 +154,6 @@ GET /api/tools - List available tools
|
|
|
198
154
|
POST /api/tools/:name - Call tool by name
|
|
199
155
|
```
|
|
200
156
|
|
|
201
|
-
Example:
|
|
202
|
-
|
|
203
157
|
```bash
|
|
204
158
|
# List variables
|
|
205
159
|
curl -H "X-API-Key: your-secret-key" http://localhost:3456/api/variables
|
|
@@ -218,36 +172,14 @@ curl -X POST -H "X-API-Key: your-secret-key" \
|
|
|
218
172
|
|
|
219
173
|
| Mode | Description | Use Case |
|
|
220
174
|
|------|-------------|----------|
|
|
175
|
+
| `auto` | Auto-detect client from headers | Universal (recommended for HTTP) |
|
|
221
176
|
| `mcp` | Model Context Protocol (stdio) | Claude Desktop, Cursor, Cline |
|
|
222
177
|
| `rest` | REST API (HTTP) | Any HTTP client, custom integrations |
|
|
223
178
|
| `openai` | OpenAI function calling format | ChatGPT, GPT-4 API, OpenAI-compatible tools |
|
|
224
179
|
| `gemini` | Google function calling format | Gemini, Google AI |
|
|
225
180
|
| `all` | All HTTP protocols on same port | Multiple clients |
|
|
226
|
-
| `auto` | Auto-detect client from headers | Universal (recommended for HTTP) |
|
|
227
|
-
|
|
228
|
-
## CLI Commands
|
|
229
181
|
|
|
230
182
|
```bash
|
|
231
|
-
# Initialize
|
|
232
|
-
envcp init [options]
|
|
233
|
-
|
|
234
|
-
# Session Management
|
|
235
|
-
envcp unlock # Unlock session with password
|
|
236
|
-
envcp lock # Lock session immediately
|
|
237
|
-
envcp status # Check session status
|
|
238
|
-
envcp extend [minutes] # Extend session duration
|
|
239
|
-
|
|
240
|
-
# Variable Management
|
|
241
|
-
envcp add <name> [options]
|
|
242
|
-
envcp list [--show-values]
|
|
243
|
-
envcp get <name>
|
|
244
|
-
envcp remove <name>
|
|
245
|
-
|
|
246
|
-
# Sync and Export
|
|
247
|
-
envcp sync # Sync to .env file
|
|
248
|
-
envcp export [--format env|json|yaml]
|
|
249
|
-
|
|
250
|
-
# Server
|
|
251
183
|
envcp serve [options]
|
|
252
184
|
--mode, -m Server mode: mcp, rest, openai, gemini, all, auto
|
|
253
185
|
--port HTTP port (default: 3456)
|
|
@@ -256,9 +188,40 @@ envcp serve [options]
|
|
|
256
188
|
--password, -p Encryption password
|
|
257
189
|
```
|
|
258
190
|
|
|
259
|
-
##
|
|
191
|
+
## Platform Compatibility
|
|
192
|
+
|
|
193
|
+
| Platform | Support | Protocol |
|
|
194
|
+
|----------|---------|----------|
|
|
195
|
+
| Claude Desktop | Native | MCP |
|
|
196
|
+
| Claude Code | Native | MCP |
|
|
197
|
+
| Cursor | Native | MCP |
|
|
198
|
+
| Cline (VS Code) | Native | MCP |
|
|
199
|
+
| Continue.dev | Native | MCP |
|
|
200
|
+
| Zed Editor | Native | MCP |
|
|
201
|
+
| ChatGPT | Via API | OpenAI Function Calling |
|
|
202
|
+
| GPT-4 API | Via API | OpenAI Function Calling |
|
|
203
|
+
| Gemini | Via API | Google Function Calling |
|
|
204
|
+
| Gemini API | Via API | Google Function Calling |
|
|
205
|
+
| Local LLMs (Ollama) | Via API | REST / OpenAI-compatible |
|
|
206
|
+
| LM Studio | Via API | REST / OpenAI-compatible |
|
|
207
|
+
| Open WebUI | Via API | REST |
|
|
208
|
+
| Any HTTP Client | Via API | REST |
|
|
260
209
|
|
|
261
|
-
|
|
210
|
+
## Available Tools
|
|
211
|
+
|
|
212
|
+
All protocols expose the same tools:
|
|
213
|
+
|
|
214
|
+
| Tool | Description |
|
|
215
|
+
|------|-------------|
|
|
216
|
+
| `envcp_list` | List variable names (not values) |
|
|
217
|
+
| `envcp_get` | Get a variable (masked by default) |
|
|
218
|
+
| `envcp_set` | Create/update a variable |
|
|
219
|
+
| `envcp_delete` | Delete a variable |
|
|
220
|
+
| `envcp_sync` | Sync to .env file |
|
|
221
|
+
| `envcp_run` | Run command with env vars injected |
|
|
222
|
+
| `envcp_check_access` | Check if variable is accessible |
|
|
223
|
+
|
|
224
|
+
## Configuration (envcp.yaml)
|
|
262
225
|
|
|
263
226
|
```yaml
|
|
264
227
|
version: "1.0"
|
|
@@ -298,20 +261,6 @@ sync:
|
|
|
298
261
|
- "*_SECRET"
|
|
299
262
|
```
|
|
300
263
|
|
|
301
|
-
## Available Tools
|
|
302
|
-
|
|
303
|
-
All protocols expose the same tools:
|
|
304
|
-
|
|
305
|
-
| Tool | Description |
|
|
306
|
-
|------|-------------|
|
|
307
|
-
| `envcp_list` | List variable names (not values) |
|
|
308
|
-
| `envcp_get` | Get a variable (masked by default) |
|
|
309
|
-
| `envcp_set` | Create/update a variable |
|
|
310
|
-
| `envcp_delete` | Delete a variable |
|
|
311
|
-
| `envcp_sync` | Sync to .env file |
|
|
312
|
-
| `envcp_run` | Run command with env vars injected |
|
|
313
|
-
| `envcp_check_access` | Check if variable is accessible |
|
|
314
|
-
|
|
315
264
|
## AI Access Control
|
|
316
265
|
|
|
317
266
|
### Disable Active Checking
|
|
@@ -338,7 +287,7 @@ access:
|
|
|
338
287
|
|
|
339
288
|
## Security
|
|
340
289
|
|
|
341
|
-
### Encryption
|
|
290
|
+
### Encryption Details
|
|
342
291
|
|
|
343
292
|
- **Cipher**: AES-256-GCM
|
|
344
293
|
- **Key Derivation**: PBKDF2-SHA512 (100,000 iterations)
|
|
@@ -364,16 +313,16 @@ Authorization: Bearer your-secret-key
|
|
|
364
313
|
|
|
365
314
|
## Best Practices
|
|
366
315
|
|
|
367
|
-
1. **Never commit `.envcp/`**
|
|
368
|
-
2. **Use API keys for HTTP modes**
|
|
369
|
-
3. **Disable `allow_ai_active_check`**
|
|
370
|
-
4. **Use blacklist patterns**
|
|
371
|
-
5. **Use `auto` mode for HTTP**
|
|
372
|
-
6. **Review access logs**
|
|
316
|
+
1. **Never commit `.envcp/`** — Add to `.gitignore`
|
|
317
|
+
2. **Use API keys for HTTP modes** — Protect your server endpoints
|
|
318
|
+
3. **Disable `allow_ai_active_check`** — Prevent AI from probing for variables
|
|
319
|
+
4. **Use blacklist patterns** — Block sensitive variable patterns
|
|
320
|
+
5. **Use `auto` mode for HTTP** — Let EnvCP detect the client type
|
|
321
|
+
6. **Review access logs** — Check `.envcp/logs/` regularly
|
|
373
322
|
|
|
374
323
|
## License
|
|
375
324
|
|
|
376
|
-
MIT License
|
|
325
|
+
MIT License — See LICENSE file for details.
|
|
377
326
|
|
|
378
327
|
## Support
|
|
379
328
|
|
package/dist/adapters/base.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export declare abstract class BaseAdapter {
|
|
|
10
10
|
protected tools: Map<string, ToolDefinition>;
|
|
11
11
|
constructor(config: EnvCPConfig, projectPath: string, password?: string);
|
|
12
12
|
protected abstract registerTools(): void;
|
|
13
|
+
protected registerDefaultTools(): void;
|
|
13
14
|
init(): Promise<void>;
|
|
14
15
|
protected ensurePassword(): Promise<void>;
|
|
15
16
|
getToolDefinitions(): ToolDefinition[];
|
|
@@ -60,9 +61,7 @@ export declare abstract class BaseAdapter {
|
|
|
60
61
|
name: string;
|
|
61
62
|
}): Promise<{
|
|
62
63
|
name: string;
|
|
63
|
-
exists: boolean;
|
|
64
64
|
accessible: boolean;
|
|
65
|
-
blacklisted: boolean;
|
|
66
65
|
message: string;
|
|
67
66
|
}>;
|
|
68
67
|
private parseCommand;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/adapters/base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,WAAW,EAAY,cAAc,EAAE,MAAM,aAAa,CAAC;AAGpE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAKrD,8BAAsB,WAAW;IAC/B,SAAS,CAAC,OAAO,EAAE,cAAc,CAAC;IAClC,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC;IAC3B,SAAS,CAAC,cAAc,EAAE,cAAc,CAAC;IACzC,SAAS,CAAC,MAAM,EAAE,WAAW,CAAC;IAC9B,SAAS,CAAC,WAAW,EAAE,MAAM,CAAC;IAC9B,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;gBAEjC,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;IAuBvE,SAAS,CAAC,QAAQ,CAAC,aAAa,IAAI,IAAI;
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/adapters/base.ts"],"names":[],"mappings":"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"}
|
package/dist/adapters/base.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StorageManager, LogManager } from '../storage/index.js';
|
|
2
2
|
import { maskValue } from '../utils/crypto.js';
|
|
3
|
-
import { canAccess, isBlacklisted, canAIActiveCheck } from '../config/manager.js';
|
|
3
|
+
import { canAccess, isBlacklisted, canAIActiveCheck, validateVariableName, matchesPattern } from '../config/manager.js';
|
|
4
4
|
import { SessionManager } from '../utils/session.js';
|
|
5
5
|
import * as fs from 'fs-extra';
|
|
6
6
|
import * as path from 'path';
|
|
@@ -24,6 +24,106 @@ export class BaseAdapter {
|
|
|
24
24
|
this.tools = new Map();
|
|
25
25
|
this.registerTools();
|
|
26
26
|
}
|
|
27
|
+
registerDefaultTools() {
|
|
28
|
+
const tools = [
|
|
29
|
+
{
|
|
30
|
+
name: 'envcp_list',
|
|
31
|
+
description: 'List all available environment variable names. Values are never shown.',
|
|
32
|
+
parameters: {
|
|
33
|
+
type: 'object',
|
|
34
|
+
properties: {
|
|
35
|
+
tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags' },
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
handler: async (params) => this.listVariables(params),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'envcp_get',
|
|
42
|
+
description: 'Get an environment variable. Returns masked value by default.',
|
|
43
|
+
parameters: {
|
|
44
|
+
type: 'object',
|
|
45
|
+
properties: {
|
|
46
|
+
name: { type: 'string', description: 'Variable name' },
|
|
47
|
+
show_value: { type: 'boolean', description: 'Show actual value (requires user confirmation)' },
|
|
48
|
+
},
|
|
49
|
+
required: ['name'],
|
|
50
|
+
},
|
|
51
|
+
handler: async (params) => this.getVariable(params),
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'envcp_set',
|
|
55
|
+
description: 'Create or update an environment variable.',
|
|
56
|
+
parameters: {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: {
|
|
59
|
+
name: { type: 'string', description: 'Variable name' },
|
|
60
|
+
value: { type: 'string', description: 'Variable value' },
|
|
61
|
+
tags: { type: 'array', items: { type: 'string' }, description: 'Tags' },
|
|
62
|
+
description: { type: 'string', description: 'Description' },
|
|
63
|
+
},
|
|
64
|
+
required: ['name', 'value'],
|
|
65
|
+
},
|
|
66
|
+
handler: async (params) => this.setVariable(params),
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'envcp_delete',
|
|
70
|
+
description: 'Delete an environment variable.',
|
|
71
|
+
parameters: {
|
|
72
|
+
type: 'object',
|
|
73
|
+
properties: {
|
|
74
|
+
name: { type: 'string', description: 'Variable name' },
|
|
75
|
+
},
|
|
76
|
+
required: ['name'],
|
|
77
|
+
},
|
|
78
|
+
handler: async (params) => this.deleteVariable(params),
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'envcp_sync',
|
|
82
|
+
description: 'Sync variables to .env file.',
|
|
83
|
+
parameters: { type: 'object', properties: {} },
|
|
84
|
+
handler: async () => this.syncToEnv(),
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'envcp_run',
|
|
88
|
+
description: 'Execute a command with environment variables injected.',
|
|
89
|
+
parameters: {
|
|
90
|
+
type: 'object',
|
|
91
|
+
properties: {
|
|
92
|
+
command: { type: 'string', description: 'Command to execute' },
|
|
93
|
+
variables: { type: 'array', items: { type: 'string' }, description: 'Variables to inject' },
|
|
94
|
+
},
|
|
95
|
+
required: ['command', 'variables'],
|
|
96
|
+
},
|
|
97
|
+
handler: async (params) => this.runCommand(params),
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: 'envcp_add_to_env',
|
|
101
|
+
description: 'Write a stored variable to a .env file.',
|
|
102
|
+
parameters: {
|
|
103
|
+
type: 'object',
|
|
104
|
+
properties: {
|
|
105
|
+
name: { type: 'string', description: 'Variable name to add' },
|
|
106
|
+
env_file: { type: 'string', description: 'Path to .env file (default: .env)' },
|
|
107
|
+
},
|
|
108
|
+
required: ['name'],
|
|
109
|
+
},
|
|
110
|
+
handler: async (params) => this.addToEnv(params),
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'envcp_check_access',
|
|
114
|
+
description: 'Check if a variable exists and can be accessed.',
|
|
115
|
+
parameters: {
|
|
116
|
+
type: 'object',
|
|
117
|
+
properties: {
|
|
118
|
+
name: { type: 'string', description: 'Variable name to check' },
|
|
119
|
+
},
|
|
120
|
+
required: ['name'],
|
|
121
|
+
},
|
|
122
|
+
handler: async (params) => this.checkAccess(params),
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
tools.forEach(tool => this.tools.set(tool.name, tool));
|
|
126
|
+
}
|
|
27
127
|
async init() {
|
|
28
128
|
await this.logs.init();
|
|
29
129
|
await this.sessionManager.init();
|
|
@@ -111,6 +211,9 @@ export class BaseAdapter {
|
|
|
111
211
|
if (!this.config.access.allow_ai_write) {
|
|
112
212
|
throw new Error('AI write access is disabled');
|
|
113
213
|
}
|
|
214
|
+
if (!validateVariableName(args.name)) {
|
|
215
|
+
throw new Error(`Invalid variable name '${args.name}'. Must match [A-Za-z_][A-Za-z0-9_]*`);
|
|
216
|
+
}
|
|
114
217
|
if (isBlacklisted(args.name, this.config)) {
|
|
115
218
|
throw new Error(`Variable '${args.name}' is blacklisted`);
|
|
116
219
|
}
|
|
@@ -171,14 +274,13 @@ export class BaseAdapter {
|
|
|
171
274
|
if (isBlacklisted(name, this.config)) {
|
|
172
275
|
continue;
|
|
173
276
|
}
|
|
174
|
-
const excluded = this.config.sync.exclude?.some(pattern =>
|
|
175
|
-
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
|
|
176
|
-
return regex.test(name);
|
|
177
|
-
});
|
|
277
|
+
const excluded = this.config.sync.exclude?.some(pattern => matchesPattern(name, pattern));
|
|
178
278
|
if (excluded || !variable.sync_to_env) {
|
|
179
279
|
continue;
|
|
180
280
|
}
|
|
181
|
-
|
|
281
|
+
const needsQuoting = /[\s#"'\\]/.test(variable.value);
|
|
282
|
+
const val = needsQuoting ? `"${variable.value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"` : variable.value;
|
|
283
|
+
lines.push(`${name}=${val}`);
|
|
182
284
|
}
|
|
183
285
|
const envPath = path.join(this.projectPath, this.config.sync.target);
|
|
184
286
|
await fs.writeFile(envPath, lines.join('\n'), 'utf8');
|
|
@@ -199,24 +301,29 @@ export class BaseAdapter {
|
|
|
199
301
|
if (isBlacklisted(args.name, this.config)) {
|
|
200
302
|
throw new Error(`Variable '${args.name}' is blacklisted`);
|
|
201
303
|
}
|
|
202
|
-
const envPath = path.
|
|
304
|
+
const envPath = path.resolve(this.projectPath, args.env_file || '.env');
|
|
305
|
+
if (!envPath.startsWith(path.resolve(this.projectPath))) {
|
|
306
|
+
throw new Error('env_file must be within the project directory');
|
|
307
|
+
}
|
|
203
308
|
let content = '';
|
|
204
309
|
if (await fs.pathExists(envPath)) {
|
|
205
310
|
content = await fs.readFile(envPath, 'utf8');
|
|
206
311
|
}
|
|
207
312
|
const envVars = dotenv.parse(content);
|
|
313
|
+
const needsQuoting = /[\s#"'\\]/.test(variable.value);
|
|
314
|
+
const quotedValue = needsQuoting ? `"${variable.value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"` : variable.value;
|
|
208
315
|
if (envVars[args.name]) {
|
|
209
316
|
const lines = content.split('\n');
|
|
210
317
|
const newLines = lines.map(line => {
|
|
211
318
|
if (line.startsWith(`${args.name}=`)) {
|
|
212
|
-
return `${args.name}=${
|
|
319
|
+
return `${args.name}=${quotedValue}`;
|
|
213
320
|
}
|
|
214
321
|
return line;
|
|
215
322
|
});
|
|
216
323
|
content = newLines.join('\n');
|
|
217
324
|
}
|
|
218
325
|
else {
|
|
219
|
-
content += `\n${args.name}=${
|
|
326
|
+
content += `\n${args.name}=${quotedValue}`;
|
|
220
327
|
}
|
|
221
328
|
await fs.writeFile(envPath, content, 'utf8');
|
|
222
329
|
await this.logs.log({
|
|
@@ -244,10 +351,8 @@ export class BaseAdapter {
|
|
|
244
351
|
});
|
|
245
352
|
return {
|
|
246
353
|
name: args.name,
|
|
247
|
-
exists,
|
|
248
354
|
accessible,
|
|
249
|
-
|
|
250
|
-
message: accessible ? 'Variable exists and can be accessed' : 'Variable cannot be accessed or does not exist',
|
|
355
|
+
message: accessible ? 'Variable exists and can be accessed' : 'Variable cannot be accessed',
|
|
251
356
|
};
|
|
252
357
|
}
|
|
253
358
|
parseCommand(command) {
|
|
@@ -286,9 +391,17 @@ export class BaseAdapter {
|
|
|
286
391
|
}
|
|
287
392
|
}
|
|
288
393
|
async runCommand(args) {
|
|
394
|
+
if (!this.config.access.allow_ai_execute) {
|
|
395
|
+
throw new Error('AI command execution is disabled');
|
|
396
|
+
}
|
|
289
397
|
this.validateCommand(args.command);
|
|
290
398
|
const { spawn } = await import('child_process');
|
|
291
|
-
const { program, args: cmdArgs } = this.parseCommand(args.command);
|
|
399
|
+
const { program: prog, args: cmdArgs } = this.parseCommand(args.command);
|
|
400
|
+
if (this.config.access.allowed_commands && this.config.access.allowed_commands.length > 0) {
|
|
401
|
+
if (!this.config.access.allowed_commands.includes(prog)) {
|
|
402
|
+
throw new Error(`Command '${prog}' is not in the allowed commands list`);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
292
405
|
const env = { ...process.env };
|
|
293
406
|
for (const name of args.variables) {
|
|
294
407
|
if (isBlacklisted(name, this.config)) {
|
|
@@ -299,16 +412,28 @@ export class BaseAdapter {
|
|
|
299
412
|
env[name] = variable.value;
|
|
300
413
|
}
|
|
301
414
|
}
|
|
415
|
+
const TIMEOUT_MS = 30000;
|
|
302
416
|
return new Promise((resolve) => {
|
|
303
|
-
const proc = spawn(
|
|
417
|
+
const proc = spawn(prog, cmdArgs, {
|
|
304
418
|
env,
|
|
305
419
|
cwd: this.projectPath,
|
|
306
420
|
});
|
|
307
421
|
let stdout = '';
|
|
308
422
|
let stderr = '';
|
|
423
|
+
let killed = false;
|
|
424
|
+
const timer = setTimeout(() => {
|
|
425
|
+
killed = true;
|
|
426
|
+
proc.kill('SIGTERM');
|
|
427
|
+
setTimeout(() => { if (!proc.killed)
|
|
428
|
+
proc.kill('SIGKILL'); }, 5000);
|
|
429
|
+
}, TIMEOUT_MS);
|
|
309
430
|
proc.stdout.on('data', (data) => { stdout += data; });
|
|
310
431
|
proc.stderr.on('data', (data) => { stderr += data; });
|
|
311
432
|
proc.on('close', (code) => {
|
|
433
|
+
clearTimeout(timer);
|
|
434
|
+
if (killed) {
|
|
435
|
+
stderr += '\n[Process killed: exceeded 30s timeout]';
|
|
436
|
+
}
|
|
312
437
|
resolve({ exitCode: code, stdout, stderr });
|
|
313
438
|
});
|
|
314
439
|
});
|