@dexto/agent-management 1.3.0 → 1.5.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/dist/AgentFactory.cjs +152 -0
- package/dist/AgentFactory.d.ts +121 -0
- package/dist/AgentFactory.d.ts.map +1 -0
- package/dist/AgentFactory.js +132 -0
- package/dist/AgentManager.cjs +226 -0
- package/dist/AgentManager.d.ts +191 -0
- package/dist/AgentManager.d.ts.map +1 -0
- package/dist/AgentManager.js +192 -0
- package/dist/config/config-enrichment.cjs +23 -3
- package/dist/config/config-enrichment.d.ts +20 -5
- package/dist/config/config-enrichment.d.ts.map +1 -1
- package/dist/config/config-enrichment.js +22 -3
- package/dist/config/config-manager.cjs +340 -3
- package/dist/config/config-manager.d.ts +158 -7
- package/dist/config/config-manager.d.ts.map +1 -1
- package/dist/config/config-manager.js +325 -3
- package/dist/config/discover-prompts.cjs +103 -0
- package/dist/config/discover-prompts.d.ts +28 -0
- package/dist/config/discover-prompts.d.ts.map +1 -0
- package/dist/config/discover-prompts.js +73 -0
- package/dist/config/errors.cjs +2 -2
- package/dist/config/errors.js +2 -2
- package/dist/config/index.cjs +14 -2
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +21 -3
- package/dist/index.cjs +109 -6
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +111 -6
- package/dist/installation.cjs +239 -0
- package/dist/installation.d.ts +72 -0
- package/dist/installation.d.ts.map +1 -0
- package/dist/installation.js +202 -0
- package/dist/models/custom-models.cjs +157 -0
- package/dist/models/custom-models.d.ts +94 -0
- package/dist/models/custom-models.d.ts.map +1 -0
- package/dist/models/custom-models.js +117 -0
- package/dist/models/index.cjs +89 -0
- package/dist/models/index.d.ts +11 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +68 -0
- package/dist/models/path-resolver.cjs +154 -0
- package/dist/models/path-resolver.d.ts +77 -0
- package/dist/models/path-resolver.d.ts.map +1 -0
- package/dist/models/path-resolver.js +108 -0
- package/dist/models/state-manager.cjs +220 -0
- package/dist/models/state-manager.d.ts +138 -0
- package/dist/models/state-manager.d.ts.map +1 -0
- package/dist/models/state-manager.js +184 -0
- package/dist/preferences/error-codes.cjs +2 -0
- package/dist/preferences/error-codes.d.ts +3 -1
- package/dist/preferences/error-codes.d.ts.map +1 -1
- package/dist/preferences/error-codes.js +2 -0
- package/dist/preferences/index.d.ts +1 -1
- package/dist/preferences/index.d.ts.map +1 -1
- package/dist/preferences/loader.cjs +32 -6
- package/dist/preferences/loader.d.ts +23 -4
- package/dist/preferences/loader.d.ts.map +1 -1
- package/dist/preferences/loader.js +32 -6
- package/dist/preferences/schemas.cjs +21 -3
- package/dist/preferences/schemas.d.ts +52 -24
- package/dist/preferences/schemas.d.ts.map +1 -1
- package/dist/preferences/schemas.js +28 -4
- package/dist/registry/registry.cjs +28 -45
- package/dist/registry/registry.d.ts +8 -6
- package/dist/registry/registry.d.ts.map +1 -1
- package/dist/registry/registry.js +26 -44
- package/dist/registry/types.d.ts +11 -13
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/resolver.cjs +82 -43
- package/dist/resolver.d.ts +7 -5
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +83 -44
- package/dist/utils/api-key-resolver.cjs +19 -1
- package/dist/utils/api-key-resolver.d.ts.map +1 -1
- package/dist/utils/api-key-resolver.js +19 -1
- package/dist/utils/api-key-store.cjs +46 -0
- package/dist/utils/api-key-store.d.ts +27 -0
- package/dist/utils/api-key-store.d.ts.map +1 -1
- package/dist/utils/api-key-store.js +44 -0
- package/dist/utils/env-file.cjs +20 -68
- package/dist/utils/env-file.d.ts +2 -1
- package/dist/utils/env-file.d.ts.map +1 -1
- package/dist/utils/env-file.js +20 -68
- package/dist/writer.cjs +20 -2
- package/dist/writer.d.ts +1 -0
- package/dist/writer.d.ts.map +1 -1
- package/dist/writer.js +20 -2
- package/package.json +2 -2
- package/dist/AgentOrchestrator.cjs +0 -263
- package/dist/AgentOrchestrator.d.ts +0 -191
- package/dist/AgentOrchestrator.d.ts.map +0 -1
- package/dist/AgentOrchestrator.js +0 -239
package/dist/resolver.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { promises as fs } from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { isPath } from "./utils/path.js";
|
|
3
|
+
import { isPath, getDextoGlobalPath, resolveBundledScript } from "./utils/path.js";
|
|
4
4
|
import {
|
|
5
5
|
getExecutionContext,
|
|
6
6
|
findDextoSourceRoot,
|
|
@@ -9,7 +9,10 @@ import {
|
|
|
9
9
|
import { logger } from "@dexto/core";
|
|
10
10
|
import { loadGlobalPreferences, globalPreferencesExist } from "./preferences/loader.js";
|
|
11
11
|
import { ConfigError } from "./config/index.js";
|
|
12
|
-
|
|
12
|
+
import { RegistryError } from "./registry/errors.js";
|
|
13
|
+
import { AgentManager } from "./AgentManager.js";
|
|
14
|
+
import { installBundledAgent } from "./installation.js";
|
|
15
|
+
async function resolveAgentPath(nameOrPath, autoInstall = true) {
|
|
13
16
|
if (nameOrPath && isPath(nameOrPath)) {
|
|
14
17
|
const resolved = path.resolve(nameOrPath);
|
|
15
18
|
try {
|
|
@@ -23,32 +26,67 @@ async function resolveAgentPath(nameOrPath, autoInstall = true, injectPreference
|
|
|
23
26
|
}
|
|
24
27
|
}
|
|
25
28
|
if (nameOrPath) {
|
|
26
|
-
|
|
27
|
-
const registry = getAgentRegistry();
|
|
28
|
-
return await registry.resolveAgent(nameOrPath, autoInstall, injectPreferences);
|
|
29
|
+
return await resolveAgentByName(nameOrPath, autoInstall);
|
|
29
30
|
}
|
|
30
|
-
return await resolveDefaultAgentByContext(autoInstall
|
|
31
|
+
return await resolveDefaultAgentByContext(autoInstall);
|
|
31
32
|
}
|
|
32
|
-
async function
|
|
33
|
+
async function resolveAgentByName(agentId, autoInstall) {
|
|
34
|
+
const agentsDir = getDextoGlobalPath("agents");
|
|
35
|
+
const installedRegistryPath = path.join(agentsDir, "registry.json");
|
|
36
|
+
try {
|
|
37
|
+
const manager = new AgentManager(installedRegistryPath);
|
|
38
|
+
await manager.loadRegistry();
|
|
39
|
+
if (manager.hasAgent(agentId)) {
|
|
40
|
+
const agentPath = await getAgentConfigPath(agentId);
|
|
41
|
+
return agentPath;
|
|
42
|
+
}
|
|
43
|
+
} catch (error) {
|
|
44
|
+
logger.debug(`Agent '${agentId}' not found in installed registry: ${error}`);
|
|
45
|
+
}
|
|
46
|
+
if (autoInstall) {
|
|
47
|
+
try {
|
|
48
|
+
logger.info(`Auto-installing agent '${agentId}' from bundled registry`);
|
|
49
|
+
const configPath = await installBundledAgent(agentId);
|
|
50
|
+
return configPath;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
logger.debug(`Failed to auto-install agent '${agentId}': ${error}`);
|
|
53
|
+
throw RegistryError.agentNotFound(agentId, []);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
throw RegistryError.agentNotInstalledAutoInstallDisabled(agentId, []);
|
|
57
|
+
}
|
|
58
|
+
async function getAgentConfigPath(agentId) {
|
|
59
|
+
const agentsDir = getDextoGlobalPath("agents");
|
|
60
|
+
const installedRegistryPath = path.join(agentsDir, "registry.json");
|
|
61
|
+
const registryContent = await fs.readFile(installedRegistryPath, "utf-8");
|
|
62
|
+
const registry = JSON.parse(registryContent);
|
|
63
|
+
const agentEntry = registry.agents.find((a) => a.id === agentId);
|
|
64
|
+
if (!agentEntry) {
|
|
65
|
+
const available = registry.agents.map((a) => a.id);
|
|
66
|
+
throw RegistryError.agentNotFound(agentId, available);
|
|
67
|
+
}
|
|
68
|
+
return path.resolve(path.dirname(installedRegistryPath), agentEntry.configPath);
|
|
69
|
+
}
|
|
70
|
+
async function resolveDefaultAgentByContext(autoInstall = true) {
|
|
33
71
|
const executionContext = getExecutionContext();
|
|
34
72
|
switch (executionContext) {
|
|
35
73
|
case "dexto-source":
|
|
36
|
-
return await resolveDefaultAgentForDextoSource(autoInstall
|
|
74
|
+
return await resolveDefaultAgentForDextoSource(autoInstall);
|
|
37
75
|
case "dexto-project":
|
|
38
|
-
return await resolveDefaultAgentForDextoProject(autoInstall
|
|
76
|
+
return await resolveDefaultAgentForDextoProject(autoInstall);
|
|
39
77
|
case "global-cli":
|
|
40
|
-
return await resolveDefaultAgentForGlobalCLI(autoInstall
|
|
78
|
+
return await resolveDefaultAgentForGlobalCLI(autoInstall);
|
|
41
79
|
default:
|
|
42
80
|
throw ConfigError.unknownContext(executionContext);
|
|
43
81
|
}
|
|
44
82
|
}
|
|
45
|
-
async function resolveDefaultAgentForDextoSource(autoInstall = true
|
|
83
|
+
async function resolveDefaultAgentForDextoSource(autoInstall = true) {
|
|
46
84
|
logger.debug("Resolving default agent for dexto source context");
|
|
47
85
|
const sourceRoot = findDextoSourceRoot();
|
|
48
86
|
if (!sourceRoot) {
|
|
49
87
|
throw ConfigError.bundledNotFound("dexto source directory not found");
|
|
50
88
|
}
|
|
51
|
-
const repoConfigPath = path.join(sourceRoot, "agents", "
|
|
89
|
+
const repoConfigPath = path.join(sourceRoot, "agents", "coding-agent", "coding-agent.yml");
|
|
52
90
|
const isDevMode = process.env.DEXTO_DEV_MODE === "true";
|
|
53
91
|
if (isDevMode) {
|
|
54
92
|
logger.debug("Dev mode: using repository config file");
|
|
@@ -65,13 +103,7 @@ async function resolveDefaultAgentForDextoSource(autoInstall = true, injectPrefe
|
|
|
65
103
|
if (preferences.setup.completed) {
|
|
66
104
|
logger.debug("Using user preferences in dexto-source context");
|
|
67
105
|
const preferredAgentName = preferences.defaults.defaultAgent;
|
|
68
|
-
|
|
69
|
-
const registry = getAgentRegistry();
|
|
70
|
-
return await registry.resolveAgent(
|
|
71
|
-
preferredAgentName,
|
|
72
|
-
autoInstall,
|
|
73
|
-
injectPreferences
|
|
74
|
-
);
|
|
106
|
+
return await resolveAgentByName(preferredAgentName, autoInstall);
|
|
75
107
|
}
|
|
76
108
|
} catch (error) {
|
|
77
109
|
logger.warn(`Failed to load preferences, falling back to repo config: ${error}`);
|
|
@@ -85,16 +117,16 @@ async function resolveDefaultAgentForDextoSource(autoInstall = true, injectPrefe
|
|
|
85
117
|
throw ConfigError.bundledNotFound(repoConfigPath);
|
|
86
118
|
}
|
|
87
119
|
}
|
|
88
|
-
async function resolveDefaultAgentForDextoProject(autoInstall = true
|
|
120
|
+
async function resolveDefaultAgentForDextoProject(autoInstall = true) {
|
|
89
121
|
logger.debug("Resolving default agent for dexto project context");
|
|
90
122
|
const projectRoot = findDextoProjectRoot();
|
|
91
123
|
if (!projectRoot) {
|
|
92
124
|
throw ConfigError.unknownContext("dexto-project: project root not found");
|
|
93
125
|
}
|
|
94
126
|
const candidatePaths = [
|
|
95
|
-
path.join(projectRoot, "
|
|
96
|
-
path.join(projectRoot, "agents", "
|
|
97
|
-
path.join(projectRoot, "src", "dexto", "agents", "
|
|
127
|
+
path.join(projectRoot, "coding-agent.yml"),
|
|
128
|
+
path.join(projectRoot, "agents", "coding-agent.yml"),
|
|
129
|
+
path.join(projectRoot, "src", "dexto", "agents", "coding-agent.yml")
|
|
98
130
|
];
|
|
99
131
|
for (const p of candidatePaths) {
|
|
100
132
|
try {
|
|
@@ -103,7 +135,7 @@ async function resolveDefaultAgentForDextoProject(autoInstall = true, injectPref
|
|
|
103
135
|
} catch {
|
|
104
136
|
}
|
|
105
137
|
}
|
|
106
|
-
logger.debug(`No project-local
|
|
138
|
+
logger.debug(`No project-local coding-agent.yml found in ${projectRoot}`);
|
|
107
139
|
if (!globalPreferencesExist()) {
|
|
108
140
|
throw ConfigError.noProjectDefault(projectRoot);
|
|
109
141
|
}
|
|
@@ -112,11 +144,9 @@ async function resolveDefaultAgentForDextoProject(autoInstall = true, injectPref
|
|
|
112
144
|
throw ConfigError.setupIncomplete();
|
|
113
145
|
}
|
|
114
146
|
const preferredAgentName = preferences.defaults.defaultAgent;
|
|
115
|
-
|
|
116
|
-
const registry = getAgentRegistry();
|
|
117
|
-
return await registry.resolveAgent(preferredAgentName, autoInstall, injectPreferences);
|
|
147
|
+
return await resolveAgentByName(preferredAgentName, autoInstall);
|
|
118
148
|
}
|
|
119
|
-
async function resolveDefaultAgentForGlobalCLI(autoInstall = true
|
|
149
|
+
async function resolveDefaultAgentForGlobalCLI(autoInstall = true) {
|
|
120
150
|
logger.debug("Resolving default agent for global CLI context");
|
|
121
151
|
if (!globalPreferencesExist()) {
|
|
122
152
|
throw ConfigError.noGlobalPreferences();
|
|
@@ -126,24 +156,33 @@ async function resolveDefaultAgentForGlobalCLI(autoInstall = true, injectPrefere
|
|
|
126
156
|
throw ConfigError.setupIncomplete();
|
|
127
157
|
}
|
|
128
158
|
const preferredAgentName = preferences.defaults.defaultAgent;
|
|
129
|
-
|
|
130
|
-
const registry = getAgentRegistry();
|
|
131
|
-
return await registry.resolveAgent(preferredAgentName, autoInstall, injectPreferences);
|
|
159
|
+
return await resolveAgentByName(preferredAgentName, autoInstall);
|
|
132
160
|
}
|
|
133
161
|
async function updateDefaultAgentPreference(agentName) {
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
const
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
162
|
+
const agentsDir = getDextoGlobalPath("agents");
|
|
163
|
+
const installedRegistryPath = path.join(agentsDir, "registry.json");
|
|
164
|
+
const bundledRegistryPath = resolveBundledScript("agents/agent-registry.json");
|
|
165
|
+
const registriesToCheck = [
|
|
166
|
+
{ path: installedRegistryPath, name: "installed" },
|
|
167
|
+
{ path: bundledRegistryPath, name: "bundled" }
|
|
168
|
+
];
|
|
169
|
+
for (const registry of registriesToCheck) {
|
|
170
|
+
try {
|
|
171
|
+
const manager = new AgentManager(registry.path);
|
|
172
|
+
await manager.loadRegistry();
|
|
173
|
+
if (manager.hasAgent(agentName)) {
|
|
174
|
+
const { updateGlobalPreferences } = await import("./preferences/loader.js");
|
|
175
|
+
await updateGlobalPreferences({
|
|
176
|
+
defaults: { defaultAgent: agentName }
|
|
177
|
+
});
|
|
178
|
+
logger.info(`Updated default agent preference to: ${agentName}`);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
} catch (error) {
|
|
182
|
+
logger.debug(`Agent '${agentName}' not found in ${registry.name} registry: ${error}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
throw RegistryError.agentNotFound(agentName, []);
|
|
147
186
|
}
|
|
148
187
|
export {
|
|
149
188
|
resolveAgentPath,
|
|
@@ -31,7 +31,25 @@ const PROVIDER_API_KEY_MAP = {
|
|
|
31
31
|
google: ["GOOGLE_GENERATIVE_AI_API_KEY", "GOOGLE_API_KEY", "GEMINI_API_KEY"],
|
|
32
32
|
groq: ["GROQ_API_KEY"],
|
|
33
33
|
cohere: ["COHERE_API_KEY"],
|
|
34
|
-
xai: ["XAI_API_KEY", "X_AI_API_KEY"]
|
|
34
|
+
xai: ["XAI_API_KEY", "X_AI_API_KEY"],
|
|
35
|
+
openrouter: ["OPENROUTER_API_KEY"],
|
|
36
|
+
litellm: ["LITELLM_API_KEY", "LITELLM_KEY"],
|
|
37
|
+
glama: ["GLAMA_API_KEY"],
|
|
38
|
+
// Vertex uses ADC (Application Default Credentials), not API keys
|
|
39
|
+
// GOOGLE_APPLICATION_CREDENTIALS points to service account JSON (optional)
|
|
40
|
+
// Primary config is GOOGLE_VERTEX_PROJECT (required) + GOOGLE_VERTEX_LOCATION (optional)
|
|
41
|
+
vertex: [],
|
|
42
|
+
// Bedrock supports two auth methods:
|
|
43
|
+
// 1. AWS_BEARER_TOKEN_BEDROCK - Bedrock API key (simplest)
|
|
44
|
+
// 2. AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY + AWS_REGION (IAM credentials)
|
|
45
|
+
// AWS_SESSION_TOKEN (optional, for temporary credentials)
|
|
46
|
+
bedrock: ["AWS_BEARER_TOKEN_BEDROCK"],
|
|
47
|
+
// Local providers don't require API keys
|
|
48
|
+
local: [],
|
|
49
|
+
// Native node-llama-cpp execution
|
|
50
|
+
ollama: []
|
|
51
|
+
// Ollama server (may optionally use OLLAMA_API_KEY for remote servers)
|
|
52
|
+
// TODO: dexto: ['DEXTO_API_KEY'],
|
|
35
53
|
// perplexity: ['PERPLEXITY_API_KEY'],
|
|
36
54
|
// together: ['TOGETHER_API_KEY'],
|
|
37
55
|
// fireworks: ['FIREWORKS_API_KEY'],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-key-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/api-key-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;GAGG;AAGH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"api-key-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/api-key-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;GAGG;AAGH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,CA4B9D,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAelF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,CAGpE"}
|
|
@@ -6,7 +6,25 @@ const PROVIDER_API_KEY_MAP = {
|
|
|
6
6
|
google: ["GOOGLE_GENERATIVE_AI_API_KEY", "GOOGLE_API_KEY", "GEMINI_API_KEY"],
|
|
7
7
|
groq: ["GROQ_API_KEY"],
|
|
8
8
|
cohere: ["COHERE_API_KEY"],
|
|
9
|
-
xai: ["XAI_API_KEY", "X_AI_API_KEY"]
|
|
9
|
+
xai: ["XAI_API_KEY", "X_AI_API_KEY"],
|
|
10
|
+
openrouter: ["OPENROUTER_API_KEY"],
|
|
11
|
+
litellm: ["LITELLM_API_KEY", "LITELLM_KEY"],
|
|
12
|
+
glama: ["GLAMA_API_KEY"],
|
|
13
|
+
// Vertex uses ADC (Application Default Credentials), not API keys
|
|
14
|
+
// GOOGLE_APPLICATION_CREDENTIALS points to service account JSON (optional)
|
|
15
|
+
// Primary config is GOOGLE_VERTEX_PROJECT (required) + GOOGLE_VERTEX_LOCATION (optional)
|
|
16
|
+
vertex: [],
|
|
17
|
+
// Bedrock supports two auth methods:
|
|
18
|
+
// 1. AWS_BEARER_TOKEN_BEDROCK - Bedrock API key (simplest)
|
|
19
|
+
// 2. AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY + AWS_REGION (IAM credentials)
|
|
20
|
+
// AWS_SESSION_TOKEN (optional, for temporary credentials)
|
|
21
|
+
bedrock: ["AWS_BEARER_TOKEN_BEDROCK"],
|
|
22
|
+
// Local providers don't require API keys
|
|
23
|
+
local: [],
|
|
24
|
+
// Native node-llama-cpp execution
|
|
25
|
+
ollama: []
|
|
26
|
+
// Ollama server (may optionally use OLLAMA_API_KEY for remote servers)
|
|
27
|
+
// TODO: dexto: ['DEXTO_API_KEY'],
|
|
10
28
|
// perplexity: ['PERPLEXITY_API_KEY'],
|
|
11
29
|
// together: ['TOGETHER_API_KEY'],
|
|
12
30
|
// fireworks: ['FIREWORKS_API_KEY'],
|
|
@@ -18,6 +18,8 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var api_key_store_exports = {};
|
|
20
20
|
__export(api_key_store_exports, {
|
|
21
|
+
SHARED_API_KEY_PROVIDERS: () => SHARED_API_KEY_PROVIDERS,
|
|
22
|
+
determineApiKeyStorage: () => determineApiKeyStorage,
|
|
21
23
|
getProviderKeyStatus: () => getProviderKeyStatus,
|
|
22
24
|
listProviderKeyStatus: () => listProviderKeyStatus,
|
|
23
25
|
saveProviderApiKey: () => saveProviderApiKey
|
|
@@ -37,6 +39,27 @@ async function saveProviderApiKey(provider, apiKey, startPath) {
|
|
|
37
39
|
return { envVar, targetEnvPath };
|
|
38
40
|
}
|
|
39
41
|
function getProviderKeyStatus(provider) {
|
|
42
|
+
if (provider === "vertex") {
|
|
43
|
+
const projectId = process.env.GOOGLE_VERTEX_PROJECT;
|
|
44
|
+
return {
|
|
45
|
+
hasApiKey: Boolean(projectId && projectId.trim()),
|
|
46
|
+
envVar: "GOOGLE_VERTEX_PROJECT"
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (provider === "bedrock") {
|
|
50
|
+
const apiKey = process.env.AWS_BEARER_TOKEN_BEDROCK;
|
|
51
|
+
if (apiKey && apiKey.trim()) {
|
|
52
|
+
return {
|
|
53
|
+
hasApiKey: true,
|
|
54
|
+
envVar: "AWS_BEARER_TOKEN_BEDROCK"
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const region = process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION;
|
|
58
|
+
return {
|
|
59
|
+
hasApiKey: Boolean(region && region.trim()),
|
|
60
|
+
envVar: "AWS_REGION"
|
|
61
|
+
};
|
|
62
|
+
}
|
|
40
63
|
const envVar = (0, import_api_key_resolver.getPrimaryApiKeyEnvVar)(provider);
|
|
41
64
|
const key = (0, import_api_key_resolver.resolveApiKeyForProvider)(provider);
|
|
42
65
|
return { hasApiKey: Boolean(key && key.trim()), envVar };
|
|
@@ -48,8 +71,31 @@ function listProviderKeyStatus() {
|
|
|
48
71
|
}
|
|
49
72
|
return result;
|
|
50
73
|
}
|
|
74
|
+
const SHARED_API_KEY_PROVIDERS = ["glama", "openrouter", "litellm"];
|
|
75
|
+
function determineApiKeyStorage(provider, userEnteredKey, providerHasKey, existingProviderKey) {
|
|
76
|
+
const result = {
|
|
77
|
+
saveToProviderEnvVar: false,
|
|
78
|
+
saveAsPerModel: false
|
|
79
|
+
};
|
|
80
|
+
if (!userEnteredKey) {
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
const hasSharedEnvVarKey = SHARED_API_KEY_PROVIDERS.includes(provider);
|
|
84
|
+
if (hasSharedEnvVarKey) {
|
|
85
|
+
if (!providerHasKey) {
|
|
86
|
+
result.saveToProviderEnvVar = true;
|
|
87
|
+
} else if (existingProviderKey && userEnteredKey !== existingProviderKey) {
|
|
88
|
+
result.saveAsPerModel = true;
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
result.saveAsPerModel = true;
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
51
95
|
// Annotate the CommonJS export names for ESM import in node:
|
|
52
96
|
0 && (module.exports = {
|
|
97
|
+
SHARED_API_KEY_PROVIDERS,
|
|
98
|
+
determineApiKeyStorage,
|
|
53
99
|
getProviderKeyStatus,
|
|
54
100
|
listProviderKeyStatus,
|
|
55
101
|
saveProviderApiKey
|
|
@@ -15,4 +15,31 @@ export declare function listProviderKeyStatus(): Record<string, {
|
|
|
15
15
|
hasApiKey: boolean;
|
|
16
16
|
envVar: string;
|
|
17
17
|
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Providers that use a shared env var for API keys (vs per-endpoint like openai-compatible).
|
|
20
|
+
* For these providers, we save to the env var if none exists, otherwise per-model override.
|
|
21
|
+
*/
|
|
22
|
+
export declare const SHARED_API_KEY_PROVIDERS: readonly ["glama", "openrouter", "litellm"];
|
|
23
|
+
export type ApiKeyStorageStrategy = {
|
|
24
|
+
/** Save the key to provider env var (e.g., GLAMA_API_KEY) */
|
|
25
|
+
saveToProviderEnvVar: boolean;
|
|
26
|
+
/** Save the key as per-model override in custom model config */
|
|
27
|
+
saveAsPerModel: boolean;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Determine where to store an API key for a custom model.
|
|
31
|
+
*
|
|
32
|
+
* Logic:
|
|
33
|
+
* - For glama/openrouter/litellm (shared env var providers):
|
|
34
|
+
* - If NO provider key exists → save to provider env var for reuse
|
|
35
|
+
* - If provider key EXISTS and user entered SAME key → don't save (uses fallback)
|
|
36
|
+
* - If provider key EXISTS and user entered DIFFERENT key → save as per-model override
|
|
37
|
+
* - For openai-compatible: always save as per-model (each endpoint needs own key)
|
|
38
|
+
*
|
|
39
|
+
* @param provider - The custom model provider
|
|
40
|
+
* @param userEnteredKey - The API key entered by user (trimmed, may be empty)
|
|
41
|
+
* @param providerHasKey - Whether the provider already has a key configured
|
|
42
|
+
* @param existingProviderKey - The existing provider key value (for comparison)
|
|
43
|
+
*/
|
|
44
|
+
export declare function determineApiKeyStorage(provider: string, userEnteredKey: string | undefined, providerHasKey: boolean, existingProviderKey: string | undefined): ApiKeyStorageStrategy;
|
|
18
45
|
//# sourceMappingURL=api-key-store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-key-store.d.ts","sourceRoot":"","sources":["../../src/utils/api-key-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C;;;GAGG;AACH,wBAAsB,kBAAkB,CACpC,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CAYpD;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,WAAW,GAAG;IACzD,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAClB,
|
|
1
|
+
{"version":3,"file":"api-key-store.d.ts","sourceRoot":"","sources":["../../src/utils/api-key-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C;;;GAGG;AACH,wBAAsB,kBAAkB,CACpC,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CAYpD;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,WAAW,GAAG;IACzD,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAClB,CA4CA;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAM9F;AAED;;;GAGG;AACH,eAAO,MAAM,wBAAwB,6CAA8C,CAAC;AAEpF,MAAM,MAAM,qBAAqB,GAAG;IAChC,6DAA6D;IAC7D,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gEAAgE;IAChE,cAAc,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAClC,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,cAAc,EAAE,OAAO,EACvB,mBAAmB,EAAE,MAAM,GAAG,SAAS,GACxC,qBAAqB,CA2BvB"}
|
|
@@ -12,6 +12,27 @@ async function saveProviderApiKey(provider, apiKey, startPath) {
|
|
|
12
12
|
return { envVar, targetEnvPath };
|
|
13
13
|
}
|
|
14
14
|
function getProviderKeyStatus(provider) {
|
|
15
|
+
if (provider === "vertex") {
|
|
16
|
+
const projectId = process.env.GOOGLE_VERTEX_PROJECT;
|
|
17
|
+
return {
|
|
18
|
+
hasApiKey: Boolean(projectId && projectId.trim()),
|
|
19
|
+
envVar: "GOOGLE_VERTEX_PROJECT"
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
if (provider === "bedrock") {
|
|
23
|
+
const apiKey = process.env.AWS_BEARER_TOKEN_BEDROCK;
|
|
24
|
+
if (apiKey && apiKey.trim()) {
|
|
25
|
+
return {
|
|
26
|
+
hasApiKey: true,
|
|
27
|
+
envVar: "AWS_BEARER_TOKEN_BEDROCK"
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const region = process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION;
|
|
31
|
+
return {
|
|
32
|
+
hasApiKey: Boolean(region && region.trim()),
|
|
33
|
+
envVar: "AWS_REGION"
|
|
34
|
+
};
|
|
35
|
+
}
|
|
15
36
|
const envVar = getPrimaryApiKeyEnvVar(provider);
|
|
16
37
|
const key = resolveApiKeyForProvider(provider);
|
|
17
38
|
return { hasApiKey: Boolean(key && key.trim()), envVar };
|
|
@@ -23,7 +44,30 @@ function listProviderKeyStatus() {
|
|
|
23
44
|
}
|
|
24
45
|
return result;
|
|
25
46
|
}
|
|
47
|
+
const SHARED_API_KEY_PROVIDERS = ["glama", "openrouter", "litellm"];
|
|
48
|
+
function determineApiKeyStorage(provider, userEnteredKey, providerHasKey, existingProviderKey) {
|
|
49
|
+
const result = {
|
|
50
|
+
saveToProviderEnvVar: false,
|
|
51
|
+
saveAsPerModel: false
|
|
52
|
+
};
|
|
53
|
+
if (!userEnteredKey) {
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
const hasSharedEnvVarKey = SHARED_API_KEY_PROVIDERS.includes(provider);
|
|
57
|
+
if (hasSharedEnvVarKey) {
|
|
58
|
+
if (!providerHasKey) {
|
|
59
|
+
result.saveToProviderEnvVar = true;
|
|
60
|
+
} else if (existingProviderKey && userEnteredKey !== existingProviderKey) {
|
|
61
|
+
result.saveAsPerModel = true;
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
result.saveAsPerModel = true;
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
26
68
|
export {
|
|
69
|
+
SHARED_API_KEY_PROVIDERS,
|
|
70
|
+
determineApiKeyStorage,
|
|
27
71
|
getProviderKeyStatus,
|
|
28
72
|
listProviderKeyStatus,
|
|
29
73
|
saveProviderApiKey
|
package/dist/utils/env-file.cjs
CHANGED
|
@@ -33,84 +33,36 @@ __export(env_file_exports, {
|
|
|
33
33
|
module.exports = __toCommonJS(env_file_exports);
|
|
34
34
|
var path = __toESM(require("node:path"), 1);
|
|
35
35
|
var import_node_fs = require("node:fs");
|
|
36
|
-
const DEXTO_ENV_KEYS = [
|
|
37
|
-
"OPENAI_API_KEY",
|
|
38
|
-
"ANTHROPIC_API_KEY",
|
|
39
|
-
"GOOGLE_GENERATIVE_AI_API_KEY",
|
|
40
|
-
"GROQ_API_KEY",
|
|
41
|
-
"COHERE_API_KEY",
|
|
42
|
-
"XAI_API_KEY",
|
|
43
|
-
"DEXTO_LOG_LEVEL"
|
|
44
|
-
];
|
|
45
|
-
function isDextoEnvKey(value) {
|
|
46
|
-
return DEXTO_ENV_KEYS.includes(value);
|
|
47
|
-
}
|
|
48
36
|
async function updateEnvFile(envFilePath, updates) {
|
|
49
37
|
await import_node_fs.promises.mkdir(path.dirname(envFilePath), { recursive: true });
|
|
50
|
-
let
|
|
38
|
+
let content = "";
|
|
51
39
|
try {
|
|
52
|
-
|
|
53
|
-
envLines = existingEnv.split("\n");
|
|
40
|
+
content = await import_node_fs.promises.readFile(envFilePath, "utf8");
|
|
54
41
|
} catch {
|
|
55
42
|
}
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
43
|
+
const lines = content.split("\n");
|
|
44
|
+
const updatedKeys = /* @__PURE__ */ new Set();
|
|
45
|
+
const updatedLines = lines.map((line) => {
|
|
46
|
+
const match = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
|
|
47
|
+
if (match && match[1] && match[1] in updates) {
|
|
48
|
+
const key = match[1];
|
|
49
|
+
updatedKeys.add(key);
|
|
50
|
+
return `${key}=${updates[key]}`;
|
|
62
51
|
}
|
|
52
|
+
return line;
|
|
63
53
|
});
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return [key, value];
|
|
71
|
-
})
|
|
72
|
-
);
|
|
73
|
-
const sectionHeader = "## Dexto env variables";
|
|
74
|
-
const headerIndex = envLines.findIndex((line) => line.trim() === sectionHeader);
|
|
75
|
-
let contentLines;
|
|
76
|
-
if (headerIndex !== -1) {
|
|
77
|
-
const beforeSection = envLines.slice(0, headerIndex);
|
|
78
|
-
let sectionEnd = headerIndex + 1;
|
|
79
|
-
while (sectionEnd < envLines.length && envLines[sectionEnd]?.trim() !== "") {
|
|
80
|
-
sectionEnd++;
|
|
81
|
-
}
|
|
82
|
-
if (sectionEnd < envLines.length && envLines[sectionEnd]?.trim() === "") {
|
|
83
|
-
sectionEnd++;
|
|
54
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
55
|
+
if (!updatedKeys.has(key)) {
|
|
56
|
+
if (updatedLines.length > 0 && updatedLines[updatedLines.length - 1] !== "") {
|
|
57
|
+
updatedLines.push("");
|
|
58
|
+
}
|
|
59
|
+
updatedLines.push(`${key}=${value}`);
|
|
84
60
|
}
|
|
85
|
-
const afterSection = envLines.slice(sectionEnd);
|
|
86
|
-
contentLines = [...beforeSection, ...afterSection];
|
|
87
|
-
} else {
|
|
88
|
-
contentLines = envLines;
|
|
89
61
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const match = line.match(/^([A-Z0-9_]+)=(.*)$/);
|
|
93
|
-
if (match && match[1] && isDextoEnvKey(match[1])) {
|
|
94
|
-
const value = match[2] ?? "";
|
|
95
|
-
existingEnvVars[match[1]] = value;
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
if (contentLines.length > 0) {
|
|
99
|
-
if (contentLines[contentLines.length - 1]?.trim() !== "") {
|
|
100
|
-
contentLines.push("");
|
|
101
|
-
}
|
|
102
|
-
} else {
|
|
103
|
-
contentLines.push("");
|
|
104
|
-
}
|
|
105
|
-
contentLines.push(sectionHeader);
|
|
106
|
-
for (const key of DEXTO_ENV_KEYS) {
|
|
107
|
-
if (key in existingEnvVars && !(key in updates)) {
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
contentLines.push(`${key}=${updatedValues[key]}`);
|
|
62
|
+
if (updatedLines[updatedLines.length - 1] !== "") {
|
|
63
|
+
updatedLines.push("");
|
|
111
64
|
}
|
|
112
|
-
|
|
113
|
-
await import_node_fs.promises.writeFile(envFilePath, contentLines.join("\n"), "utf8");
|
|
65
|
+
await import_node_fs.promises.writeFile(envFilePath, updatedLines.join("\n"), "utf8");
|
|
114
66
|
}
|
|
115
67
|
// Annotate the CommonJS export names for ESM import in node:
|
|
116
68
|
0 && (module.exports = {
|
package/dist/utils/env-file.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Update a .env file with
|
|
2
|
+
* Update a .env file with the provided key-value pairs.
|
|
3
|
+
* Existing keys are updated in place, new keys are appended.
|
|
3
4
|
*/
|
|
4
5
|
export declare function updateEnvFile(envFilePath: string, updates: Record<string, string>): Promise<void>;
|
|
5
6
|
//# sourceMappingURL=env-file.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env-file.d.ts","sourceRoot":"","sources":["../../src/utils/env-file.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"env-file.d.ts","sourceRoot":"","sources":["../../src/utils/env-file.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAsB,aAAa,CAC/B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC,CAyCf"}
|
package/dist/utils/env-file.js
CHANGED
|
@@ -1,83 +1,35 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { promises as fs } from "node:fs";
|
|
3
|
-
const DEXTO_ENV_KEYS = [
|
|
4
|
-
"OPENAI_API_KEY",
|
|
5
|
-
"ANTHROPIC_API_KEY",
|
|
6
|
-
"GOOGLE_GENERATIVE_AI_API_KEY",
|
|
7
|
-
"GROQ_API_KEY",
|
|
8
|
-
"COHERE_API_KEY",
|
|
9
|
-
"XAI_API_KEY",
|
|
10
|
-
"DEXTO_LOG_LEVEL"
|
|
11
|
-
];
|
|
12
|
-
function isDextoEnvKey(value) {
|
|
13
|
-
return DEXTO_ENV_KEYS.includes(value);
|
|
14
|
-
}
|
|
15
3
|
async function updateEnvFile(envFilePath, updates) {
|
|
16
4
|
await fs.mkdir(path.dirname(envFilePath), { recursive: true });
|
|
17
|
-
let
|
|
5
|
+
let content = "";
|
|
18
6
|
try {
|
|
19
|
-
|
|
20
|
-
envLines = existingEnv.split("\n");
|
|
7
|
+
content = await fs.readFile(envFilePath, "utf8");
|
|
21
8
|
} catch {
|
|
22
9
|
}
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
10
|
+
const lines = content.split("\n");
|
|
11
|
+
const updatedKeys = /* @__PURE__ */ new Set();
|
|
12
|
+
const updatedLines = lines.map((line) => {
|
|
13
|
+
const match = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
|
|
14
|
+
if (match && match[1] && match[1] in updates) {
|
|
15
|
+
const key = match[1];
|
|
16
|
+
updatedKeys.add(key);
|
|
17
|
+
return `${key}=${updates[key]}`;
|
|
29
18
|
}
|
|
19
|
+
return line;
|
|
30
20
|
});
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return [key, value];
|
|
38
|
-
})
|
|
39
|
-
);
|
|
40
|
-
const sectionHeader = "## Dexto env variables";
|
|
41
|
-
const headerIndex = envLines.findIndex((line) => line.trim() === sectionHeader);
|
|
42
|
-
let contentLines;
|
|
43
|
-
if (headerIndex !== -1) {
|
|
44
|
-
const beforeSection = envLines.slice(0, headerIndex);
|
|
45
|
-
let sectionEnd = headerIndex + 1;
|
|
46
|
-
while (sectionEnd < envLines.length && envLines[sectionEnd]?.trim() !== "") {
|
|
47
|
-
sectionEnd++;
|
|
48
|
-
}
|
|
49
|
-
if (sectionEnd < envLines.length && envLines[sectionEnd]?.trim() === "") {
|
|
50
|
-
sectionEnd++;
|
|
21
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
22
|
+
if (!updatedKeys.has(key)) {
|
|
23
|
+
if (updatedLines.length > 0 && updatedLines[updatedLines.length - 1] !== "") {
|
|
24
|
+
updatedLines.push("");
|
|
25
|
+
}
|
|
26
|
+
updatedLines.push(`${key}=${value}`);
|
|
51
27
|
}
|
|
52
|
-
const afterSection = envLines.slice(sectionEnd);
|
|
53
|
-
contentLines = [...beforeSection, ...afterSection];
|
|
54
|
-
} else {
|
|
55
|
-
contentLines = envLines;
|
|
56
28
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const match = line.match(/^([A-Z0-9_]+)=(.*)$/);
|
|
60
|
-
if (match && match[1] && isDextoEnvKey(match[1])) {
|
|
61
|
-
const value = match[2] ?? "";
|
|
62
|
-
existingEnvVars[match[1]] = value;
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
if (contentLines.length > 0) {
|
|
66
|
-
if (contentLines[contentLines.length - 1]?.trim() !== "") {
|
|
67
|
-
contentLines.push("");
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
contentLines.push("");
|
|
71
|
-
}
|
|
72
|
-
contentLines.push(sectionHeader);
|
|
73
|
-
for (const key of DEXTO_ENV_KEYS) {
|
|
74
|
-
if (key in existingEnvVars && !(key in updates)) {
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
contentLines.push(`${key}=${updatedValues[key]}`);
|
|
29
|
+
if (updatedLines[updatedLines.length - 1] !== "") {
|
|
30
|
+
updatedLines.push("");
|
|
78
31
|
}
|
|
79
|
-
|
|
80
|
-
await fs.writeFile(envFilePath, contentLines.join("\n"), "utf8");
|
|
32
|
+
await fs.writeFile(envFilePath, updatedLines.join("\n"), "utf8");
|
|
81
33
|
}
|
|
82
34
|
export {
|
|
83
35
|
updateEnvFile
|