@fentz26/envcp 1.0.7 → 1.0.9
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/LICENSE +28 -17
- package/README.md +58 -12
- package/dist/adapters/base.d.ts.map +1 -1
- package/dist/adapters/base.js +2 -0
- package/dist/adapters/base.js.map +1 -1
- package/dist/cli/index.js +241 -24
- package/dist/cli/index.js.map +1 -1
- package/dist/config/manager.d.ts +15 -0
- package/dist/config/manager.d.ts.map +1 -1
- package/dist/config/manager.js +25 -9
- package/dist/config/manager.js.map +1 -1
- package/dist/storage/index.d.ts +7 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +68 -22
- package/dist/storage/index.js.map +1 -1
- package/dist/types.d.ts +18 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +7 -5
- package/dist/types.js.map +1 -1
- package/dist/utils/crypto.d.ts +21 -4
- package/dist/utils/crypto.d.ts.map +1 -1
- package/dist/utils/crypto.js +91 -15
- package/dist/utils/crypto.js.map +1 -1
- package/dist/utils/keychain.d.ts +27 -0
- package/dist/utils/keychain.d.ts.map +1 -0
- package/dist/utils/keychain.js +234 -0
- package/dist/utils/keychain.js.map +1 -0
- package/dist/utils/session.d.ts.map +1 -1
- package/dist/utils/session.js +37 -21
- package/dist/utils/session.js.map +1 -1
- package/package.json +16 -4
- package/scripts/convert-wiki.sh +91 -0
- package/scripts/install.sh +54 -0
- package/scripts/postinstall.js +13 -6
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { execFile } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
const execFileAsync = promisify(execFile);
|
|
5
|
+
const SERVICE_NAME = 'envcp';
|
|
6
|
+
const ACCOUNT_NAME = 'master-password';
|
|
7
|
+
// --- macOS Keychain (security CLI) ---
|
|
8
|
+
class MacOSKeychain {
|
|
9
|
+
name = 'macOS Keychain';
|
|
10
|
+
async store(service, account, password) {
|
|
11
|
+
try {
|
|
12
|
+
// Delete existing entry first (ignore errors)
|
|
13
|
+
try {
|
|
14
|
+
await execFileAsync('security', ['delete-generic-password', '-s', service, '-a', account]);
|
|
15
|
+
}
|
|
16
|
+
catch { /* entry may not exist */ }
|
|
17
|
+
await execFileAsync('security', [
|
|
18
|
+
'add-generic-password',
|
|
19
|
+
'-s', service,
|
|
20
|
+
'-a', account,
|
|
21
|
+
'-w', password,
|
|
22
|
+
'-U', // update if exists
|
|
23
|
+
]);
|
|
24
|
+
return { success: true };
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async retrieve(service, account) {
|
|
31
|
+
try {
|
|
32
|
+
const { stdout } = await execFileAsync('security', [
|
|
33
|
+
'find-generic-password',
|
|
34
|
+
'-s', service,
|
|
35
|
+
'-a', account,
|
|
36
|
+
'-w', // output password only
|
|
37
|
+
]);
|
|
38
|
+
return stdout.trim() || null;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async remove(service, account) {
|
|
45
|
+
try {
|
|
46
|
+
await execFileAsync('security', ['delete-generic-password', '-s', service, '-a', account]);
|
|
47
|
+
return { success: true };
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async isAvailable() {
|
|
54
|
+
try {
|
|
55
|
+
await execFileAsync('security', ['help']);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// 'security help' exits with code 1 but still indicates the binary exists
|
|
60
|
+
try {
|
|
61
|
+
await execFileAsync('which', ['security']);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// --- Linux libsecret (secret-tool CLI) ---
|
|
71
|
+
class LinuxKeychain {
|
|
72
|
+
name = 'GNOME Keyring (libsecret)';
|
|
73
|
+
async store(service, account, password) {
|
|
74
|
+
try {
|
|
75
|
+
const proc = execFileAsync('secret-tool', [
|
|
76
|
+
'store',
|
|
77
|
+
'--label', `${service} password`,
|
|
78
|
+
'service', service,
|
|
79
|
+
'account', account,
|
|
80
|
+
]);
|
|
81
|
+
// secret-tool reads the secret from stdin
|
|
82
|
+
proc.child.stdin.write(password);
|
|
83
|
+
proc.child.stdin.end();
|
|
84
|
+
await proc;
|
|
85
|
+
return { success: true };
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async retrieve(service, account) {
|
|
92
|
+
try {
|
|
93
|
+
const { stdout } = await execFileAsync('secret-tool', [
|
|
94
|
+
'lookup',
|
|
95
|
+
'service', service,
|
|
96
|
+
'account', account,
|
|
97
|
+
]);
|
|
98
|
+
return stdout || null;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async remove(service, account) {
|
|
105
|
+
try {
|
|
106
|
+
await execFileAsync('secret-tool', [
|
|
107
|
+
'clear',
|
|
108
|
+
'service', service,
|
|
109
|
+
'account', account,
|
|
110
|
+
]);
|
|
111
|
+
return { success: true };
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async isAvailable() {
|
|
118
|
+
try {
|
|
119
|
+
await execFileAsync('which', ['secret-tool']);
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// --- Windows Credential Manager (PowerShell) ---
|
|
128
|
+
class WindowsKeychain {
|
|
129
|
+
name = 'Windows Credential Manager';
|
|
130
|
+
async store(service, account, password) {
|
|
131
|
+
try {
|
|
132
|
+
// Use cmdkey for simplicity
|
|
133
|
+
const target = `${service}:${account}`;
|
|
134
|
+
await execFileAsync('cmdkey', ['/add:' + target, '/user:' + account, '/pass:' + password]);
|
|
135
|
+
return { success: true };
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async retrieve(service, account) {
|
|
142
|
+
try {
|
|
143
|
+
// cmdkey can't retrieve passwords; use PowerShell CredentialManager
|
|
144
|
+
const target = `${service}:${account}`;
|
|
145
|
+
const script = `
|
|
146
|
+
Add-Type -AssemblyName System.Runtime.InteropServices;
|
|
147
|
+
$cred = [System.Runtime.InteropServices.Marshal];
|
|
148
|
+
$target = '${target.replace(/'/g, "''")}';
|
|
149
|
+
$c = New-Object -TypeName PSCredential -ArgumentList @('x', (ConvertTo-SecureString (cmdkey /list:$target | Out-String) -AsPlainText -Force));
|
|
150
|
+
`;
|
|
151
|
+
// Simpler approach: use Windows CredentialManager module or generic approach
|
|
152
|
+
const { stdout } = await execFileAsync('powershell', [
|
|
153
|
+
'-NoProfile', '-Command',
|
|
154
|
+
`$cred = Get-StoredCredential -Target '${target.replace(/'/g, "''")}'; if ($cred) { $cred.GetNetworkCredential().Password } else { '' }`,
|
|
155
|
+
]);
|
|
156
|
+
return stdout.trim() || null;
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
// Fallback: try with cmdkey-based approach via PowerShell DPAPI
|
|
160
|
+
try {
|
|
161
|
+
const target = `${service}:${account}`;
|
|
162
|
+
const { stdout } = await execFileAsync('powershell', [
|
|
163
|
+
'-NoProfile', '-Command',
|
|
164
|
+
`[Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR((New-Object Management.Automation.PSCredential('u',(Get-Content '${os.homedir()}/.envcp/.credential' | ConvertTo-SecureString))).Password))`,
|
|
165
|
+
]);
|
|
166
|
+
return stdout.trim() || null;
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async remove(service, account) {
|
|
174
|
+
try {
|
|
175
|
+
const target = `${service}:${account}`;
|
|
176
|
+
await execFileAsync('cmdkey', ['/delete:' + target]);
|
|
177
|
+
return { success: true };
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async isAvailable() {
|
|
184
|
+
try {
|
|
185
|
+
await execFileAsync('where', ['cmdkey']);
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// --- Keychain manager ---
|
|
194
|
+
export class KeychainManager {
|
|
195
|
+
backend;
|
|
196
|
+
service;
|
|
197
|
+
constructor(service = SERVICE_NAME) {
|
|
198
|
+
this.service = service;
|
|
199
|
+
const platform = os.platform();
|
|
200
|
+
if (platform === 'darwin') {
|
|
201
|
+
this.backend = new MacOSKeychain();
|
|
202
|
+
}
|
|
203
|
+
else if (platform === 'win32') {
|
|
204
|
+
this.backend = new WindowsKeychain();
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
this.backend = new LinuxKeychain();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
get backendName() {
|
|
211
|
+
return this.backend.name;
|
|
212
|
+
}
|
|
213
|
+
async isAvailable() {
|
|
214
|
+
return this.backend.isAvailable();
|
|
215
|
+
}
|
|
216
|
+
async storePassword(password, projectPath) {
|
|
217
|
+
const account = projectPath ? `${ACCOUNT_NAME}:${projectPath}` : ACCOUNT_NAME;
|
|
218
|
+
return this.backend.store(this.service, account, password);
|
|
219
|
+
}
|
|
220
|
+
async retrievePassword(projectPath) {
|
|
221
|
+
const account = projectPath ? `${ACCOUNT_NAME}:${projectPath}` : ACCOUNT_NAME;
|
|
222
|
+
return this.backend.retrieve(this.service, account);
|
|
223
|
+
}
|
|
224
|
+
async removePassword(projectPath) {
|
|
225
|
+
const account = projectPath ? `${ACCOUNT_NAME}:${projectPath}` : ACCOUNT_NAME;
|
|
226
|
+
return this.backend.remove(this.service, account);
|
|
227
|
+
}
|
|
228
|
+
async getStatus(projectPath) {
|
|
229
|
+
const available = await this.isAvailable();
|
|
230
|
+
const hasPassword = available ? (await this.retrievePassword(projectPath)) !== null : false;
|
|
231
|
+
return { available, backend: this.backend.name, hasPassword };
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=keychain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../src/utils/keychain.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/utils/session.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/utils/session.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAiB,MAAM,aAAa,CAAC;AAGrD,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,aAAa,CAAS;gBAElB,WAAW,EAAE,MAAM,EAAE,cAAc,GAAE,MAAW,EAAE,aAAa,GAAE,MAAU;IAMjF,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA+B1C,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAqChD,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAQ3B,MAAM,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAoCjC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAW9B,WAAW,IAAI,MAAM,GAAG,IAAI;IAI5B,UAAU,IAAI,OAAO,GAAG,IAAI;IAI5B,gBAAgB,IAAI,MAAM;CAQ3B"}
|
package/dist/utils/session.js
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
+
import lockfile from 'proper-lockfile';
|
|
3
4
|
import { SessionSchema } from '../types.js';
|
|
4
5
|
import { generateId, encrypt, decrypt } from './crypto.js';
|
|
5
|
-
import * as crypto from 'crypto';
|
|
6
|
-
const PBKDF2_ITERATIONS = 100000;
|
|
7
|
-
function hashPassword(password, salt) {
|
|
8
|
-
return crypto.pbkdf2Sync(password, salt, PBKDF2_ITERATIONS, 32, 'sha512');
|
|
9
|
-
}
|
|
10
6
|
export class SessionManager {
|
|
11
7
|
sessionPath;
|
|
12
8
|
session = null;
|
|
@@ -32,28 +28,41 @@ export class SessionManager {
|
|
|
32
28
|
last_access: now.toISOString(),
|
|
33
29
|
};
|
|
34
30
|
this.password = password;
|
|
35
|
-
const salt = crypto.randomBytes(32);
|
|
36
|
-
const passwordHash = hashPassword(password, salt);
|
|
37
31
|
const sessionData = JSON.stringify({
|
|
38
32
|
session: this.session,
|
|
39
|
-
passwordHash: passwordHash.toString('hex'),
|
|
40
|
-
salt: salt.toString('hex'),
|
|
41
33
|
});
|
|
42
|
-
const encrypted = encrypt(sessionData, password);
|
|
43
|
-
|
|
34
|
+
const encrypted = await encrypt(sessionData, password);
|
|
35
|
+
// Ensure the file exists for lockfile (append-mode is a no-op if it exists)
|
|
36
|
+
await fs.writeFile(this.sessionPath, '', { encoding: 'utf8', mode: 0o600, flag: 'a' });
|
|
37
|
+
const releaseCreate = await lockfile.lock(this.sessionPath, { retries: { retries: 3, minTimeout: 50 } });
|
|
38
|
+
try {
|
|
39
|
+
await fs.writeFile(this.sessionPath, encrypted, { encoding: 'utf8', mode: 0o600 });
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
await releaseCreate();
|
|
43
|
+
}
|
|
44
44
|
return this.session;
|
|
45
45
|
}
|
|
46
46
|
async load(password) {
|
|
47
|
-
|
|
47
|
+
let encrypted;
|
|
48
|
+
try {
|
|
49
|
+
const stat = await fs.lstat(this.sessionPath);
|
|
50
|
+
if (!stat.isFile()) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
encrypted = await fs.readFile(this.sessionPath, 'utf8');
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
if (err.code === 'ENOENT')
|
|
57
|
+
return null;
|
|
48
58
|
return null;
|
|
49
59
|
}
|
|
50
60
|
try {
|
|
51
|
-
const encrypted = await fs.readFile(this.sessionPath, 'utf8');
|
|
52
61
|
const pwd = password || this.password;
|
|
53
62
|
if (!pwd) {
|
|
54
63
|
return null;
|
|
55
64
|
}
|
|
56
|
-
const decrypted = decrypt(encrypted, pwd);
|
|
65
|
+
const decrypted = await decrypt(encrypted, pwd);
|
|
57
66
|
const data = JSON.parse(decrypted);
|
|
58
67
|
this.session = SessionSchema.parse(data.session);
|
|
59
68
|
// Password is verified by successful decryption — no longer stored in file
|
|
@@ -89,23 +98,30 @@ export class SessionManager {
|
|
|
89
98
|
this.session.expires = expires.toISOString();
|
|
90
99
|
this.session.extensions += 1;
|
|
91
100
|
this.session.last_access = now.toISOString();
|
|
92
|
-
const salt = crypto.randomBytes(32);
|
|
93
|
-
const passwordHash = hashPassword(this.password, salt);
|
|
94
101
|
const sessionData = JSON.stringify({
|
|
95
102
|
session: this.session,
|
|
96
|
-
passwordHash: passwordHash.toString('hex'),
|
|
97
|
-
salt: salt.toString('hex'),
|
|
98
103
|
});
|
|
99
|
-
const encrypted = encrypt(sessionData, this.password);
|
|
100
|
-
await fs.writeFile(this.sessionPath,
|
|
104
|
+
const encrypted = await encrypt(sessionData, this.password);
|
|
105
|
+
await fs.writeFile(this.sessionPath, '', { encoding: 'utf8', mode: 0o600, flag: 'a' });
|
|
106
|
+
const releaseExtend = await lockfile.lock(this.sessionPath, { retries: { retries: 3, minTimeout: 50 } });
|
|
107
|
+
try {
|
|
108
|
+
await fs.writeFile(this.sessionPath, encrypted, { encoding: 'utf8', mode: 0o600 });
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
await releaseExtend();
|
|
112
|
+
}
|
|
101
113
|
return this.session;
|
|
102
114
|
}
|
|
103
115
|
async destroy() {
|
|
104
116
|
this.session = null;
|
|
105
117
|
this.password = null;
|
|
106
|
-
|
|
118
|
+
try {
|
|
107
119
|
await fs.unlink(this.sessionPath);
|
|
108
120
|
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
if (err.code !== 'ENOENT')
|
|
123
|
+
throw err;
|
|
124
|
+
}
|
|
109
125
|
}
|
|
110
126
|
getPassword() {
|
|
111
127
|
return this.password;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/utils/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/utils/session.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fentz26/envcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
|
+
"mcpName": "dev.fentz.envcp/envcp",
|
|
4
5
|
"description": "MCP server for secure environment variable management - Keep your secrets safe from AI agents",
|
|
6
|
+
"homepage": "https://envcp.fentz.dev",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/fentz26/EnvCP.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/fentz26/EnvCP/issues"
|
|
13
|
+
},
|
|
5
14
|
"type": "module",
|
|
6
15
|
"main": "dist/index.js",
|
|
7
16
|
"types": "dist/index.d.ts",
|
|
@@ -12,8 +21,7 @@
|
|
|
12
21
|
"build": "tsc",
|
|
13
22
|
"start": "node dist/index.js",
|
|
14
23
|
"dev": "ts-node src/index.ts",
|
|
15
|
-
"test": "jest"
|
|
16
|
-
"postinstall": "node scripts/postinstall.js"
|
|
24
|
+
"test": "NODE_OPTIONS='--experimental-vm-modules' jest"
|
|
17
25
|
},
|
|
18
26
|
"keywords": [
|
|
19
27
|
"mcp",
|
|
@@ -25,23 +33,27 @@
|
|
|
25
33
|
"model-context-protocol"
|
|
26
34
|
],
|
|
27
35
|
"author": "fentz26",
|
|
28
|
-
"license": "
|
|
36
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
29
37
|
"dependencies": {
|
|
30
38
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
39
|
+
"argon2": "^0.44.0",
|
|
31
40
|
"chalk": "^4.1.2",
|
|
32
41
|
"commander": "^11.1.0",
|
|
33
42
|
"dotenv": "^17.4.1",
|
|
34
43
|
"fs-extra": "^11.2.0",
|
|
35
44
|
"inquirer": "^8.2.6",
|
|
36
45
|
"js-yaml": "^4.1.0",
|
|
46
|
+
"proper-lockfile": "^4.1.2",
|
|
37
47
|
"zod": "^3.22.4"
|
|
38
48
|
},
|
|
39
49
|
"devDependencies": {
|
|
50
|
+
"@types/argon2": "^0.14.1",
|
|
40
51
|
"@types/fs-extra": "^11.0.4",
|
|
41
52
|
"@types/inquirer": "^9.0.7",
|
|
42
53
|
"@types/jest": "^30.0.0",
|
|
43
54
|
"@types/js-yaml": "^4.0.9",
|
|
44
55
|
"@types/node": "^20.10.0",
|
|
56
|
+
"@types/proper-lockfile": "^4.1.4",
|
|
45
57
|
"jest": "^30.3.0",
|
|
46
58
|
"ts-jest": "^29.4.9",
|
|
47
59
|
"typescript": "^5.3.0"
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Wiki to MDX conversion script
|
|
4
|
+
# Converts wiki markdown files to Mintlify MDX format
|
|
5
|
+
|
|
6
|
+
WIKI_DIR="/home/fentz/EnvCP/wiki-content"
|
|
7
|
+
DOCS_DIR="/home/fentz/EnvCP/docs"
|
|
8
|
+
|
|
9
|
+
# Mapping of wiki files to docs locations
|
|
10
|
+
declare -A file_map=(
|
|
11
|
+
["Home.md"]="index.mdx"
|
|
12
|
+
["Installation.md"]="installation.mdx"
|
|
13
|
+
["Quick-Start-Guide.md"]="quick-start.mdx"
|
|
14
|
+
["CLI-Reference.md"]="cli-reference/commands.mdx"
|
|
15
|
+
["Configuration-Reference.md"]="configuration/reference.mdx"
|
|
16
|
+
["MCP-Integration.md"]="integrations/mcp.mdx"
|
|
17
|
+
["OpenAI-Integration.md"]="integrations/openai.mdx"
|
|
18
|
+
["Gemini-Integration.md"]="integrations/gemini.mdx"
|
|
19
|
+
["Local-LLM-Integration.md"]="integrations/local-llm.mdx"
|
|
20
|
+
["API-Reference.md"]="api-reference/endpoints.mdx"
|
|
21
|
+
["Security-Best-Practices.md"]="security/best-practices.mdx"
|
|
22
|
+
["Session-Management.md"]="advanced/session-management.mdx"
|
|
23
|
+
["Troubleshooting.md"]="advanced/troubleshooting.mdx"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
convert_file() {
|
|
27
|
+
local input_file="$1"
|
|
28
|
+
local output_file="$2"
|
|
29
|
+
local filename=$(basename "$input_file")
|
|
30
|
+
|
|
31
|
+
# Add frontmatter based on file
|
|
32
|
+
local title="${filename%.md}"
|
|
33
|
+
title="${title//-/ }"
|
|
34
|
+
title="${title//_/ }"
|
|
35
|
+
|
|
36
|
+
# Extract first heading as title
|
|
37
|
+
local first_heading=$(grep -m 1 "^# " "$input_file" | sed 's/^# //')
|
|
38
|
+
if [ -n "$first_heading" ]; then
|
|
39
|
+
title="$first_heading"
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# Create temp file with frontmatter
|
|
43
|
+
echo "---" > /tmp/temp_mdx.mdx
|
|
44
|
+
echo "title: $title" >> /tmp/temp_mdx.mdx
|
|
45
|
+
echo "---" >> /tmp/temp_mdx.mdx
|
|
46
|
+
echo "" >> /tmp/temp_mdx.mdx
|
|
47
|
+
|
|
48
|
+
# Convert wiki-specific markdown to MDX
|
|
49
|
+
cat "$input_file" | \
|
|
50
|
+
# Convert wiki links to docs links
|
|
51
|
+
sed 's/\[Installation\](Installation)/[Installation](\/installation)/g' | \
|
|
52
|
+
sed 's/\[Quick Start Guide\](Quick-Start-Guide)/[Quick Start Guide](\/quick-start)/g' | \
|
|
53
|
+
sed 's/\[Configuration\](Configuration)/[Configuration](\/configuration\/reference)/g' | \
|
|
54
|
+
sed 's/\[MCP Integration\](MCP-Integration)/[MCP Integration](\/integrations\/mcp)/g' | \
|
|
55
|
+
sed 's/\[OpenAI Integration\](OpenAI-Integration)/[OpenAI Integration](\/integrations\/openai)/g' | \
|
|
56
|
+
sed 's/\[Gemini Integration\](Gemini-Integration)/[Gemini Integration](\/integrations\/gemini)/g' | \
|
|
57
|
+
sed 's/\[Local LLM Integration\](Local-LLM-Integration)/[Local LLM Integration](\/integrations\/local-llm)/g' | \
|
|
58
|
+
sed 's/\[Security Best Practices\](Security-Best-Practices)/[Security Best Practices](\/security\/best-practices)/g' | \
|
|
59
|
+
sed 's/\[Session Management\](Session-Management)/[Session Management](\/advanced\/session-management)/g' | \
|
|
60
|
+
sed 's/\[Troubleshooting\](Troubleshooting)/[Troubleshooting](\/advanced\/troubleshooting)/g' | \
|
|
61
|
+
sed 's/\[CLI Reference\](CLI-Reference)/[CLI Reference](\/cli-reference\/commands)/g' | \
|
|
62
|
+
sed 's/\[Configuration Reference\](Configuration-Reference)/[Configuration Reference](\/configuration\/reference)/g' | \
|
|
63
|
+
sed 's/\[API Reference\](API-Reference)/[API Reference](\/api-reference\/endpoints)/g' \
|
|
64
|
+
>> /tmp/temp_mdx.mdx
|
|
65
|
+
|
|
66
|
+
# Remove first heading if it matches title (to avoid duplication)
|
|
67
|
+
if [ "$(head -n 4 /tmp/temp_mdx.mdx | tail -n 1 | sed 's/^# //')" = "$title" ]; then
|
|
68
|
+
# Remove the duplicate heading
|
|
69
|
+
sed -i '4d' /tmp/temp_mdx.mdx
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Move to final location
|
|
73
|
+
mkdir -p "$(dirname "$output_file")"
|
|
74
|
+
cp /tmp/temp_mdx.mdx "$output_file"
|
|
75
|
+
|
|
76
|
+
echo "Converted: $input_file → $output_file"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Convert all files
|
|
80
|
+
for wiki_file in "${!file_map[@]}"; do
|
|
81
|
+
input_path="$WIKI_DIR/$wiki_file"
|
|
82
|
+
output_path="$DOCS_DIR/${file_map[$wiki_file]}"
|
|
83
|
+
|
|
84
|
+
if [ -f "$input_path" ]; then
|
|
85
|
+
convert_file "$input_path" "$output_path"
|
|
86
|
+
else
|
|
87
|
+
echo "Warning: $input_path not found"
|
|
88
|
+
fi
|
|
89
|
+
done
|
|
90
|
+
|
|
91
|
+
echo "✓ Conversion complete!"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
RED='\033[0;31m'
|
|
6
|
+
GREEN='\033[0;32m'
|
|
7
|
+
BLUE='\033[0;34m'
|
|
8
|
+
BOLD='\033[1m'
|
|
9
|
+
NC='\033[0m'
|
|
10
|
+
|
|
11
|
+
echo -e "${BOLD}${BLUE}"
|
|
12
|
+
cat << "EOF"
|
|
13
|
+
███████╗███╗ ██╗██╗ ██╗ ██████╗██████╗
|
|
14
|
+
██╔════╝████╗ ██║██║ ██║██╔════╝██╔══██╗
|
|
15
|
+
█████╗ ██╔██╗ ██║██║ ██║██║ ██████╔╝
|
|
16
|
+
██╔══╝ ██║╚██╗██║╚██╗ ██╔╝██║ ██╔═══╝
|
|
17
|
+
███████╗██║ ╚████║ ╚████╔╝ ╚██████╗██║
|
|
18
|
+
╚══════╝╚═╝ ╚═══╝ ╚═══╝ ╚═════╝╚═╝
|
|
19
|
+
EOF
|
|
20
|
+
echo -e "${NC}"
|
|
21
|
+
|
|
22
|
+
echo -e "${BLUE}Installing EnvCP...${NC}"
|
|
23
|
+
echo ""
|
|
24
|
+
|
|
25
|
+
if ! command -v npm &> /dev/null; then
|
|
26
|
+
echo -e "${RED}Error: npm is not installed.${NC}"
|
|
27
|
+
echo "Please install Node.js first: https://nodejs.org/"
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
NODE_VERSION=$(node -e "console.log(process.version.slice(1).split('.')[0])")
|
|
32
|
+
if [ "$NODE_VERSION" -lt 18 ]; then
|
|
33
|
+
echo -e "${RED}Error: Node.js 18 or higher is required (you have $NODE_VERSION).${NC}"
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
echo -e "${BLUE}Installing @fentz26/envcp globally...${NC}"
|
|
38
|
+
|
|
39
|
+
if npm install -g @fentz26/envcp; then
|
|
40
|
+
echo ""
|
|
41
|
+
echo -e "${GREEN}${BOLD}Successfully installed EnvCP!${NC}"
|
|
42
|
+
echo ""
|
|
43
|
+
echo -e "${BOLD}Get started:${NC}"
|
|
44
|
+
echo " envcp init # Initialize configuration"
|
|
45
|
+
echo " envcp add KEY # Add a secret"
|
|
46
|
+
echo " envcp --help # See all commands"
|
|
47
|
+
echo ""
|
|
48
|
+
echo -e "${BOLD}Docs:${NC} https://envcp.fentz.dev"
|
|
49
|
+
else
|
|
50
|
+
echo ""
|
|
51
|
+
echo -e "${RED}Installation failed. Try with sudo:${NC}"
|
|
52
|
+
echo " curl -fsSL https://raw.githubusercontent.com/fentz26/EnvCP/main/scripts/install.sh | sudo bash"
|
|
53
|
+
exit 1
|
|
54
|
+
fi
|
package/scripts/postinstall.js
CHANGED
|
@@ -13,18 +13,25 @@ const message = `
|
|
|
13
13
|
|
|
14
14
|
─────────────────────────────────────────────
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Vault location:
|
|
17
|
+
|
|
18
|
+
~/ or / -> Global vault (shared across all projects)
|
|
19
|
+
any folder -> Project vault (named after the folder)
|
|
20
|
+
You can rename it anytime with: envcp vault rename [name]
|
|
21
|
+
|
|
22
|
+
─────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
Get started:
|
|
17
25
|
|
|
18
26
|
Simple (one-time setup):
|
|
19
|
-
$ envcp init
|
|
27
|
+
$ envcp init # Interactive guided setup
|
|
20
28
|
|
|
21
29
|
Advanced (manual config):
|
|
22
|
-
$ envcp init --advanced
|
|
23
|
-
$ envcp add
|
|
24
|
-
$ envcp config set KEY VALUE # Set config values
|
|
30
|
+
$ envcp init --advanced # Full config options
|
|
31
|
+
$ envcp add [NAME] [VALUE] # Add a secret manually
|
|
25
32
|
|
|
26
33
|
Explore:
|
|
27
|
-
$ envcp --help
|
|
34
|
+
$ envcp --help # See all commands
|
|
28
35
|
|
|
29
36
|
Docs: https://github.com/fentz26/EnvCP
|
|
30
37
|
`;
|