@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.
Files changed (71) hide show
  1. package/README.md +82 -133
  2. package/dist/adapters/base.d.ts +1 -2
  3. package/dist/adapters/base.d.ts.map +1 -1
  4. package/dist/adapters/base.js +139 -14
  5. package/dist/adapters/base.js.map +1 -1
  6. package/dist/adapters/gemini.d.ts +1 -0
  7. package/dist/adapters/gemini.d.ts.map +1 -1
  8. package/dist/adapters/gemini.js +13 -99
  9. package/dist/adapters/gemini.js.map +1 -1
  10. package/dist/adapters/openai.d.ts +1 -0
  11. package/dist/adapters/openai.d.ts.map +1 -1
  12. package/dist/adapters/openai.js +13 -99
  13. package/dist/adapters/openai.js.map +1 -1
  14. package/dist/adapters/rest.d.ts +1 -0
  15. package/dist/adapters/rest.d.ts.map +1 -1
  16. package/dist/adapters/rest.js +16 -13
  17. package/dist/adapters/rest.js.map +1 -1
  18. package/dist/cli/index.js +510 -197
  19. package/dist/cli/index.js.map +1 -1
  20. package/dist/config/manager.d.ts +6 -0
  21. package/dist/config/manager.d.ts.map +1 -1
  22. package/dist/config/manager.js +81 -1
  23. package/dist/config/manager.js.map +1 -1
  24. package/dist/mcp/server.d.ts +1 -16
  25. package/dist/mcp/server.d.ts.map +1 -1
  26. package/dist/mcp/server.js +23 -511
  27. package/dist/mcp/server.js.map +1 -1
  28. package/dist/server/unified.d.ts +1 -0
  29. package/dist/server/unified.d.ts.map +1 -1
  30. package/dist/server/unified.js +31 -19
  31. package/dist/server/unified.js.map +1 -1
  32. package/dist/storage/index.d.ts +12 -1
  33. package/dist/storage/index.d.ts.map +1 -1
  34. package/dist/storage/index.js +107 -10
  35. package/dist/storage/index.js.map +1 -1
  36. package/dist/types.d.ts +28 -0
  37. package/dist/types.d.ts.map +1 -1
  38. package/dist/types.js +6 -0
  39. package/dist/types.js.map +1 -1
  40. package/dist/utils/crypto.d.ts +3 -0
  41. package/dist/utils/crypto.d.ts.map +1 -1
  42. package/dist/utils/crypto.js +12 -0
  43. package/dist/utils/crypto.js.map +1 -1
  44. package/dist/utils/http.d.ts +13 -1
  45. package/dist/utils/http.d.ts.map +1 -1
  46. package/dist/utils/http.js +65 -2
  47. package/dist/utils/http.js.map +1 -1
  48. package/dist/utils/session.d.ts.map +1 -1
  49. package/dist/utils/session.js +8 -3
  50. package/dist/utils/session.js.map +1 -1
  51. package/package.json +9 -3
  52. package/.github/workflows/publish.yml +0 -48
  53. package/src/adapters/base.ts +0 -411
  54. package/src/adapters/gemini.ts +0 -314
  55. package/src/adapters/index.ts +0 -4
  56. package/src/adapters/openai.ts +0 -324
  57. package/src/adapters/rest.ts +0 -294
  58. package/src/cli/index.ts +0 -640
  59. package/src/cli.ts +0 -2
  60. package/src/config/manager.ts +0 -134
  61. package/src/index.ts +0 -4
  62. package/src/mcp/index.ts +0 -1
  63. package/src/mcp/server.ts +0 -623
  64. package/src/server/index.ts +0 -1
  65. package/src/server/unified.ts +0 -460
  66. package/src/storage/index.ts +0 -112
  67. package/src/types.ts +0 -181
  68. package/src/utils/crypto.ts +0 -100
  69. package/src/utils/http.ts +0 -45
  70. package/src/utils/session.ts +0 -141
  71. package/tsconfig.json +0 -20
@@ -1,411 +0,0 @@
1
- import { StorageManager, LogManager } from '../storage/index.js';
2
- import { EnvCPConfig, Variable, ToolDefinition } from '../types.js';
3
- import { maskValue } from '../utils/crypto.js';
4
- import { canAccess, isBlacklisted, canAIActiveCheck, requiresUserReference } from '../config/manager.js';
5
- import { SessionManager } from '../utils/session.js';
6
- import * as fs from 'fs-extra';
7
- import * as path from 'path';
8
- import * as dotenv from 'dotenv';
9
-
10
- export abstract class BaseAdapter {
11
- protected storage: StorageManager;
12
- protected logs: LogManager;
13
- protected sessionManager: SessionManager;
14
- protected config: EnvCPConfig;
15
- protected projectPath: string;
16
- protected tools: Map<string, ToolDefinition>;
17
-
18
- constructor(config: EnvCPConfig, projectPath: string, password?: string) {
19
- this.config = config;
20
- this.projectPath = projectPath;
21
- this.storage = new StorageManager(
22
- path.join(projectPath, config.storage.path),
23
- config.storage.encrypted
24
- );
25
-
26
- this.sessionManager = new SessionManager(
27
- path.join(projectPath, config.session?.path || '.envcp/.session'),
28
- config.session?.timeout_minutes || 30,
29
- config.session?.max_extensions || 5
30
- );
31
-
32
- if (password) {
33
- this.storage.setPassword(password);
34
- }
35
-
36
- this.logs = new LogManager(path.join(projectPath, '.envcp', 'logs'));
37
- this.tools = new Map();
38
- this.registerTools();
39
- }
40
-
41
- protected abstract registerTools(): void;
42
-
43
- async init(): Promise<void> {
44
- await this.logs.init();
45
- await this.sessionManager.init();
46
- }
47
-
48
- protected async ensurePassword(): Promise<void> {
49
- const pwd = this.sessionManager.getPassword();
50
- if (pwd && await this.sessionManager.isValid()) {
51
- this.storage.setPassword(pwd);
52
- return;
53
- }
54
- throw new Error('Session locked. Please unlock first using: envcp unlock');
55
- }
56
-
57
- getToolDefinitions(): ToolDefinition[] {
58
- return Array.from(this.tools.values());
59
- }
60
-
61
- async callTool(name: string, params: Record<string, unknown>): Promise<unknown> {
62
- const tool = this.tools.get(name);
63
- if (!tool) {
64
- throw new Error(`Unknown tool: ${name}`);
65
- }
66
- await this.ensurePassword();
67
- return tool.handler(params);
68
- }
69
-
70
- // Shared tool implementations
71
- protected async listVariables(args: { tags?: string[] }): Promise<{ variables: string[]; count: number }> {
72
- if (!this.config.access.allow_ai_read) {
73
- throw new Error('AI read access is disabled');
74
- }
75
-
76
- if (!canAIActiveCheck(this.config)) {
77
- throw new Error('AI active check is disabled. User must explicitly mention variable names.');
78
- }
79
-
80
- const names = await this.storage.list();
81
- let filtered = names.filter(n => canAccess(n, this.config) && !isBlacklisted(n, this.config));
82
-
83
- if (args.tags && args.tags.length > 0) {
84
- const variables = await this.storage.load();
85
- filtered = filtered.filter(name => {
86
- const v = variables[name];
87
- return v.tags && args.tags!.some(t => v.tags!.includes(t));
88
- });
89
- }
90
-
91
- await this.logs.log({
92
- timestamp: new Date().toISOString(),
93
- operation: 'list',
94
- source: 'api',
95
- success: true,
96
- message: `Listed ${filtered.length} variables`,
97
- });
98
-
99
- return { variables: filtered, count: filtered.length };
100
- }
101
-
102
- protected async getVariable(args: { name: string; show_value?: boolean }): Promise<{
103
- name: string;
104
- value: string;
105
- tags?: string[];
106
- description?: string;
107
- encrypted: boolean;
108
- }> {
109
- if (!this.config.access.allow_ai_read) {
110
- throw new Error('AI read access is disabled');
111
- }
112
-
113
- const variable = await this.storage.get(args.name);
114
-
115
- if (!variable) {
116
- throw new Error(`Variable '${args.name}' not found`);
117
- }
118
-
119
- if (isBlacklisted(args.name, this.config)) {
120
- throw new Error(`Variable '${args.name}' is blacklisted and cannot be accessed`);
121
- }
122
-
123
- if (!canAccess(args.name, this.config)) {
124
- throw new Error(`Access denied to variable '${args.name}'`);
125
- }
126
-
127
- variable.accessed = new Date().toISOString();
128
- await this.storage.set(args.name, variable);
129
-
130
- const canReveal = args.show_value && !this.config.access.mask_values && !this.config.access.require_confirmation;
131
- const value = canReveal ? variable.value : maskValue(variable.value);
132
-
133
- await this.logs.log({
134
- timestamp: new Date().toISOString(),
135
- operation: 'get',
136
- variable: args.name,
137
- source: 'api',
138
- success: true,
139
- message: canReveal ? 'Value revealed' : 'Value masked',
140
- });
141
-
142
- return {
143
- name: variable.name,
144
- value: value,
145
- tags: variable.tags,
146
- description: variable.description,
147
- encrypted: variable.encrypted,
148
- };
149
- }
150
-
151
- protected async setVariable(args: {
152
- name: string;
153
- value: string;
154
- tags?: string[];
155
- description?: string;
156
- }): Promise<{ success: boolean; message: string }> {
157
- if (!this.config.access.allow_ai_write) {
158
- throw new Error('AI write access is disabled');
159
- }
160
-
161
- if (isBlacklisted(args.name, this.config)) {
162
- throw new Error(`Variable '${args.name}' is blacklisted`);
163
- }
164
-
165
- const existing = await this.storage.get(args.name);
166
- const now = new Date().toISOString();
167
-
168
- const variable: Variable = {
169
- name: args.name,
170
- value: args.value,
171
- encrypted: this.config.storage.encrypted,
172
- tags: args.tags,
173
- description: args.description,
174
- created: existing?.created || now,
175
- updated: now,
176
- sync_to_env: true,
177
- };
178
-
179
- await this.storage.set(args.name, variable);
180
-
181
- await this.logs.log({
182
- timestamp: new Date().toISOString(),
183
- operation: existing ? 'update' : 'add',
184
- variable: args.name,
185
- source: 'api',
186
- success: true,
187
- message: `Variable ${existing ? 'updated' : 'created'}`,
188
- });
189
-
190
- return { success: true, message: `Variable '${args.name}' ${existing ? 'updated' : 'created'}` };
191
- }
192
-
193
- protected async deleteVariable(args: { name: string }): Promise<{ success: boolean; message: string }> {
194
- if (!this.config.access.allow_ai_delete) {
195
- throw new Error('AI delete access is disabled');
196
- }
197
-
198
- const deleted = await this.storage.delete(args.name);
199
-
200
- await this.logs.log({
201
- timestamp: new Date().toISOString(),
202
- operation: 'delete',
203
- variable: args.name,
204
- source: 'api',
205
- success: deleted,
206
- message: deleted ? 'Variable deleted' : 'Variable not found',
207
- });
208
-
209
- return {
210
- success: deleted,
211
- message: deleted ? `Variable '${args.name}' deleted` : `Variable '${args.name}' not found`
212
- };
213
- }
214
-
215
- protected async syncToEnv(): Promise<{ success: boolean; message: string }> {
216
- if (!this.config.access.allow_ai_export) {
217
- throw new Error('AI export access is disabled');
218
- }
219
-
220
- if (!this.config.sync.enabled) {
221
- throw new Error('Sync is disabled in configuration');
222
- }
223
-
224
- const variables = await this.storage.load();
225
- const lines: string[] = [];
226
-
227
- if (this.config.sync.header) {
228
- lines.push(this.config.sync.header);
229
- }
230
-
231
- for (const [name, variable] of Object.entries(variables)) {
232
- if (isBlacklisted(name, this.config)) {
233
- continue;
234
- }
235
-
236
- const excluded = this.config.sync.exclude?.some(pattern => {
237
- const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
238
- return regex.test(name);
239
- });
240
-
241
- if (excluded || !variable.sync_to_env) {
242
- continue;
243
- }
244
-
245
- lines.push(`${name}=${variable.value}`);
246
- }
247
-
248
- const envPath = path.join(this.projectPath, this.config.sync.target);
249
- await fs.writeFile(envPath, lines.join('\n'), 'utf8');
250
-
251
- await this.logs.log({
252
- timestamp: new Date().toISOString(),
253
- operation: 'sync',
254
- source: 'api',
255
- success: true,
256
- message: `Synced ${lines.length} variables to ${this.config.sync.target}`,
257
- });
258
-
259
- return { success: true, message: `Synced ${lines.length} variables to ${this.config.sync.target}` };
260
- }
261
-
262
- protected async addToEnv(args: { name: string; env_file?: string }): Promise<{ success: boolean; message: string }> {
263
- const variable = await this.storage.get(args.name);
264
-
265
- if (!variable) {
266
- throw new Error(`Variable '${args.name}' not found`);
267
- }
268
-
269
- if (isBlacklisted(args.name, this.config)) {
270
- throw new Error(`Variable '${args.name}' is blacklisted`);
271
- }
272
-
273
- const envPath = path.join(this.projectPath, args.env_file || '.env');
274
- let content = '';
275
-
276
- if (await fs.pathExists(envPath)) {
277
- content = await fs.readFile(envPath, 'utf8');
278
- }
279
-
280
- const envVars = dotenv.parse(content);
281
-
282
- if (envVars[args.name]) {
283
- const lines = content.split('\n');
284
- const newLines = lines.map(line => {
285
- if (line.startsWith(`${args.name}=`)) {
286
- return `${args.name}=${variable.value}`;
287
- }
288
- return line;
289
- });
290
- content = newLines.join('\n');
291
- } else {
292
- content += `\n${args.name}=${variable.value}`;
293
- }
294
-
295
- await fs.writeFile(envPath, content, 'utf8');
296
-
297
- await this.logs.log({
298
- timestamp: new Date().toISOString(),
299
- operation: 'add',
300
- variable: args.name,
301
- source: 'api',
302
- success: true,
303
- message: `Added to ${args.env_file || '.env'}`,
304
- });
305
-
306
- return { success: true, message: `Variable '${args.name}' added to ${args.env_file || '.env'}` };
307
- }
308
-
309
- protected async checkAccess(args: { name: string }): Promise<{
310
- name: string;
311
- exists: boolean;
312
- accessible: boolean;
313
- blacklisted: boolean;
314
- message: string;
315
- }> {
316
- const variable = await this.storage.get(args.name);
317
- const exists = !!variable;
318
- const blacklisted = isBlacklisted(args.name, this.config);
319
- const accessible = exists && !blacklisted && canAccess(args.name, this.config);
320
-
321
- await this.logs.log({
322
- timestamp: new Date().toISOString(),
323
- operation: 'check_access',
324
- variable: args.name,
325
- source: 'api',
326
- success: true,
327
- message: `Access check: ${accessible ? 'granted' : 'denied'}`,
328
- });
329
-
330
- return {
331
- name: args.name,
332
- exists,
333
- accessible,
334
- blacklisted,
335
- message: accessible ? 'Variable exists and can be accessed' : 'Variable cannot be accessed or does not exist',
336
- };
337
- }
338
-
339
- private parseCommand(command: string): { program: string; args: string[] } {
340
- const tokens: string[] = [];
341
- let current = '';
342
- let inSingle = false;
343
- let inDouble = false;
344
-
345
- for (let i = 0; i < command.length; i++) {
346
- const ch = command[i];
347
- if (ch === "'" && !inDouble) {
348
- inSingle = !inSingle;
349
- } else if (ch === '"' && !inSingle) {
350
- inDouble = !inDouble;
351
- } else if (ch === ' ' && !inSingle && !inDouble) {
352
- if (current.length > 0) {
353
- tokens.push(current);
354
- current = '';
355
- }
356
- } else {
357
- current += ch;
358
- }
359
- }
360
- if (current.length > 0) tokens.push(current);
361
-
362
- if (tokens.length === 0) throw new Error('Empty command');
363
- return { program: tokens[0], args: tokens.slice(1) };
364
- }
365
-
366
- private validateCommand(command: string): void {
367
- const shellMetachars = /[;&|`$(){}!><\n\\]/;
368
- if (shellMetachars.test(command)) {
369
- throw new Error('Command contains disallowed shell metacharacters: ; & | ` $ ( ) { } ! > < \\');
370
- }
371
- }
372
-
373
- protected async runCommand(args: { command: string; variables: string[] }): Promise<{
374
- exitCode: number | null;
375
- stdout: string;
376
- stderr: string;
377
- }> {
378
- this.validateCommand(args.command);
379
-
380
- const { spawn } = await import('child_process');
381
- const { program, args: cmdArgs } = this.parseCommand(args.command);
382
- const env: Record<string, string> = { ...process.env } as Record<string, string>;
383
-
384
- for (const name of args.variables) {
385
- if (isBlacklisted(name, this.config)) {
386
- continue;
387
- }
388
- const variable = await this.storage.get(name);
389
- if (variable) {
390
- env[name] = variable.value;
391
- }
392
- }
393
-
394
- return new Promise((resolve) => {
395
- const proc = spawn(program, cmdArgs, {
396
- env,
397
- cwd: this.projectPath,
398
- });
399
-
400
- let stdout = '';
401
- let stderr = '';
402
-
403
- proc.stdout.on('data', (data) => { stdout += data; });
404
- proc.stderr.on('data', (data) => { stderr += data; });
405
-
406
- proc.on('close', (code) => {
407
- resolve({ exitCode: code, stdout, stderr });
408
- });
409
- });
410
- }
411
- }