@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.
Files changed (45) hide show
  1. package/README.md +3 -3
  2. package/dist/cli/index.js +382 -5
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/config/manager.d.ts +6 -0
  5. package/dist/config/manager.d.ts.map +1 -1
  6. package/dist/config/manager.js +77 -0
  7. package/dist/config/manager.js.map +1 -1
  8. package/dist/storage/index.d.ts +10 -1
  9. package/dist/storage/index.d.ts.map +1 -1
  10. package/dist/storage/index.js +89 -6
  11. package/dist/storage/index.js.map +1 -1
  12. package/dist/types.d.ts +18 -0
  13. package/dist/types.d.ts.map +1 -1
  14. package/dist/types.js +4 -0
  15. package/dist/types.js.map +1 -1
  16. package/dist/utils/crypto.d.ts +3 -0
  17. package/dist/utils/crypto.d.ts.map +1 -1
  18. package/dist/utils/crypto.js +12 -0
  19. package/dist/utils/crypto.js.map +1 -1
  20. package/package.json +6 -1
  21. package/.github/workflows/publish.yml +0 -48
  22. package/__tests__/config.test.ts +0 -65
  23. package/__tests__/crypto.test.ts +0 -76
  24. package/__tests__/http.test.ts +0 -49
  25. package/__tests__/storage.test.ts +0 -94
  26. package/jest.config.js +0 -11
  27. package/src/adapters/base.ts +0 -542
  28. package/src/adapters/gemini.ts +0 -228
  29. package/src/adapters/index.ts +0 -4
  30. package/src/adapters/openai.ts +0 -238
  31. package/src/adapters/rest.ts +0 -298
  32. package/src/cli/index.ts +0 -516
  33. package/src/cli.ts +0 -2
  34. package/src/config/manager.ts +0 -137
  35. package/src/index.ts +0 -4
  36. package/src/mcp/index.ts +0 -1
  37. package/src/mcp/server.ts +0 -67
  38. package/src/server/index.ts +0 -1
  39. package/src/server/unified.ts +0 -474
  40. package/src/storage/index.ts +0 -128
  41. package/src/types.ts +0 -183
  42. package/src/utils/crypto.ts +0 -100
  43. package/src/utils/http.ts +0 -119
  44. package/src/utils/session.ts +0 -146
  45. package/tsconfig.json +0 -20
@@ -1,76 +0,0 @@
1
- import { encrypt, decrypt, maskValue, validatePassword, quickHash, generateId, generateSessionToken } from '../src/utils/crypto';
2
-
3
- describe('encrypt/decrypt', () => {
4
- it('round-trips correctly', () => {
5
- const text = 'hello world secret';
6
- const password = 'test-password-123';
7
- const encrypted = encrypt(text, password);
8
- expect(encrypted).not.toBe(text);
9
- expect(decrypt(encrypted, password)).toBe(text);
10
- });
11
-
12
- it('fails with wrong password', () => {
13
- const encrypted = encrypt('secret', 'correct');
14
- expect(() => decrypt(encrypted, 'wrong')).toThrow();
15
- });
16
-
17
- it('produces different ciphertext each time (random salt/iv)', () => {
18
- const text = 'same text';
19
- const password = 'same password';
20
- const a = encrypt(text, password);
21
- const b = encrypt(text, password);
22
- expect(a).not.toBe(b);
23
- });
24
- });
25
-
26
- describe('maskValue', () => {
27
- it('fully masks short values', () => {
28
- expect(maskValue('ab')).toBe('**');
29
- expect(maskValue('abcdefgh')).toBe('********');
30
- });
31
-
32
- it('shows prefix/suffix for longer values', () => {
33
- const result = maskValue('abcdefghijklmnop');
34
- expect(result.startsWith('abcd')).toBe(true);
35
- expect(result.endsWith('mnop')).toBe(true);
36
- expect(result).toContain('*');
37
- });
38
- });
39
-
40
- describe('validatePassword', () => {
41
- it('accepts valid password with defaults', () => {
42
- expect(validatePassword('a', {}).valid).toBe(true);
43
- });
44
-
45
- it('rejects too short password', () => {
46
- expect(validatePassword('', { min_length: 1 }).valid).toBe(false);
47
- });
48
-
49
- it('rejects single char when disallowed', () => {
50
- expect(validatePassword('a', { allow_single_char: false }).valid).toBe(false);
51
- });
52
-
53
- it('rejects numeric-only when disallowed', () => {
54
- expect(validatePassword('1234', { allow_numeric_only: false }).valid).toBe(false);
55
- });
56
-
57
- it('enforces complexity', () => {
58
- expect(validatePassword('abc', { require_complexity: true }).valid).toBe(false);
59
- expect(validatePassword('Abc123!', { require_complexity: true }).valid).toBe(true);
60
- });
61
- });
62
-
63
- describe('helpers', () => {
64
- it('generateId returns 32-char hex', () => {
65
- expect(generateId()).toMatch(/^[a-f0-9]{32}$/);
66
- });
67
-
68
- it('generateSessionToken returns 64-char hex', () => {
69
- expect(generateSessionToken()).toMatch(/^[a-f0-9]{64}$/);
70
- });
71
-
72
- it('quickHash returns 16-char hex', () => {
73
- expect(quickHash('test')).toMatch(/^[a-f0-9]{16}$/);
74
- expect(quickHash('test')).toBe(quickHash('test'));
75
- });
76
- });
@@ -1,49 +0,0 @@
1
- import { validateApiKey, RateLimiter } from '../src/utils/http';
2
-
3
- describe('validateApiKey', () => {
4
- it('returns false for undefined', () => {
5
- expect(validateApiKey(undefined, 'expected')).toBe(false);
6
- });
7
-
8
- it('returns false for wrong length', () => {
9
- expect(validateApiKey('short', 'expected-key')).toBe(false);
10
- });
11
-
12
- it('returns false for wrong key', () => {
13
- expect(validateApiKey('wrong-key-xx', 'expected-key')).toBe(false);
14
- });
15
-
16
- it('returns true for correct key', () => {
17
- expect(validateApiKey('my-secret-key', 'my-secret-key')).toBe(true);
18
- });
19
- });
20
-
21
- describe('RateLimiter', () => {
22
- it('allows requests within limit', () => {
23
- const limiter = new RateLimiter(3, 60000);
24
- expect(limiter.isAllowed('ip1')).toBe(true);
25
- expect(limiter.isAllowed('ip1')).toBe(true);
26
- expect(limiter.isAllowed('ip1')).toBe(true);
27
- });
28
-
29
- it('blocks requests over limit', () => {
30
- const limiter = new RateLimiter(2, 60000);
31
- expect(limiter.isAllowed('ip1')).toBe(true);
32
- expect(limiter.isAllowed('ip1')).toBe(true);
33
- expect(limiter.isAllowed('ip1')).toBe(false);
34
- });
35
-
36
- it('tracks IPs independently', () => {
37
- const limiter = new RateLimiter(1, 60000);
38
- expect(limiter.isAllowed('ip1')).toBe(true);
39
- expect(limiter.isAllowed('ip2')).toBe(true);
40
- expect(limiter.isAllowed('ip1')).toBe(false);
41
- });
42
-
43
- it('reports remaining requests', () => {
44
- const limiter = new RateLimiter(3, 60000);
45
- expect(limiter.getRemainingRequests('ip1')).toBe(3);
46
- limiter.isAllowed('ip1');
47
- expect(limiter.getRemainingRequests('ip1')).toBe(2);
48
- });
49
- });
@@ -1,94 +0,0 @@
1
- import * as fs from 'fs-extra';
2
- import * as path from 'path';
3
- import * as os from 'os';
4
- import { StorageManager } from '../src/storage/index';
5
-
6
- describe('StorageManager', () => {
7
- let tmpDir: string;
8
- let storePath: string;
9
-
10
- beforeEach(async () => {
11
- tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'envcp-test-'));
12
- storePath = path.join(tmpDir, 'store.enc');
13
- });
14
-
15
- afterEach(async () => {
16
- await fs.remove(tmpDir);
17
- });
18
-
19
- it('stores and retrieves variables (encrypted)', async () => {
20
- const storage = new StorageManager(storePath, true);
21
- storage.setPassword('test123');
22
-
23
- await storage.set('API_KEY', {
24
- name: 'API_KEY',
25
- value: 'secret-value',
26
- encrypted: true,
27
- created: new Date().toISOString(),
28
- updated: new Date().toISOString(),
29
- sync_to_env: true,
30
- });
31
-
32
- const result = await storage.get('API_KEY');
33
- expect(result).toBeDefined();
34
- expect(result!.value).toBe('secret-value');
35
- });
36
-
37
- it('lists variable names', async () => {
38
- const storage = new StorageManager(storePath, true);
39
- storage.setPassword('test123');
40
-
41
- const now = new Date().toISOString();
42
- await storage.set('A', { name: 'A', value: '1', encrypted: true, created: now, updated: now, sync_to_env: true });
43
- await storage.set('B', { name: 'B', value: '2', encrypted: true, created: now, updated: now, sync_to_env: true });
44
-
45
- const names = await storage.list();
46
- expect(names.sort()).toEqual(['A', 'B']);
47
- });
48
-
49
- it('deletes variables', async () => {
50
- const storage = new StorageManager(storePath, true);
51
- storage.setPassword('test123');
52
-
53
- const now = new Date().toISOString();
54
- await storage.set('X', { name: 'X', value: 'v', encrypted: true, created: now, updated: now, sync_to_env: true });
55
-
56
- expect(await storage.delete('X')).toBe(true);
57
- expect(await storage.get('X')).toBeUndefined();
58
- expect(await storage.delete('X')).toBe(false);
59
- });
60
-
61
- it('returns empty for non-existent store', async () => {
62
- const storage = new StorageManager(storePath, true);
63
- storage.setPassword('test123');
64
-
65
- const variables = await storage.load();
66
- expect(variables).toEqual({});
67
- });
68
-
69
- it('fails with wrong password', async () => {
70
- const storage = new StorageManager(storePath, true);
71
- storage.setPassword('correct');
72
-
73
- const now = new Date().toISOString();
74
- await storage.set('KEY', { name: 'KEY', value: 'v', encrypted: true, created: now, updated: now, sync_to_env: true });
75
-
76
- const storage2 = new StorageManager(storePath, true);
77
- storage2.setPassword('wrong');
78
-
79
- await expect(storage2.load()).rejects.toThrow('Failed to decrypt');
80
- });
81
-
82
- it('uses cache on subsequent loads', async () => {
83
- const storage = new StorageManager(storePath, true);
84
- storage.setPassword('test123');
85
-
86
- const now = new Date().toISOString();
87
- await storage.set('A', { name: 'A', value: '1', encrypted: true, created: now, updated: now, sync_to_env: true });
88
-
89
- // Second load should use cache
90
- const v1 = await storage.load();
91
- const v2 = await storage.load();
92
- expect(v1).toBe(v2); // same reference = cache hit
93
- });
94
- });
package/jest.config.js DELETED
@@ -1,11 +0,0 @@
1
- export default {
2
- preset: 'ts-jest/presets/default-esm',
3
- testEnvironment: 'node',
4
- extensionsToTreatAsEsm: ['.ts'],
5
- moduleNameMapper: {
6
- '^(\\.{1,2}/.*)\\.js$': '$1',
7
- },
8
- transform: {
9
- '^.+\\.ts$': ['ts-jest', { useESM: true }],
10
- },
11
- };