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