@fentz26/envcp 1.0.2 → 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 +3 -3
- package/dist/cli/index.js +382 -5
- 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 +77 -0
- package/dist/config/manager.js.map +1 -1
- package/dist/storage/index.d.ts +10 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +89 -6
- package/dist/storage/index.js.map +1 -1
- package/dist/types.d.ts +18 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -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/package.json +6 -1
- package/.github/workflows/publish.yml +0 -48
- package/__tests__/config.test.ts +0 -65
- package/__tests__/crypto.test.ts +0 -76
- package/__tests__/http.test.ts +0 -49
- package/__tests__/storage.test.ts +0 -94
- package/jest.config.js +0 -11
- package/src/adapters/base.ts +0 -542
- package/src/adapters/gemini.ts +0 -228
- package/src/adapters/index.ts +0 -4
- package/src/adapters/openai.ts +0 -238
- package/src/adapters/rest.ts +0 -298
- package/src/cli/index.ts +0 -516
- package/src/cli.ts +0 -2
- package/src/config/manager.ts +0 -137
- package/src/index.ts +0 -4
- package/src/mcp/index.ts +0 -1
- package/src/mcp/server.ts +0 -67
- package/src/server/index.ts +0 -1
- package/src/server/unified.ts +0 -474
- package/src/storage/index.ts +0 -128
- package/src/types.ts +0 -183
- package/src/utils/crypto.ts +0 -100
- package/src/utils/http.ts +0 -119
- package/src/utils/session.ts +0 -146
- package/tsconfig.json +0 -20
package/src/cli/index.ts
DELETED
|
@@ -1,516 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import inquirer from 'inquirer';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
import * as fs from 'fs-extra';
|
|
6
|
-
import { loadConfig, initConfig } from '../config/manager.js';
|
|
7
|
-
import { StorageManager } from '../storage/index.js';
|
|
8
|
-
import { SessionManager } from '../utils/session.js';
|
|
9
|
-
import { maskValue, validatePassword } from '../utils/crypto.js';
|
|
10
|
-
import { Variable, EnvCPConfig } from '../types.js';
|
|
11
|
-
|
|
12
|
-
async function withSession(fn: (storage: StorageManager, password: string, config: EnvCPConfig, projectPath: string) => Promise<void>): Promise<void> {
|
|
13
|
-
const projectPath = process.cwd();
|
|
14
|
-
const config = await loadConfig(projectPath);
|
|
15
|
-
|
|
16
|
-
const sessionManager = new SessionManager(
|
|
17
|
-
path.join(projectPath, config.session?.path || '.envcp/.session'),
|
|
18
|
-
config.session?.timeout_minutes || 30,
|
|
19
|
-
config.session?.max_extensions || 5
|
|
20
|
-
);
|
|
21
|
-
await sessionManager.init();
|
|
22
|
-
|
|
23
|
-
let session = await sessionManager.load();
|
|
24
|
-
let password = '';
|
|
25
|
-
|
|
26
|
-
if (!session) {
|
|
27
|
-
const answer = await inquirer.prompt([
|
|
28
|
-
{ type: 'password', name: 'password', message: 'Enter password:', mask: '*' }
|
|
29
|
-
]);
|
|
30
|
-
password = answer.password;
|
|
31
|
-
|
|
32
|
-
const validation = validatePassword(password, config.password || {});
|
|
33
|
-
if (!validation.valid) {
|
|
34
|
-
console.log(chalk.red(validation.error));
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
session = await sessionManager.create(password);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
password = sessionManager.getPassword() || password;
|
|
42
|
-
|
|
43
|
-
const storage = new StorageManager(
|
|
44
|
-
path.join(projectPath, config.storage.path),
|
|
45
|
-
config.storage.encrypted
|
|
46
|
-
);
|
|
47
|
-
if (password) storage.setPassword(password);
|
|
48
|
-
|
|
49
|
-
await fn(storage, password, config, projectPath);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const program = new Command();
|
|
53
|
-
|
|
54
|
-
program
|
|
55
|
-
.name('envcp')
|
|
56
|
-
.description('Secure environment variable management for AI-assisted coding')
|
|
57
|
-
.version('1.0.0');
|
|
58
|
-
|
|
59
|
-
program
|
|
60
|
-
.command('init')
|
|
61
|
-
.description('Initialize EnvCP in the current project')
|
|
62
|
-
.option('-p, --project <name>', 'Project name')
|
|
63
|
-
.option('-e, --encrypted', 'Enable encryption', true)
|
|
64
|
-
.action(async (options) => {
|
|
65
|
-
const projectPath = process.cwd();
|
|
66
|
-
const projectName = options.project || path.basename(projectPath);
|
|
67
|
-
|
|
68
|
-
console.log(chalk.blue('Initializing EnvCP...'));
|
|
69
|
-
|
|
70
|
-
const config = await initConfig(projectPath, projectName);
|
|
71
|
-
|
|
72
|
-
console.log(chalk.green('EnvCP initialized successfully!'));
|
|
73
|
-
console.log(chalk.gray(` Project: ${config.project}`));
|
|
74
|
-
console.log(chalk.gray(` Storage: ${config.storage.path}`));
|
|
75
|
-
console.log(chalk.gray(` Encrypted: ${config.storage.encrypted}`));
|
|
76
|
-
console.log(chalk.gray(` Session timeout: ${config.session?.timeout_minutes || 30} minutes`));
|
|
77
|
-
console.log(chalk.gray(` AI active check: ${config.access?.allow_ai_active_check ? 'enabled' : 'disabled'}`));
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
program
|
|
81
|
-
.command('unlock')
|
|
82
|
-
.description('Unlock EnvCP session with password')
|
|
83
|
-
.option('-p, --password <password>', 'Password (will prompt if not provided)')
|
|
84
|
-
.action(async (options) => {
|
|
85
|
-
const projectPath = process.cwd();
|
|
86
|
-
const config = await loadConfig(projectPath);
|
|
87
|
-
|
|
88
|
-
let password = options.password;
|
|
89
|
-
|
|
90
|
-
if (!password) {
|
|
91
|
-
const answer = await inquirer.prompt([
|
|
92
|
-
{
|
|
93
|
-
type: 'password',
|
|
94
|
-
name: 'password',
|
|
95
|
-
message: 'Enter password:',
|
|
96
|
-
mask: '*'
|
|
97
|
-
}
|
|
98
|
-
]);
|
|
99
|
-
password = answer.password;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const validation = validatePassword(password, config.password || {});
|
|
103
|
-
if (!validation.valid) {
|
|
104
|
-
console.log(chalk.red(validation.error));
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const sessionManager = new SessionManager(
|
|
109
|
-
path.join(projectPath, config.session?.path || '.envcp/.session'),
|
|
110
|
-
config.session?.timeout_minutes || 30,
|
|
111
|
-
config.session?.max_extensions || 5
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
await sessionManager.init();
|
|
115
|
-
|
|
116
|
-
const storage = new StorageManager(
|
|
117
|
-
path.join(projectPath, config.storage.path),
|
|
118
|
-
config.storage.encrypted
|
|
119
|
-
);
|
|
120
|
-
storage.setPassword(password);
|
|
121
|
-
|
|
122
|
-
const storeExists = await storage.exists();
|
|
123
|
-
|
|
124
|
-
if (!storeExists) {
|
|
125
|
-
const confirm = await inquirer.prompt([
|
|
126
|
-
{ type: 'password', name: 'password', message: 'Confirm password:', mask: '*' }
|
|
127
|
-
]);
|
|
128
|
-
if (confirm.password !== password) {
|
|
129
|
-
console.log(chalk.red('Passwords do not match'));
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
try {
|
|
135
|
-
await storage.load();
|
|
136
|
-
} catch (error) {
|
|
137
|
-
console.log(chalk.red('Invalid password'));
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const session = await sessionManager.create(password);
|
|
142
|
-
|
|
143
|
-
console.log(chalk.green('Session unlocked!'));
|
|
144
|
-
console.log(chalk.gray(` Session ID: ${session.id}`));
|
|
145
|
-
console.log(chalk.gray(` Expires in: ${config.session?.timeout_minutes || 30} minutes`));
|
|
146
|
-
const maxExt = config.session?.max_extensions || 5;
|
|
147
|
-
console.log(chalk.gray(` Extensions remaining: ${maxExt - session.extensions}/${maxExt}`));
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
program
|
|
151
|
-
.command('lock')
|
|
152
|
-
.description('Lock EnvCP session')
|
|
153
|
-
.action(async () => {
|
|
154
|
-
const projectPath = process.cwd();
|
|
155
|
-
const config = await loadConfig(projectPath);
|
|
156
|
-
|
|
157
|
-
const sessionManager = new SessionManager(
|
|
158
|
-
path.join(projectPath, config.session?.path || '.envcp/.session'),
|
|
159
|
-
config.session?.timeout_minutes || 30,
|
|
160
|
-
config.session?.max_extensions || 5
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
await sessionManager.init();
|
|
164
|
-
await sessionManager.destroy();
|
|
165
|
-
|
|
166
|
-
console.log(chalk.green('Session locked'));
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
program
|
|
170
|
-
.command('status')
|
|
171
|
-
.description('Check session status')
|
|
172
|
-
.action(async () => {
|
|
173
|
-
const projectPath = process.cwd();
|
|
174
|
-
const config = await loadConfig(projectPath);
|
|
175
|
-
|
|
176
|
-
const sessionManager = new SessionManager(
|
|
177
|
-
path.join(projectPath, config.session?.path || '.envcp/.session'),
|
|
178
|
-
config.session?.timeout_minutes || 30,
|
|
179
|
-
config.session?.max_extensions || 5
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
await sessionManager.init();
|
|
183
|
-
|
|
184
|
-
const answer = await inquirer.prompt([
|
|
185
|
-
{ type: 'password', name: 'password', message: 'Enter password:', mask: '*' }
|
|
186
|
-
]);
|
|
187
|
-
const session = await sessionManager.load(answer.password);
|
|
188
|
-
|
|
189
|
-
if (!session) {
|
|
190
|
-
console.log(chalk.yellow('No active session (expired, invalid password, or not unlocked)'));
|
|
191
|
-
console.log(chalk.gray('Run: envcp unlock'));
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const remaining = sessionManager.getRemainingTime();
|
|
196
|
-
const maxExt = config.session?.max_extensions || 5;
|
|
197
|
-
|
|
198
|
-
console.log(chalk.green('Session active'));
|
|
199
|
-
console.log(chalk.gray(` Session ID: ${session.id}`));
|
|
200
|
-
console.log(chalk.gray(` Remaining: ${remaining} minutes`));
|
|
201
|
-
console.log(chalk.gray(` Extensions remaining: ${maxExt - session.extensions}/${maxExt}`));
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
program
|
|
205
|
-
.command('extend')
|
|
206
|
-
.description('Extend session timeout')
|
|
207
|
-
.action(async () => {
|
|
208
|
-
const projectPath = process.cwd();
|
|
209
|
-
const config = await loadConfig(projectPath);
|
|
210
|
-
|
|
211
|
-
const sessionManager = new SessionManager(
|
|
212
|
-
path.join(projectPath, config.session?.path || '.envcp/.session'),
|
|
213
|
-
config.session?.timeout_minutes || 30,
|
|
214
|
-
config.session?.max_extensions || 5
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
await sessionManager.init();
|
|
218
|
-
|
|
219
|
-
const answer = await inquirer.prompt([
|
|
220
|
-
{ type: 'password', name: 'password', message: 'Enter password:', mask: '*' }
|
|
221
|
-
]);
|
|
222
|
-
const loaded = await sessionManager.load(answer.password);
|
|
223
|
-
|
|
224
|
-
if (!loaded) {
|
|
225
|
-
console.log(chalk.red('Cannot extend session. No active session or invalid password.'));
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const session = await sessionManager.extend();
|
|
230
|
-
|
|
231
|
-
if (!session) {
|
|
232
|
-
console.log(chalk.red('Cannot extend session. Session expired or max extensions reached.'));
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const maxExt = config.session?.max_extensions || 5;
|
|
237
|
-
|
|
238
|
-
console.log(chalk.green('Session extended!'));
|
|
239
|
-
console.log(chalk.gray(` Remaining: ${sessionManager.getRemainingTime()} minutes`));
|
|
240
|
-
console.log(chalk.gray(` Extensions remaining: ${maxExt - session.extensions}/${maxExt}`));
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
program
|
|
244
|
-
.command('add <name>')
|
|
245
|
-
.description('Add a new environment variable')
|
|
246
|
-
.option('-v, --value <value>', 'Variable value')
|
|
247
|
-
.option('-t, --tags <tags>', 'Tags (comma-separated)')
|
|
248
|
-
.option('-d, --description <desc>', 'Description')
|
|
249
|
-
.action(async (name, options) => {
|
|
250
|
-
await withSession(async (storage, _password, config) => {
|
|
251
|
-
let value = options.value;
|
|
252
|
-
let tags: string[] = [];
|
|
253
|
-
let description = options.description;
|
|
254
|
-
|
|
255
|
-
if (!value) {
|
|
256
|
-
const answers = await inquirer.prompt([
|
|
257
|
-
{ type: 'password', name: 'value', message: 'Enter value:', mask: '*' },
|
|
258
|
-
{ type: 'input', name: 'tags', message: 'Tags (comma-separated):' },
|
|
259
|
-
{ type: 'input', name: 'description', message: 'Description:' },
|
|
260
|
-
]);
|
|
261
|
-
value = answers.value;
|
|
262
|
-
tags = answers.tags.split(',').map((t: string) => t.trim()).filter(Boolean);
|
|
263
|
-
description = answers.description;
|
|
264
|
-
} else if (options.tags) {
|
|
265
|
-
tags = options.tags.split(',').map((t: string) => t.trim()).filter(Boolean);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const now = new Date().toISOString();
|
|
269
|
-
const variable: Variable = {
|
|
270
|
-
name,
|
|
271
|
-
value,
|
|
272
|
-
encrypted: config.storage.encrypted,
|
|
273
|
-
tags: tags.length > 0 ? tags : undefined,
|
|
274
|
-
description,
|
|
275
|
-
created: now,
|
|
276
|
-
updated: now,
|
|
277
|
-
sync_to_env: true,
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
await storage.set(name, variable);
|
|
281
|
-
console.log(chalk.green(`Variable '${name}' added successfully`));
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
program
|
|
286
|
-
.command('list')
|
|
287
|
-
.description('List all variables (names only, values hidden)')
|
|
288
|
-
.option('-v, --show-values', 'Show actual values')
|
|
289
|
-
.action(async (options) => {
|
|
290
|
-
await withSession(async (storage) => {
|
|
291
|
-
const variables = await storage.load();
|
|
292
|
-
const names = Object.keys(variables);
|
|
293
|
-
|
|
294
|
-
if (names.length === 0) {
|
|
295
|
-
console.log(chalk.yellow('No variables found'));
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
console.log(chalk.blue(`\nVariables (${names.length}):\n`));
|
|
300
|
-
|
|
301
|
-
for (const name of names) {
|
|
302
|
-
const v = variables[name];
|
|
303
|
-
const value = options.showValues ? v.value : maskValue(v.value);
|
|
304
|
-
const tags = v.tags ? chalk.gray(` [${v.tags.join(', ')}]`) : '';
|
|
305
|
-
console.log(` ${chalk.cyan(name)} = ${value}${tags}`);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
console.log('');
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
program
|
|
313
|
-
.command('get <name>')
|
|
314
|
-
.description('Get a variable value')
|
|
315
|
-
.action(async (name) => {
|
|
316
|
-
await withSession(async (storage) => {
|
|
317
|
-
const variable = await storage.get(name);
|
|
318
|
-
|
|
319
|
-
if (!variable) {
|
|
320
|
-
console.log(chalk.red(`Variable '${name}' not found`));
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
console.log(chalk.cyan(name));
|
|
325
|
-
console.log(` Value: ${variable.value}`);
|
|
326
|
-
if (variable.tags) console.log(` Tags: ${variable.tags.join(', ')}`);
|
|
327
|
-
if (variable.description) console.log(` Description: ${variable.description}`);
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
program
|
|
332
|
-
.command('remove <name>')
|
|
333
|
-
.description('Remove a variable')
|
|
334
|
-
.action(async (name) => {
|
|
335
|
-
await withSession(async (storage) => {
|
|
336
|
-
const deleted = await storage.delete(name);
|
|
337
|
-
|
|
338
|
-
if (deleted) {
|
|
339
|
-
console.log(chalk.green(`Variable '${name}' removed`));
|
|
340
|
-
} else {
|
|
341
|
-
console.log(chalk.red(`Variable '${name}' not found`));
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
program
|
|
347
|
-
.command('sync')
|
|
348
|
-
.description('Sync variables to .env file')
|
|
349
|
-
.action(async () => {
|
|
350
|
-
await withSession(async (storage, _password, config, projectPath) => {
|
|
351
|
-
if (!config.sync.enabled) {
|
|
352
|
-
console.log(chalk.yellow('Sync is disabled in configuration'));
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
const variables = await storage.load();
|
|
357
|
-
const lines: string[] = [];
|
|
358
|
-
|
|
359
|
-
if (config.sync.header) {
|
|
360
|
-
lines.push(config.sync.header);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
for (const [name, variable] of Object.entries(variables)) {
|
|
364
|
-
const needsQuoting = /[\s#"'\\]/.test(variable.value);
|
|
365
|
-
const val = needsQuoting ? `"${variable.value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"` : variable.value;
|
|
366
|
-
lines.push(`${name}=${val}`);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
await fs.writeFile(path.join(projectPath, config.sync.target), lines.join('\n'), 'utf8');
|
|
370
|
-
console.log(chalk.green(`Synced ${lines.length} variables to ${config.sync.target}`));
|
|
371
|
-
});
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
program
|
|
375
|
-
.command('serve')
|
|
376
|
-
.description('Start EnvCP server')
|
|
377
|
-
.option('-p, --password <password>', 'Encryption password')
|
|
378
|
-
.option('-m, --mode <mode>', 'Server mode: mcp, rest, openai, gemini, all, auto', 'auto')
|
|
379
|
-
.option('--port <port>', 'HTTP port (for non-MCP modes)', '3456')
|
|
380
|
-
.option('--host <host>', 'HTTP host', '127.0.0.1')
|
|
381
|
-
.option('-k, --api-key <key>', 'API key for HTTP authentication')
|
|
382
|
-
.action(async (options) => {
|
|
383
|
-
const projectPath = process.cwd();
|
|
384
|
-
const config = await loadConfig(projectPath);
|
|
385
|
-
|
|
386
|
-
const sessionManager = new SessionManager(
|
|
387
|
-
path.join(projectPath, config.session?.path || '.envcp/.session'),
|
|
388
|
-
config.session?.timeout_minutes || 30,
|
|
389
|
-
config.session?.max_extensions || 5
|
|
390
|
-
);
|
|
391
|
-
await sessionManager.init();
|
|
392
|
-
|
|
393
|
-
let session = await sessionManager.load();
|
|
394
|
-
let password = options.password;
|
|
395
|
-
|
|
396
|
-
if (!session && !password) {
|
|
397
|
-
const answer = await inquirer.prompt([
|
|
398
|
-
{ type: 'password', name: 'password', message: 'Enter password:', mask: '*' }
|
|
399
|
-
]);
|
|
400
|
-
password = answer.password;
|
|
401
|
-
|
|
402
|
-
const validation = validatePassword(password, config.password || {});
|
|
403
|
-
if (!validation.valid) {
|
|
404
|
-
console.log(chalk.red(validation.error));
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
session = await sessionManager.create(password);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
password = sessionManager.getPassword() || password;
|
|
412
|
-
|
|
413
|
-
const mode = options.mode as string;
|
|
414
|
-
const port = parseInt(options.port, 10);
|
|
415
|
-
const host = options.host;
|
|
416
|
-
const apiKey = options.apiKey;
|
|
417
|
-
|
|
418
|
-
// MCP mode uses stdio
|
|
419
|
-
if (mode === 'mcp') {
|
|
420
|
-
const { EnvCPServer } = await import('../mcp/server.js');
|
|
421
|
-
const server = new EnvCPServer(config, projectPath, password);
|
|
422
|
-
await server.start();
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// HTTP-based modes
|
|
427
|
-
const { UnifiedServer } = await import('../server/unified.js');
|
|
428
|
-
|
|
429
|
-
const serverConfig = {
|
|
430
|
-
mode: mode as 'mcp' | 'rest' | 'openai' | 'gemini' | 'all' | 'auto',
|
|
431
|
-
port,
|
|
432
|
-
host,
|
|
433
|
-
api_key: apiKey,
|
|
434
|
-
cors: true,
|
|
435
|
-
auto_detect: mode === 'auto',
|
|
436
|
-
};
|
|
437
|
-
|
|
438
|
-
const server = new UnifiedServer(config, serverConfig, projectPath, password);
|
|
439
|
-
|
|
440
|
-
console.log(chalk.blue('Starting EnvCP server...'));
|
|
441
|
-
console.log(chalk.gray(` Mode: ${mode}`));
|
|
442
|
-
console.log(chalk.gray(` Host: ${host}`));
|
|
443
|
-
console.log(chalk.gray(` Port: ${port}`));
|
|
444
|
-
if (apiKey) console.log(chalk.gray(` API Key: ${apiKey.substring(0, 4)}...`));
|
|
445
|
-
console.log('');
|
|
446
|
-
|
|
447
|
-
await server.start();
|
|
448
|
-
|
|
449
|
-
console.log(chalk.green(`EnvCP server running at http://${host}:${port}`));
|
|
450
|
-
console.log('');
|
|
451
|
-
console.log(chalk.blue('Available endpoints:'));
|
|
452
|
-
|
|
453
|
-
if (mode === 'auto' || mode === 'all') {
|
|
454
|
-
console.log(chalk.gray(' REST API: /api/*'));
|
|
455
|
-
console.log(chalk.gray(' OpenAI: /v1/chat/completions, /v1/functions/*'));
|
|
456
|
-
console.log(chalk.gray(' Gemini: /v1/models/envcp:generateContent'));
|
|
457
|
-
console.log('');
|
|
458
|
-
console.log(chalk.yellow('Auto-detection enabled: Server will detect client type from request headers'));
|
|
459
|
-
} else if (mode === 'rest') {
|
|
460
|
-
console.log(chalk.gray(' GET /api/variables - List variables'));
|
|
461
|
-
console.log(chalk.gray(' GET /api/variables/:name - Get variable'));
|
|
462
|
-
console.log(chalk.gray(' POST /api/variables - Create variable'));
|
|
463
|
-
console.log(chalk.gray(' PUT /api/variables/:name - Update variable'));
|
|
464
|
-
console.log(chalk.gray(' DELETE /api/variables/:name - Delete variable'));
|
|
465
|
-
console.log(chalk.gray(' POST /api/sync - Sync to .env'));
|
|
466
|
-
console.log(chalk.gray(' POST /api/tools/:name - Call tool'));
|
|
467
|
-
} else if (mode === 'openai') {
|
|
468
|
-
console.log(chalk.gray(' GET /v1/models - List models'));
|
|
469
|
-
console.log(chalk.gray(' GET /v1/functions - List functions'));
|
|
470
|
-
console.log(chalk.gray(' POST /v1/functions/call - Call function'));
|
|
471
|
-
console.log(chalk.gray(' POST /v1/tool_calls - Process tool calls'));
|
|
472
|
-
console.log(chalk.gray(' POST /v1/chat/completions - Chat completions'));
|
|
473
|
-
} else if (mode === 'gemini') {
|
|
474
|
-
console.log(chalk.gray(' GET /v1/models - List models'));
|
|
475
|
-
console.log(chalk.gray(' GET /v1/tools - List tools'));
|
|
476
|
-
console.log(chalk.gray(' POST /v1/functions/call - Call function'));
|
|
477
|
-
console.log(chalk.gray(' POST /v1/function_calls - Process function calls'));
|
|
478
|
-
console.log(chalk.gray(' POST /v1/models/envcp:generateContent'));
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
console.log('');
|
|
482
|
-
console.log(chalk.gray('Press Ctrl+C to stop'));
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
program
|
|
486
|
-
.command('export')
|
|
487
|
-
.description('Export variables')
|
|
488
|
-
.option('-f, --format <format>', 'Output format: env, json, yaml', 'env')
|
|
489
|
-
.action(async (options) => {
|
|
490
|
-
await withSession(async (storage) => {
|
|
491
|
-
const variables = await storage.load();
|
|
492
|
-
|
|
493
|
-
let output: string;
|
|
494
|
-
|
|
495
|
-
switch (options.format) {
|
|
496
|
-
case 'json':
|
|
497
|
-
output = JSON.stringify(variables, null, 2);
|
|
498
|
-
break;
|
|
499
|
-
case 'yaml':
|
|
500
|
-
const yaml = await import('js-yaml');
|
|
501
|
-
output = yaml.dump(variables);
|
|
502
|
-
break;
|
|
503
|
-
default:
|
|
504
|
-
const lines = Object.entries(variables).map(([k, v]) => {
|
|
505
|
-
const needsQuoting = /[\s#"'\\]/.test(v.value);
|
|
506
|
-
const val = needsQuoting ? `"${v.value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"` : v.value;
|
|
507
|
-
return `${k}=${val}`;
|
|
508
|
-
});
|
|
509
|
-
output = lines.join('\n');
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
console.log(output);
|
|
513
|
-
});
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
program.parse();
|
package/src/cli.ts
DELETED
package/src/config/manager.ts
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs-extra';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import * as yaml from 'js-yaml';
|
|
4
|
-
import { EnvCPConfig, EnvCPConfigSchema } from '../types.js';
|
|
5
|
-
|
|
6
|
-
const DEFAULT_CONFIG: Partial<EnvCPConfig> = {
|
|
7
|
-
version: '1.0',
|
|
8
|
-
storage: {
|
|
9
|
-
path: '.envcp/store.enc',
|
|
10
|
-
encrypted: true,
|
|
11
|
-
algorithm: 'aes-256-gcm',
|
|
12
|
-
compression: false,
|
|
13
|
-
},
|
|
14
|
-
access: {
|
|
15
|
-
allow_ai_read: false,
|
|
16
|
-
allow_ai_write: false,
|
|
17
|
-
allow_ai_delete: false,
|
|
18
|
-
allow_ai_export: false,
|
|
19
|
-
allow_ai_execute: false,
|
|
20
|
-
allow_ai_active_check: false,
|
|
21
|
-
require_user_reference: true,
|
|
22
|
-
allowed_commands: undefined,
|
|
23
|
-
require_confirmation: true,
|
|
24
|
-
mask_values: true,
|
|
25
|
-
audit_log: true,
|
|
26
|
-
blacklist_patterns: ['*_SECRET', '*_PRIVATE', 'ADMIN_*', 'ROOT_*'],
|
|
27
|
-
},
|
|
28
|
-
sync: {
|
|
29
|
-
enabled: false,
|
|
30
|
-
target: '.env',
|
|
31
|
-
exclude: [],
|
|
32
|
-
format: 'dotenv',
|
|
33
|
-
},
|
|
34
|
-
session: {
|
|
35
|
-
enabled: true,
|
|
36
|
-
timeout_minutes: 30,
|
|
37
|
-
max_extensions: 5,
|
|
38
|
-
path: '.envcp/.session',
|
|
39
|
-
},
|
|
40
|
-
password: {
|
|
41
|
-
min_length: 1,
|
|
42
|
-
require_complexity: false,
|
|
43
|
-
allow_numeric_only: true,
|
|
44
|
-
allow_single_char: true,
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export async function loadConfig(projectPath: string): Promise<EnvCPConfig> {
|
|
49
|
-
const configPath = path.join(projectPath, 'envcp.yaml');
|
|
50
|
-
|
|
51
|
-
if (await fs.pathExists(configPath)) {
|
|
52
|
-
const content = await fs.readFile(configPath, 'utf8');
|
|
53
|
-
const parsed = yaml.load(content);
|
|
54
|
-
const config = EnvCPConfigSchema.parse(parsed);
|
|
55
|
-
return config;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return EnvCPConfigSchema.parse(DEFAULT_CONFIG);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export async function saveConfig(config: EnvCPConfig, projectPath: string): Promise<void> {
|
|
62
|
-
const configPath = path.join(projectPath, 'envcp.yaml');
|
|
63
|
-
const content = yaml.dump(config, { indent: 2, lineWidth: -1 });
|
|
64
|
-
await fs.writeFile(configPath, content, 'utf8');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export async function initConfig(projectPath: string, projectName?: string): Promise<EnvCPConfig> {
|
|
68
|
-
const envcpDir = path.join(projectPath, '.envcp');
|
|
69
|
-
await fs.ensureDir(envcpDir);
|
|
70
|
-
await fs.ensureDir(path.join(envcpDir, 'logs'));
|
|
71
|
-
|
|
72
|
-
const config: EnvCPConfig = {
|
|
73
|
-
...DEFAULT_CONFIG,
|
|
74
|
-
project: projectName || path.basename(projectPath),
|
|
75
|
-
} as EnvCPConfig;
|
|
76
|
-
|
|
77
|
-
await saveConfig(config, projectPath);
|
|
78
|
-
|
|
79
|
-
const gitignorePath = path.join(projectPath, '.gitignore');
|
|
80
|
-
if (await fs.pathExists(gitignorePath)) {
|
|
81
|
-
const gitignore = await fs.readFile(gitignorePath, 'utf8');
|
|
82
|
-
if (!gitignore.includes('.envcp/')) {
|
|
83
|
-
await fs.appendFile(gitignorePath, '\n# EnvCP\n.envcp/\nstore.enc\n');
|
|
84
|
-
}
|
|
85
|
-
} else {
|
|
86
|
-
await fs.writeFile(gitignorePath, '# EnvCP\n.envcp/\nstore.enc\n');
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return config;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function validateVariableName(name: string): boolean {
|
|
93
|
-
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function matchesPattern(name: string, pattern: string): boolean {
|
|
97
|
-
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
|
|
98
|
-
const regex = new RegExp('^' + escaped.replace(/\*/g, '.*') + '$');
|
|
99
|
-
return regex.test(name);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export function canAccess(name: string, config: EnvCPConfig): boolean {
|
|
103
|
-
if (config.access.blacklist_patterns && config.access.blacklist_patterns.length > 0) {
|
|
104
|
-
if (config.access.blacklist_patterns.some((p: string) => matchesPattern(name, p))) {
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (config.access.denied_patterns && config.access.denied_patterns.length > 0) {
|
|
110
|
-
if (config.access.denied_patterns.some((p: string) => matchesPattern(name, p))) {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (config.access.allowed_patterns && config.access.allowed_patterns.length > 0) {
|
|
116
|
-
if (!config.access.allowed_patterns.some((p: string) => matchesPattern(name, p))) {
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export function isBlacklisted(name: string, config: EnvCPConfig): boolean {
|
|
125
|
-
if (config.access.blacklist_patterns && config.access.blacklist_patterns.length > 0) {
|
|
126
|
-
return config.access.blacklist_patterns.some((p: string) => matchesPattern(name, p));
|
|
127
|
-
}
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export function canAIActiveCheck(config: EnvCPConfig): boolean {
|
|
132
|
-
return config.access.allow_ai_active_check === true;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export function requiresUserReference(config: EnvCPConfig): boolean {
|
|
136
|
-
return config.access.require_user_reference === true;
|
|
137
|
-
}
|
package/src/index.ts
DELETED
package/src/mcp/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { EnvCPServer } from './server.js';
|