@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/utils/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAGjC,MAAM,aAAa,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,MAAM;AAEzC,MAAM,UAAU,cAAc,CAAC,GAAwB,EAAE,gBAAwB,WAAW;IAC1F,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,aAAa,CAAC,CAAC;IAC5D,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,iCAAiC,CAAC,CAAC;IACjF,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,6EAA6E,CAAC,CAAC;AAC/H,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAwB,EAAE,MAAc,EAAE,IAAa;IAC9E,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAC9D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAyB;IACjD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAC/B,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC;YACrB,IAAI,IAAI,GAAG,aAAa,EAAE,CAAC;gBACzB,GAAG,CAAC,OAAO,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC;gBAC5C,OAAO;YACT,CAAC;YACD,IAAI,IAAI,KAAK,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACjB,IAAI,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACxC,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;YACzC,CAAC;QACH,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAA4B,EAAE,QAAgB;IAC3E,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACtD,OAAO,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Session } from '../types.js';
|
|
2
|
+
export declare class SessionManager {
|
|
3
|
+
private sessionPath;
|
|
4
|
+
private session;
|
|
5
|
+
private password;
|
|
6
|
+
private timeoutMinutes;
|
|
7
|
+
private maxExtensions;
|
|
8
|
+
constructor(sessionPath: string, timeoutMinutes?: number, maxExtensions?: number);
|
|
9
|
+
init(): Promise<void>;
|
|
10
|
+
create(password: string): Promise<Session>;
|
|
11
|
+
load(password?: string): Promise<Session | null>;
|
|
12
|
+
isValid(): Promise<boolean>;
|
|
13
|
+
extend(): Promise<Session | null>;
|
|
14
|
+
destroy(): Promise<void>;
|
|
15
|
+
getPassword(): string | null;
|
|
16
|
+
getSession(): Session | null;
|
|
17
|
+
getRemainingTime(): number;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/utils/session.ts"],"names":[],"mappings":"AAEA,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;IAyB1C,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IA6BhD,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAQ3B,MAAM,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IA+BjC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAS9B,WAAW,IAAI,MAAM,GAAG,IAAI;IAI5B,UAAU,IAAI,OAAO,GAAG,IAAI;IAI5B,gBAAgB,IAAI,MAAM;CAQ3B"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as fs from 'fs-extra';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { SessionSchema } from '../types.js';
|
|
4
|
+
import { generateId, encrypt, decrypt } from './crypto.js';
|
|
5
|
+
export class SessionManager {
|
|
6
|
+
sessionPath;
|
|
7
|
+
session = null;
|
|
8
|
+
password = null;
|
|
9
|
+
timeoutMinutes;
|
|
10
|
+
maxExtensions;
|
|
11
|
+
constructor(sessionPath, timeoutMinutes = 30, maxExtensions = 5) {
|
|
12
|
+
this.sessionPath = sessionPath;
|
|
13
|
+
this.timeoutMinutes = timeoutMinutes;
|
|
14
|
+
this.maxExtensions = maxExtensions;
|
|
15
|
+
}
|
|
16
|
+
async init() {
|
|
17
|
+
await fs.ensureDir(path.dirname(this.sessionPath));
|
|
18
|
+
}
|
|
19
|
+
async create(password) {
|
|
20
|
+
const now = new Date();
|
|
21
|
+
const expires = new Date(now.getTime() + this.timeoutMinutes * 60 * 1000);
|
|
22
|
+
this.session = {
|
|
23
|
+
id: generateId(),
|
|
24
|
+
created: now.toISOString(),
|
|
25
|
+
expires: expires.toISOString(),
|
|
26
|
+
extensions: 0,
|
|
27
|
+
last_access: now.toISOString(),
|
|
28
|
+
};
|
|
29
|
+
this.password = password;
|
|
30
|
+
const sessionData = JSON.stringify({
|
|
31
|
+
session: this.session,
|
|
32
|
+
password: password,
|
|
33
|
+
});
|
|
34
|
+
const encrypted = encrypt(sessionData, password);
|
|
35
|
+
await fs.writeFile(this.sessionPath, encrypted, 'utf8');
|
|
36
|
+
return this.session;
|
|
37
|
+
}
|
|
38
|
+
async load(password) {
|
|
39
|
+
if (!await fs.pathExists(this.sessionPath)) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const encrypted = await fs.readFile(this.sessionPath, 'utf8');
|
|
44
|
+
const pwd = password || this.password;
|
|
45
|
+
if (!pwd) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const decrypted = decrypt(encrypted, pwd);
|
|
49
|
+
const data = JSON.parse(decrypted);
|
|
50
|
+
this.session = SessionSchema.parse(data.session);
|
|
51
|
+
this.password = data.password;
|
|
52
|
+
if (new Date() > new Date(this.session.expires)) {
|
|
53
|
+
await this.destroy();
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return this.session;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async isValid() {
|
|
63
|
+
if (!this.session) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return new Date() < new Date(this.session.expires);
|
|
67
|
+
}
|
|
68
|
+
async extend() {
|
|
69
|
+
if (!this.session || !this.password) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
if (this.session.extensions >= this.maxExtensions) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
if (!await this.isValid()) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const now = new Date();
|
|
79
|
+
const expires = new Date(now.getTime() + this.timeoutMinutes * 60 * 1000);
|
|
80
|
+
this.session.expires = expires.toISOString();
|
|
81
|
+
this.session.extensions += 1;
|
|
82
|
+
this.session.last_access = now.toISOString();
|
|
83
|
+
const sessionData = JSON.stringify({
|
|
84
|
+
session: this.session,
|
|
85
|
+
password: this.password,
|
|
86
|
+
});
|
|
87
|
+
const encrypted = encrypt(sessionData, this.password);
|
|
88
|
+
await fs.writeFile(this.sessionPath, encrypted, 'utf8');
|
|
89
|
+
return this.session;
|
|
90
|
+
}
|
|
91
|
+
async destroy() {
|
|
92
|
+
this.session = null;
|
|
93
|
+
this.password = null;
|
|
94
|
+
if (await fs.pathExists(this.sessionPath)) {
|
|
95
|
+
await fs.unlink(this.sessionPath);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
getPassword() {
|
|
99
|
+
return this.password;
|
|
100
|
+
}
|
|
101
|
+
getSession() {
|
|
102
|
+
return this.session;
|
|
103
|
+
}
|
|
104
|
+
getRemainingTime() {
|
|
105
|
+
if (!this.session) {
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
const remaining = new Date(this.session.expires).getTime() - Date.now();
|
|
109
|
+
return Math.max(0, Math.floor(remaining / 1000 / 60));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/utils/session.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fentz26/envcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for secure environment variable management - Keep your secrets safe from AI agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"envcp": "dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"dev": "ts-node src/index.ts",
|
|
15
|
+
"test": "jest"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"environment-variables",
|
|
20
|
+
"secrets",
|
|
21
|
+
"security",
|
|
22
|
+
"ai",
|
|
23
|
+
"claude",
|
|
24
|
+
"model-context-protocol"
|
|
25
|
+
],
|
|
26
|
+
"author": "fentz26",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
30
|
+
"chalk": "^4.1.2",
|
|
31
|
+
"commander": "^11.1.0",
|
|
32
|
+
|
|
33
|
+
"dotenv": "^16.3.1",
|
|
34
|
+
"fs-extra": "^11.2.0",
|
|
35
|
+
"inquirer": "^8.2.6",
|
|
36
|
+
"js-yaml": "^4.1.0",
|
|
37
|
+
"zod": "^3.22.4"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
|
|
41
|
+
"@types/fs-extra": "^11.0.4",
|
|
42
|
+
"@types/inquirer": "^9.0.7",
|
|
43
|
+
"@types/js-yaml": "^4.0.9",
|
|
44
|
+
"@types/node": "^20.10.0",
|
|
45
|
+
"typescript": "^5.3.0"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18.0.0"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,411 @@
|
|
|
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
|
+
}
|