@enactprotocol/secrets 2.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/README.md +73 -0
- package/package.json +30 -0
- package/src/dagger/index.ts +19 -0
- package/src/dagger/secret-object.ts +126 -0
- package/src/dagger/uri-parser.ts +202 -0
- package/src/env/index.ts +54 -0
- package/src/env/manager.ts +251 -0
- package/src/env/parser.ts +240 -0
- package/src/env/reader.ts +105 -0
- package/src/env/writer.ts +118 -0
- package/src/index.ts +120 -0
- package/src/keyring.ts +136 -0
- package/src/resolver.ts +184 -0
- package/src/types.ts +194 -0
- package/tests/dagger/secret-object.test.ts +217 -0
- package/tests/dagger/uri-parser.test.ts +194 -0
- package/tests/env/manager.test.ts +220 -0
- package/tests/env/parser.test.ts +352 -0
- package/tests/env/reader-writer.test.ts +257 -0
- package/tests/fixtures/complex.env +26 -0
- package/tests/fixtures/empty.env +0 -0
- package/tests/fixtures/simple.env +4 -0
- package/tests/keyring.test.ts +250 -0
- package/tests/mocks/keyring.ts +164 -0
- package/tests/resolver.test.ts +287 -0
- package/tsconfig.json +11 -0
- package/tsconfig.tsbuildinfo +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# @enactprotocol/secrets
|
|
2
|
+
|
|
3
|
+
OS keyring integration and environment variable management for Enact.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This package provides:
|
|
8
|
+
- OS-native keyring storage (macOS Keychain, Windows Credential Manager, Linux Secret Service)
|
|
9
|
+
- Namespace-scoped secret resolution with inheritance
|
|
10
|
+
- `.env` file management (global and local)
|
|
11
|
+
- Dagger secret URI scheme support
|
|
12
|
+
- Secure secret handling with memory-only runtime
|
|
13
|
+
|
|
14
|
+
## Architecture
|
|
15
|
+
|
|
16
|
+
### Secret Storage
|
|
17
|
+
|
|
18
|
+
Secrets are stored in the OS keyring with the service name `enact-cli` and account identifier `{namespace}:{SECRET_NAME}`.
|
|
19
|
+
|
|
20
|
+
Examples:
|
|
21
|
+
- `alice/api:API_TOKEN`
|
|
22
|
+
- `acme-corp/data:DATABASE_URL`
|
|
23
|
+
|
|
24
|
+
### Namespace Inheritance
|
|
25
|
+
|
|
26
|
+
When resolving secrets, Enact walks up the namespace path:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
Tool: alice/api/slack/notifier
|
|
30
|
+
Needs: API_TOKEN
|
|
31
|
+
|
|
32
|
+
Lookup:
|
|
33
|
+
1. alice/api/slack:API_TOKEN
|
|
34
|
+
2. alice/api:API_TOKEN ✓ found
|
|
35
|
+
3. alice:API_TOKEN
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
First match wins.
|
|
39
|
+
|
|
40
|
+
### Environment Variables
|
|
41
|
+
|
|
42
|
+
Non-secret environment variables are stored in `.env` files with priority:
|
|
43
|
+
|
|
44
|
+
1. **Local project** (`.enact/.env`) - highest priority
|
|
45
|
+
2. **Global user** (`~/.enact/.env`)
|
|
46
|
+
3. **Default values** from tool manifest - lowest priority
|
|
47
|
+
|
|
48
|
+
## Status
|
|
49
|
+
|
|
50
|
+
Currently in Phase 1 (scaffolding). Full implementation will be completed in Phase 3.
|
|
51
|
+
|
|
52
|
+
## Development
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Build
|
|
56
|
+
bun run build
|
|
57
|
+
|
|
58
|
+
# Test
|
|
59
|
+
bun test
|
|
60
|
+
|
|
61
|
+
# Type check
|
|
62
|
+
bun run typecheck
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Planned Features (Phase 3)
|
|
66
|
+
|
|
67
|
+
- [ ] Keyring integration (@zowe/secrets-for-zowe-sdk)
|
|
68
|
+
- [ ] setSecret() / getSecret() / listSecrets() / deleteSecret()
|
|
69
|
+
- [ ] Namespace inheritance resolution
|
|
70
|
+
- [ ] .env file reading and writing
|
|
71
|
+
- [ ] Dagger secret URI parsing (env://, file://, cmd://, op://, vault://)
|
|
72
|
+
- [ ] Cross-platform testing
|
|
73
|
+
- [ ] Comprehensive test coverage
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@enactprotocol/secrets",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "OS keyring integration and environment variable management for Enact",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc --build",
|
|
16
|
+
"clean": "rm -rf dist",
|
|
17
|
+
"test": "bun test",
|
|
18
|
+
"typecheck": "tsc --noEmit"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@zowe/secrets-for-zowe-sdk": "^8.0.2"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^22.10.1",
|
|
25
|
+
"typescript": "^5.7.2"
|
|
26
|
+
},
|
|
27
|
+
"keywords": ["keyring", "secrets", "environment", "credentials"],
|
|
28
|
+
"author": "Enact Protocol",
|
|
29
|
+
"license": "Apache-2.0"
|
|
30
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dagger secret integration
|
|
3
|
+
*
|
|
4
|
+
* Re-exports all dagger-related functions
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
parseSecretUri,
|
|
9
|
+
resolveSecretUri,
|
|
10
|
+
isSecretUri,
|
|
11
|
+
getSupportedSchemes,
|
|
12
|
+
} from "./uri-parser";
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
getSecretObject,
|
|
16
|
+
getSecretObjects,
|
|
17
|
+
parseSecretOverride,
|
|
18
|
+
parseSecretOverrides,
|
|
19
|
+
} from "./secret-object";
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get secret objects for Dagger integration
|
|
3
|
+
*
|
|
4
|
+
* Combines keyring secrets with Dagger URI overrides
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { resolveSecret } from "../resolver";
|
|
8
|
+
import type { GetSecretOptions, SecretObject } from "../types";
|
|
9
|
+
import { isSecretUri, resolveSecretUri } from "./uri-parser";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get a secret object for Dagger
|
|
13
|
+
*
|
|
14
|
+
* If an override URI is provided, resolves that instead of keyring.
|
|
15
|
+
* Otherwise, resolves from keyring with namespace inheritance.
|
|
16
|
+
*
|
|
17
|
+
* @param toolPath - The tool path for namespace resolution
|
|
18
|
+
* @param secretName - The secret name
|
|
19
|
+
* @param options - Options including override URI
|
|
20
|
+
* @returns Secret object with name, value, and source info
|
|
21
|
+
* @throws Error if secret not found
|
|
22
|
+
*/
|
|
23
|
+
export async function getSecretObject(
|
|
24
|
+
toolPath: string,
|
|
25
|
+
secretName: string,
|
|
26
|
+
options: GetSecretOptions = {}
|
|
27
|
+
): Promise<SecretObject> {
|
|
28
|
+
const { overrideUri } = options;
|
|
29
|
+
|
|
30
|
+
// If override URI is provided, resolve it
|
|
31
|
+
if (overrideUri) {
|
|
32
|
+
const value = await resolveSecretUri(overrideUri);
|
|
33
|
+
return {
|
|
34
|
+
name: secretName,
|
|
35
|
+
value,
|
|
36
|
+
source: "override",
|
|
37
|
+
overrideUri,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Otherwise, resolve from keyring
|
|
42
|
+
const result = await resolveSecret(toolPath, secretName);
|
|
43
|
+
|
|
44
|
+
if (!result.found) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`Secret '${secretName}' not found for tool '${toolPath}'. ` +
|
|
47
|
+
`Searched namespaces: ${result.searchedNamespaces.join(", ")}. ` +
|
|
48
|
+
`Set with: enact env set ${secretName} --secret --namespace <namespace>`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
name: secretName,
|
|
54
|
+
value: result.value,
|
|
55
|
+
source: "keyring",
|
|
56
|
+
namespace: result.namespace,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get multiple secret objects for a tool
|
|
62
|
+
*
|
|
63
|
+
* @param toolPath - The tool path for namespace resolution
|
|
64
|
+
* @param secrets - Map of secret name to optional override URI
|
|
65
|
+
* @returns Map of secret name to secret object
|
|
66
|
+
*/
|
|
67
|
+
export async function getSecretObjects(
|
|
68
|
+
toolPath: string,
|
|
69
|
+
secrets: Record<string, string | undefined>
|
|
70
|
+
): Promise<Map<string, SecretObject>> {
|
|
71
|
+
const results = new Map<string, SecretObject>();
|
|
72
|
+
|
|
73
|
+
for (const [name, overrideUri] of Object.entries(secrets)) {
|
|
74
|
+
const obj = await getSecretObject(toolPath, name, overrideUri ? { overrideUri } : {});
|
|
75
|
+
results.set(name, obj);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return results;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Parse a secret override from CLI format
|
|
83
|
+
*
|
|
84
|
+
* Format: SECRET_NAME=uri
|
|
85
|
+
* Example: API_TOKEN=env://MY_API_TOKEN
|
|
86
|
+
*
|
|
87
|
+
* @param override - The override string
|
|
88
|
+
* @returns Parsed name and URI, or null if invalid
|
|
89
|
+
*/
|
|
90
|
+
export function parseSecretOverride(override: string): { name: string; uri: string } | null {
|
|
91
|
+
const eqIndex = override.indexOf("=");
|
|
92
|
+
if (eqIndex === -1) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const name = override.slice(0, eqIndex).trim();
|
|
97
|
+
const uri = override.slice(eqIndex + 1).trim();
|
|
98
|
+
|
|
99
|
+
if (!name || !isSecretUri(uri)) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return { name, uri };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Parse multiple secret overrides from CLI
|
|
108
|
+
*
|
|
109
|
+
* @param overrides - Array of override strings
|
|
110
|
+
* @returns Map of secret name to override URI
|
|
111
|
+
*/
|
|
112
|
+
export function parseSecretOverrides(overrides: string[]): Record<string, string> {
|
|
113
|
+
const result: Record<string, string> = {};
|
|
114
|
+
|
|
115
|
+
for (const override of overrides) {
|
|
116
|
+
const parsed = parseSecretOverride(override);
|
|
117
|
+
if (parsed) {
|
|
118
|
+
result[parsed.name] = parsed.uri;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Re-export URI functions
|
|
126
|
+
export { parseSecretUri, resolveSecretUri, isSecretUri, getSupportedSchemes } from "./uri-parser";
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dagger Secret URI parser and resolver
|
|
3
|
+
*
|
|
4
|
+
* Supports secret URIs:
|
|
5
|
+
* - env://VAR_NAME - Environment variable
|
|
6
|
+
* - file://PATH - File contents
|
|
7
|
+
* - cmd://COMMAND - Command output
|
|
8
|
+
* - op://VAULT/ITEM/FIELD - 1Password
|
|
9
|
+
* - vault://PATH - HashiCorp Vault
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { execSync } from "node:child_process";
|
|
13
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
14
|
+
import { resolve } from "node:path";
|
|
15
|
+
import type { DaggerSecretScheme, DaggerSecretUri } from "../types";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Valid URI schemes
|
|
19
|
+
*/
|
|
20
|
+
const VALID_SCHEMES = new Set<DaggerSecretScheme>(["env", "file", "cmd", "op", "vault"]);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Parse a Dagger secret URI
|
|
24
|
+
*
|
|
25
|
+
* @param uri - The URI to parse (e.g., "env://API_KEY")
|
|
26
|
+
* @returns Parsed URI with scheme and value
|
|
27
|
+
* @throws Error if URI is invalid
|
|
28
|
+
*/
|
|
29
|
+
export function parseSecretUri(uri: string): DaggerSecretUri {
|
|
30
|
+
const match = uri.match(/^([a-z]+):\/\/(.+)$/);
|
|
31
|
+
if (!match || !match[1] || !match[2]) {
|
|
32
|
+
throw new Error(`Invalid secret URI format: ${uri}. Expected format: scheme://value`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const schemeStr = match[1];
|
|
36
|
+
const value = match[2];
|
|
37
|
+
const scheme = schemeStr as DaggerSecretScheme;
|
|
38
|
+
|
|
39
|
+
if (!VALID_SCHEMES.has(scheme)) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`Invalid secret URI scheme: ${scheme}. Valid schemes: ${Array.from(VALID_SCHEMES).join(", ")}`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
scheme,
|
|
47
|
+
value,
|
|
48
|
+
original: uri,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if a string is a valid secret URI
|
|
54
|
+
*/
|
|
55
|
+
export function isSecretUri(uri: string): boolean {
|
|
56
|
+
try {
|
|
57
|
+
parseSecretUri(uri);
|
|
58
|
+
return true;
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Resolve a secret URI to its actual value
|
|
66
|
+
*
|
|
67
|
+
* @param uri - The URI to resolve
|
|
68
|
+
* @returns The secret value
|
|
69
|
+
* @throws Error if resolution fails
|
|
70
|
+
*/
|
|
71
|
+
export async function resolveSecretUri(uri: string): Promise<string> {
|
|
72
|
+
const parsed = parseSecretUri(uri);
|
|
73
|
+
|
|
74
|
+
switch (parsed.scheme) {
|
|
75
|
+
case "env":
|
|
76
|
+
return resolveEnvUri(parsed.value);
|
|
77
|
+
case "file":
|
|
78
|
+
return resolveFileUri(parsed.value);
|
|
79
|
+
case "cmd":
|
|
80
|
+
return resolveCmdUri(parsed.value);
|
|
81
|
+
case "op":
|
|
82
|
+
return resolveOpUri(parsed.value);
|
|
83
|
+
case "vault":
|
|
84
|
+
return resolveVaultUri(parsed.value);
|
|
85
|
+
default:
|
|
86
|
+
throw new Error(`Unsupported secret scheme: ${parsed.scheme}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Resolve env:// URI - read from environment variable
|
|
92
|
+
*/
|
|
93
|
+
function resolveEnvUri(varName: string): string {
|
|
94
|
+
const value = process.env[varName];
|
|
95
|
+
if (value === undefined) {
|
|
96
|
+
throw new Error(`Environment variable '${varName}' is not set (from env://${varName})`);
|
|
97
|
+
}
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Resolve file:// URI - read file contents
|
|
103
|
+
*/
|
|
104
|
+
function resolveFileUri(path: string): string {
|
|
105
|
+
// Handle relative paths
|
|
106
|
+
const resolvedPath = resolve(path);
|
|
107
|
+
|
|
108
|
+
if (!existsSync(resolvedPath)) {
|
|
109
|
+
throw new Error(`File not found: ${resolvedPath} (from file://${path})`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return readFileSync(resolvedPath, "utf-8").trim();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Resolve cmd:// URI - execute command and capture output
|
|
117
|
+
*/
|
|
118
|
+
function resolveCmdUri(command: string): string {
|
|
119
|
+
try {
|
|
120
|
+
const output = execSync(command, {
|
|
121
|
+
encoding: "utf-8",
|
|
122
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
123
|
+
});
|
|
124
|
+
return output.trim();
|
|
125
|
+
} catch (error) {
|
|
126
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
127
|
+
throw new Error(`Command failed: ${command} (from cmd://${command}): ${message}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Resolve op:// URI - 1Password CLI
|
|
133
|
+
* Format: op://vault/item/field
|
|
134
|
+
*
|
|
135
|
+
* Requires 1Password CLI (op) to be installed and authenticated
|
|
136
|
+
*/
|
|
137
|
+
function resolveOpUri(path: string): string {
|
|
138
|
+
const opUri = `op://${path}`;
|
|
139
|
+
try {
|
|
140
|
+
const output = execSync(`op read "${opUri}"`, {
|
|
141
|
+
encoding: "utf-8",
|
|
142
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
143
|
+
});
|
|
144
|
+
return output.trim();
|
|
145
|
+
} catch (error) {
|
|
146
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
147
|
+
throw new Error(
|
|
148
|
+
`1Password read failed for ${opUri}. Ensure 'op' CLI is installed and authenticated: ${message}`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Resolve vault:// URI - HashiCorp Vault
|
|
155
|
+
* Format: vault://path/to/secret#field
|
|
156
|
+
*
|
|
157
|
+
* Requires Vault CLI and VAULT_ADDR, VAULT_TOKEN environment variables
|
|
158
|
+
*/
|
|
159
|
+
function resolveVaultUri(path: string): string {
|
|
160
|
+
// Parse path and optional field
|
|
161
|
+
const [secretPath, field] = path.split("#");
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const command = `vault kv get -format=json "${secretPath}"`;
|
|
165
|
+
const output = execSync(command, {
|
|
166
|
+
encoding: "utf-8",
|
|
167
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const data = JSON.parse(output);
|
|
171
|
+
const secretData = data.data?.data ?? data.data;
|
|
172
|
+
|
|
173
|
+
if (field) {
|
|
174
|
+
if (!(field in secretData)) {
|
|
175
|
+
throw new Error(`Field '${field}' not found in secret at ${secretPath}`);
|
|
176
|
+
}
|
|
177
|
+
return String(secretData[field]);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// If no field specified, return first value or JSON
|
|
181
|
+
const values = Object.values(secretData);
|
|
182
|
+
if (values.length === 1) {
|
|
183
|
+
return String(values[0]);
|
|
184
|
+
}
|
|
185
|
+
return JSON.stringify(secretData);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
if (error instanceof SyntaxError) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
`Failed to parse Vault response for ${path}. Ensure VAULT_ADDR and VAULT_TOKEN are set.`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
193
|
+
throw new Error(`Vault read failed for ${path}: ${message}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get supported URI schemes
|
|
199
|
+
*/
|
|
200
|
+
export function getSupportedSchemes(): DaggerSecretScheme[] {
|
|
201
|
+
return Array.from(VALID_SCHEMES);
|
|
202
|
+
}
|
package/src/env/index.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment variable management
|
|
3
|
+
*
|
|
4
|
+
* Re-exports all env-related functions
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Parser functions
|
|
8
|
+
export {
|
|
9
|
+
parseEnvFile,
|
|
10
|
+
parseEnvContent,
|
|
11
|
+
serializeEnvFile,
|
|
12
|
+
createEnvContent,
|
|
13
|
+
updateEnvVar,
|
|
14
|
+
removeEnvVar,
|
|
15
|
+
} from "./parser";
|
|
16
|
+
|
|
17
|
+
// Reader functions
|
|
18
|
+
export {
|
|
19
|
+
getGlobalEnvPath,
|
|
20
|
+
getLocalEnvPath,
|
|
21
|
+
readEnvFile,
|
|
22
|
+
readEnvVars,
|
|
23
|
+
loadGlobalEnv,
|
|
24
|
+
loadLocalEnv,
|
|
25
|
+
loadGlobalEnvFile,
|
|
26
|
+
loadLocalEnvFile,
|
|
27
|
+
globalEnvExists,
|
|
28
|
+
localEnvExists,
|
|
29
|
+
} from "./reader";
|
|
30
|
+
|
|
31
|
+
// Writer functions
|
|
32
|
+
export {
|
|
33
|
+
writeEnvFile,
|
|
34
|
+
writeEnvVars,
|
|
35
|
+
setEnvVar,
|
|
36
|
+
deleteEnvVar,
|
|
37
|
+
setGlobalEnvVar,
|
|
38
|
+
setLocalEnvVar,
|
|
39
|
+
deleteGlobalEnvVar,
|
|
40
|
+
deleteLocalEnvVar,
|
|
41
|
+
} from "./writer";
|
|
42
|
+
|
|
43
|
+
// Manager functions (high-level API)
|
|
44
|
+
export {
|
|
45
|
+
setEnv,
|
|
46
|
+
getEnv,
|
|
47
|
+
getEnvValue,
|
|
48
|
+
deleteEnv,
|
|
49
|
+
listEnv,
|
|
50
|
+
resolveAllEnv,
|
|
51
|
+
resolveToolEnv,
|
|
52
|
+
hasLocalEnv,
|
|
53
|
+
hasGlobalEnv,
|
|
54
|
+
} from "./manager";
|