@sylphx/flow 1.0.1 ā 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/package.json +10 -9
- package/src/commands/codebase-command.ts +168 -0
- package/src/commands/flow-command.ts +1137 -0
- package/src/commands/flow-orchestrator.ts +296 -0
- package/src/commands/hook-command.ts +444 -0
- package/src/commands/init-command.ts +92 -0
- package/src/commands/init-core.ts +322 -0
- package/src/commands/knowledge-command.ts +161 -0
- package/src/commands/run-command.ts +120 -0
- package/src/components/benchmark-monitor.tsx +331 -0
- package/src/components/reindex-progress.tsx +261 -0
- package/src/composables/functional/index.ts +14 -0
- package/src/composables/functional/useEnvironment.ts +171 -0
- package/src/composables/functional/useFileSystem.ts +139 -0
- package/src/composables/index.ts +5 -0
- package/src/composables/useEnv.ts +13 -0
- package/src/composables/useRuntimeConfig.ts +27 -0
- package/src/composables/useTargetConfig.ts +45 -0
- package/src/config/ai-config.ts +376 -0
- package/src/config/constants.ts +35 -0
- package/src/config/index.ts +27 -0
- package/src/config/rules.ts +43 -0
- package/src/config/servers.ts +371 -0
- package/src/config/targets.ts +126 -0
- package/src/core/agent-loader.ts +141 -0
- package/src/core/agent-manager.ts +174 -0
- package/src/core/ai-sdk.ts +603 -0
- package/src/core/app-factory.ts +381 -0
- package/src/core/builtin-agents.ts +9 -0
- package/src/core/command-system.ts +550 -0
- package/src/core/config-system.ts +550 -0
- package/src/core/connection-pool.ts +390 -0
- package/src/core/di-container.ts +155 -0
- package/src/core/error-handling.ts +519 -0
- package/src/core/formatting/bytes.test.ts +115 -0
- package/src/core/formatting/bytes.ts +64 -0
- package/src/core/functional/async.ts +313 -0
- package/src/core/functional/either.ts +109 -0
- package/src/core/functional/error-handler.ts +135 -0
- package/src/core/functional/error-types.ts +311 -0
- package/src/core/functional/index.ts +19 -0
- package/src/core/functional/option.ts +142 -0
- package/src/core/functional/pipe.ts +189 -0
- package/src/core/functional/result.ts +204 -0
- package/src/core/functional/validation.ts +138 -0
- package/src/core/headless-display.ts +96 -0
- package/src/core/index.ts +6 -0
- package/src/core/installers/file-installer.ts +303 -0
- package/src/core/installers/mcp-installer.ts +213 -0
- package/src/core/interfaces/index.ts +22 -0
- package/src/core/interfaces/repository.interface.ts +91 -0
- package/src/core/interfaces/service.interface.ts +133 -0
- package/src/core/interfaces.ts +129 -0
- package/src/core/loop-controller.ts +200 -0
- package/src/core/result.ts +351 -0
- package/src/core/rule-loader.ts +147 -0
- package/src/core/rule-manager.ts +240 -0
- package/src/core/service-config.ts +252 -0
- package/src/core/session-service.ts +121 -0
- package/src/core/state-detector.ts +389 -0
- package/src/core/storage-factory.ts +115 -0
- package/src/core/stream-handler.ts +288 -0
- package/src/core/target-manager.ts +161 -0
- package/src/core/type-utils.ts +427 -0
- package/src/core/unified-storage.ts +456 -0
- package/src/core/upgrade-manager.ts +300 -0
- package/src/core/validation/limit.test.ts +155 -0
- package/src/core/validation/limit.ts +46 -0
- package/src/core/validation/query.test.ts +44 -0
- package/src/core/validation/query.ts +20 -0
- package/src/db/auto-migrate.ts +322 -0
- package/src/db/base-database-client.ts +144 -0
- package/src/db/cache-db.ts +218 -0
- package/src/db/cache-schema.ts +75 -0
- package/src/db/database.ts +70 -0
- package/src/db/index.ts +252 -0
- package/src/db/memory-db.ts +153 -0
- package/src/db/memory-schema.ts +29 -0
- package/src/db/schema.ts +289 -0
- package/src/db/session-repository.ts +733 -0
- package/src/domains/codebase/index.ts +5 -0
- package/src/domains/codebase/tools.ts +139 -0
- package/src/domains/index.ts +8 -0
- package/src/domains/knowledge/index.ts +10 -0
- package/src/domains/knowledge/resources.ts +537 -0
- package/src/domains/knowledge/tools.ts +174 -0
- package/src/domains/utilities/index.ts +6 -0
- package/src/domains/utilities/time/index.ts +5 -0
- package/src/domains/utilities/time/tools.ts +291 -0
- package/src/index.ts +211 -0
- package/src/services/agent-service.ts +273 -0
- package/src/services/claude-config-service.ts +252 -0
- package/src/services/config-service.ts +258 -0
- package/src/services/evaluation-service.ts +271 -0
- package/src/services/functional/evaluation-logic.ts +296 -0
- package/src/services/functional/file-processor.ts +273 -0
- package/src/services/functional/index.ts +12 -0
- package/src/services/index.ts +13 -0
- package/src/services/mcp-service.ts +432 -0
- package/src/services/memory.service.ts +476 -0
- package/src/services/search/base-indexer.ts +156 -0
- package/src/services/search/codebase-indexer-types.ts +38 -0
- package/src/services/search/codebase-indexer.ts +647 -0
- package/src/services/search/embeddings-provider.ts +455 -0
- package/src/services/search/embeddings.ts +316 -0
- package/src/services/search/functional-indexer.ts +323 -0
- package/src/services/search/index.ts +27 -0
- package/src/services/search/indexer.ts +380 -0
- package/src/services/search/knowledge-indexer.ts +422 -0
- package/src/services/search/semantic-search.ts +244 -0
- package/src/services/search/tfidf.ts +559 -0
- package/src/services/search/unified-search-service.ts +888 -0
- package/src/services/smart-config-service.ts +385 -0
- package/src/services/storage/cache-storage.ts +487 -0
- package/src/services/storage/drizzle-storage.ts +581 -0
- package/src/services/storage/index.ts +15 -0
- package/src/services/storage/lancedb-vector-storage.ts +494 -0
- package/src/services/storage/memory-storage.ts +268 -0
- package/src/services/storage/separated-storage.ts +467 -0
- package/src/services/storage/vector-storage.ts +13 -0
- package/src/shared/agents/index.ts +63 -0
- package/src/shared/files/index.ts +99 -0
- package/src/shared/index.ts +32 -0
- package/src/shared/logging/index.ts +24 -0
- package/src/shared/processing/index.ts +153 -0
- package/src/shared/types/index.ts +25 -0
- package/src/targets/claude-code.ts +574 -0
- package/src/targets/functional/claude-code-logic.ts +185 -0
- package/src/targets/functional/index.ts +6 -0
- package/src/targets/opencode.ts +529 -0
- package/src/types/agent.types.ts +32 -0
- package/src/types/api/batch.ts +108 -0
- package/src/types/api/errors.ts +118 -0
- package/src/types/api/index.ts +55 -0
- package/src/types/api/requests.ts +76 -0
- package/src/types/api/responses.ts +180 -0
- package/src/types/api/websockets.ts +85 -0
- package/src/types/api.types.ts +9 -0
- package/src/types/benchmark.ts +49 -0
- package/src/types/cli.types.ts +87 -0
- package/src/types/common.types.ts +35 -0
- package/src/types/database.types.ts +510 -0
- package/src/types/mcp-config.types.ts +448 -0
- package/src/types/mcp.types.ts +69 -0
- package/src/types/memory-types.ts +63 -0
- package/src/types/provider.types.ts +28 -0
- package/src/types/rule.types.ts +24 -0
- package/src/types/session.types.ts +214 -0
- package/src/types/target-config.types.ts +295 -0
- package/src/types/target.types.ts +140 -0
- package/src/types/todo.types.ts +25 -0
- package/src/types.ts +40 -0
- package/src/utils/advanced-tokenizer.ts +191 -0
- package/src/utils/agent-enhancer.ts +114 -0
- package/src/utils/ai-model-fetcher.ts +19 -0
- package/src/utils/async-file-operations.ts +516 -0
- package/src/utils/audio-player.ts +345 -0
- package/src/utils/cli-output.ts +266 -0
- package/src/utils/codebase-helpers.ts +211 -0
- package/src/utils/console-ui.ts +79 -0
- package/src/utils/database-errors.ts +140 -0
- package/src/utils/debug-logger.ts +49 -0
- package/src/utils/error-handler.ts +53 -0
- package/src/utils/file-operations.ts +310 -0
- package/src/utils/file-scanner.ts +259 -0
- package/src/utils/functional/array.ts +355 -0
- package/src/utils/functional/index.ts +15 -0
- package/src/utils/functional/object.ts +279 -0
- package/src/utils/functional/string.ts +281 -0
- package/src/utils/functional.ts +543 -0
- package/src/utils/help.ts +20 -0
- package/src/utils/immutable-cache.ts +106 -0
- package/src/utils/index.ts +78 -0
- package/src/utils/jsonc.ts +158 -0
- package/src/utils/logger.ts +396 -0
- package/src/utils/mcp-config.ts +249 -0
- package/src/utils/memory-tui.ts +414 -0
- package/src/utils/models-dev.ts +91 -0
- package/src/utils/notifications.ts +169 -0
- package/src/utils/object-utils.ts +51 -0
- package/src/utils/parallel-operations.ts +487 -0
- package/src/utils/paths.ts +143 -0
- package/src/utils/process-manager.ts +155 -0
- package/src/utils/prompts.ts +120 -0
- package/src/utils/search-tool-builder.ts +214 -0
- package/src/utils/secret-utils.ts +179 -0
- package/src/utils/security.ts +537 -0
- package/src/utils/session-manager.ts +168 -0
- package/src/utils/session-title.ts +87 -0
- package/src/utils/settings.ts +182 -0
- package/src/utils/simplified-errors.ts +410 -0
- package/src/utils/sync-utils.ts +159 -0
- package/src/utils/target-config.ts +570 -0
- package/src/utils/target-utils.ts +394 -0
- package/src/utils/template-engine.ts +94 -0
- package/src/utils/test-audio.ts +71 -0
- package/src/utils/todo-context.ts +46 -0
- package/src/utils/token-counter.ts +288 -0
- package/dist/index.d.ts +0 -10
- package/dist/index.js +0 -59554
- package/dist/lancedb.linux-x64-gnu-b7f0jgsz.node +0 -0
- package/dist/lancedb.linux-x64-musl-tgcv22rx.node +0 -0
- package/dist/shared/chunk-25dwp0dp.js +0 -89
- package/dist/shared/chunk-3pjb6063.js +0 -208
- package/dist/shared/chunk-4d6ydpw7.js +0 -2854
- package/dist/shared/chunk-4wjcadjk.js +0 -225
- package/dist/shared/chunk-5j4w74t6.js +0 -30
- package/dist/shared/chunk-5j8m3dh3.js +0 -58
- package/dist/shared/chunk-5thh3qem.js +0 -91
- package/dist/shared/chunk-6g9xy73m.js +0 -252
- package/dist/shared/chunk-7eq34c42.js +0 -23
- package/dist/shared/chunk-c2gwgx3r.js +0 -115
- package/dist/shared/chunk-cjd3mk4c.js +0 -1320
- package/dist/shared/chunk-g5cv6703.js +0 -368
- package/dist/shared/chunk-hpkhykhq.js +0 -574
- package/dist/shared/chunk-m2322pdk.js +0 -122
- package/dist/shared/chunk-nd5fdvaq.js +0 -26
- package/dist/shared/chunk-pgd3m6zf.js +0 -108
- package/dist/shared/chunk-qk8n91hw.js +0 -494
- package/dist/shared/chunk-rkkn8szp.js +0 -16855
- package/dist/shared/chunk-t16rfxh0.js +0 -61
- package/dist/shared/chunk-t4fbfa5v.js +0 -19
- package/dist/shared/chunk-t77h86w6.js +0 -276
- package/dist/shared/chunk-v0ez4aef.js +0 -71
- package/dist/shared/chunk-v29j2r3s.js +0 -32051
- package/dist/shared/chunk-vfbc6ew5.js +0 -765
- package/dist/shared/chunk-vmeqwm1c.js +0 -204
- package/dist/shared/chunk-x66eh37x.js +0 -137
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline';
|
|
2
|
+
import {
|
|
3
|
+
getAllEnvVars,
|
|
4
|
+
getOptionalEnvVars,
|
|
5
|
+
getRequiredEnvVars,
|
|
6
|
+
getSecretEnvVars,
|
|
7
|
+
MCP_SERVER_REGISTRY,
|
|
8
|
+
type MCPServerID,
|
|
9
|
+
} from '../config/servers.js';
|
|
10
|
+
import { targetManager } from '../core/target-manager.js';
|
|
11
|
+
import { resolveConfig } from '../services/mcp-service.js';
|
|
12
|
+
import { deleteNestedProperty, getNestedProperty, setNestedProperty } from './object-utils.js';
|
|
13
|
+
import { secretUtils } from './secret-utils.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Target-specific MCP configuration utilities
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Add MCP servers to a target's configuration
|
|
21
|
+
*/
|
|
22
|
+
export async function addMCPServersToTarget(
|
|
23
|
+
cwd: string,
|
|
24
|
+
targetId: string,
|
|
25
|
+
serverTypes: MCPServerID[]
|
|
26
|
+
): Promise<void> {
|
|
27
|
+
const targetOption = targetManager.getTarget(targetId);
|
|
28
|
+
|
|
29
|
+
if (targetOption._tag === 'None') {
|
|
30
|
+
throw new Error(`Target not found: ${targetId}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const target = targetOption.value;
|
|
34
|
+
|
|
35
|
+
if (!target.setupMCP) {
|
|
36
|
+
throw new Error(`Target ${targetId} does not support MCP servers`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Read current configuration
|
|
40
|
+
const config = await target.readConfig(cwd);
|
|
41
|
+
|
|
42
|
+
// Initialize MCP section if it doesn't exist
|
|
43
|
+
const mcpConfigPath = target.config.mcpConfigPath;
|
|
44
|
+
const mcpSection = getNestedProperty(config, mcpConfigPath) || {};
|
|
45
|
+
setNestedProperty(config, mcpConfigPath, mcpSection);
|
|
46
|
+
|
|
47
|
+
let addedCount = 0;
|
|
48
|
+
|
|
49
|
+
// Add each requested server
|
|
50
|
+
for (const serverType of serverTypes) {
|
|
51
|
+
const server = MCP_SERVER_REGISTRY[serverType];
|
|
52
|
+
if (!server) {
|
|
53
|
+
console.warn(`Warning: Unknown MCP server type: ${serverType}`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (mcpSection[server.name]) {
|
|
58
|
+
console.log(`ā¹ļø MCP server already exists: ${server.name}`);
|
|
59
|
+
} else {
|
|
60
|
+
const _transformedConfig = target.transformMCPConfig(server.config, server.id);
|
|
61
|
+
|
|
62
|
+
// Apply dynamic command and args generation (only for CLI servers)
|
|
63
|
+
let resolvedCommand: unknown;
|
|
64
|
+
let resolvedArgs: unknown;
|
|
65
|
+
|
|
66
|
+
if (server.config.command || server.config.args) {
|
|
67
|
+
resolvedCommand = server.config.command
|
|
68
|
+
? await resolveConfig(server.config.command)
|
|
69
|
+
: undefined;
|
|
70
|
+
resolvedArgs = server.config.args ? await resolveConfig(server.config.args) : [];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Update the config with resolved values - target will handle format
|
|
74
|
+
const updatedConfig = {
|
|
75
|
+
...server.config,
|
|
76
|
+
command: resolvedCommand,
|
|
77
|
+
args: resolvedArgs,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Let target handle the transformation
|
|
81
|
+
const finalConfig = target.transformMCPConfig(updatedConfig, server.id);
|
|
82
|
+
|
|
83
|
+
mcpSection[server.name] = finalConfig;
|
|
84
|
+
console.log(`š¦ Added MCP server: ${server.name} (${server.description})`);
|
|
85
|
+
addedCount++;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Write the updated configuration
|
|
90
|
+
await target.writeConfig(cwd, config);
|
|
91
|
+
console.log(`ā Updated ${target.config.configFile} with ${addedCount} new MCP server(s)`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Remove MCP servers from a target's configuration
|
|
96
|
+
*/
|
|
97
|
+
export async function removeMCPServersFromTarget(
|
|
98
|
+
cwd: string,
|
|
99
|
+
targetId: string,
|
|
100
|
+
serverTypes: MCPServerID[]
|
|
101
|
+
): Promise<void> {
|
|
102
|
+
const targetOption = targetManager.getTarget(targetId);
|
|
103
|
+
|
|
104
|
+
if (targetOption._tag === 'None') {
|
|
105
|
+
throw new Error(`Target not found: ${targetId}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const target = targetOption.value;
|
|
109
|
+
|
|
110
|
+
// Read current configuration
|
|
111
|
+
const config = await target.readConfig(cwd);
|
|
112
|
+
|
|
113
|
+
// Get MCP section
|
|
114
|
+
const mcpConfigPath = target.config.mcpConfigPath;
|
|
115
|
+
const mcpSection = getNestedProperty(config, mcpConfigPath);
|
|
116
|
+
|
|
117
|
+
if (!mcpSection) {
|
|
118
|
+
console.log('ā¹ļø No MCP servers configured');
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let removedCount = 0;
|
|
123
|
+
|
|
124
|
+
// Remove each requested server
|
|
125
|
+
for (const serverType of serverTypes) {
|
|
126
|
+
const server = MCP_SERVER_REGISTRY[serverType];
|
|
127
|
+
if (!server) {
|
|
128
|
+
console.warn(`Warning: Unknown MCP server type: ${serverType}`);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (mcpSection[server.name]) {
|
|
133
|
+
delete mcpSection[server.name];
|
|
134
|
+
console.log(`šļø Removed MCP server: ${server.name}`);
|
|
135
|
+
removedCount++;
|
|
136
|
+
} else {
|
|
137
|
+
console.log(`ā¹ļø MCP server not found: ${server.name}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Remove MCP section if it's empty
|
|
142
|
+
if (Object.keys(mcpSection).length === 0) {
|
|
143
|
+
deleteNestedProperty(config, mcpConfigPath);
|
|
144
|
+
} else {
|
|
145
|
+
setNestedProperty(config, mcpConfigPath, mcpSection);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Write the updated configuration
|
|
149
|
+
await target.writeConfig(cwd, config);
|
|
150
|
+
console.log(`ā Updated ${target.config.configFile} (removed ${removedCount} MCP server(s))`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* List currently configured MCP servers for a target
|
|
155
|
+
*/
|
|
156
|
+
export async function listMCPServersForTarget(cwd: string, targetId: string): Promise<void> {
|
|
157
|
+
const targetOption = targetManager.getTarget(targetId);
|
|
158
|
+
|
|
159
|
+
if (targetOption._tag === 'None') {
|
|
160
|
+
throw new Error(`Target not found: ${targetId}`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const target = targetOption.value;
|
|
164
|
+
|
|
165
|
+
// Read current configuration
|
|
166
|
+
const config = await target.readConfig(cwd);
|
|
167
|
+
|
|
168
|
+
// Get MCP section
|
|
169
|
+
const mcpConfigPath = target.config.mcpConfigPath;
|
|
170
|
+
const mcpSection = getNestedProperty(config, mcpConfigPath);
|
|
171
|
+
|
|
172
|
+
if (!mcpSection || Object.keys(mcpSection).length === 0) {
|
|
173
|
+
console.log('ā¹ļø No MCP servers configured');
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
console.log(`š Currently configured MCP servers for ${target.name}:`);
|
|
178
|
+
console.log('');
|
|
179
|
+
|
|
180
|
+
for (const [name, serverConfig] of Object.entries(mcpSection)) {
|
|
181
|
+
let configInfo = '';
|
|
182
|
+
if (serverConfig && typeof serverConfig === 'object' && 'type' in serverConfig) {
|
|
183
|
+
if (serverConfig.type === 'local') {
|
|
184
|
+
configInfo = (serverConfig as any).command?.join(' ') || 'Unknown command';
|
|
185
|
+
} else if (serverConfig.type === 'remote') {
|
|
186
|
+
configInfo = `HTTP: ${(serverConfig as any).url}`;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
console.log(` ⢠${name}: ${configInfo}`);
|
|
191
|
+
|
|
192
|
+
// Find the server type for additional info
|
|
193
|
+
const serverInfo = Object.values(MCP_SERVER_REGISTRY).find((s) => s.name === name);
|
|
194
|
+
if (serverInfo) {
|
|
195
|
+
console.log(` ${serverInfo.description}`);
|
|
196
|
+
}
|
|
197
|
+
console.log('');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Configure API keys for a specific MCP server in a target
|
|
203
|
+
* @returns Promise<boolean> - true if API keys were provided, false otherwise
|
|
204
|
+
*/
|
|
205
|
+
export async function configureMCPServerForTarget(
|
|
206
|
+
cwd: string,
|
|
207
|
+
targetId: string,
|
|
208
|
+
serverType: MCPServerID
|
|
209
|
+
): Promise<boolean> {
|
|
210
|
+
const targetOption = targetManager.getTarget(targetId);
|
|
211
|
+
|
|
212
|
+
if (targetOption._tag === 'None') {
|
|
213
|
+
throw new Error(`Target not found: ${targetId}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const target = targetOption.value;
|
|
217
|
+
|
|
218
|
+
const server = MCP_SERVER_REGISTRY[serverType];
|
|
219
|
+
if (!server) {
|
|
220
|
+
console.error(`ā Unknown MCP server: ${serverType}`);
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const requiredEnvVars = getRequiredEnvVars(serverType);
|
|
225
|
+
const optionalEnvVars = getOptionalEnvVars(serverType);
|
|
226
|
+
|
|
227
|
+
if (requiredEnvVars.length === 0 && optionalEnvVars.length === 0) {
|
|
228
|
+
console.log(`ā¹ļø ${server.name} does not require any API keys`);
|
|
229
|
+
return true; // No keys needed, so consider it successful
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
console.log(`š§ Configuring ${server.description} for ${target.name}...`);
|
|
233
|
+
|
|
234
|
+
// Check if server already exists
|
|
235
|
+
const config = await target.readConfig(cwd);
|
|
236
|
+
const mcpConfigPath = target.config.mcpConfigPath;
|
|
237
|
+
const mcpSection = getNestedProperty(config, mcpConfigPath);
|
|
238
|
+
const isServerInstalled = !!mcpSection?.[server.name];
|
|
239
|
+
|
|
240
|
+
// Check if existing server has valid keys (only required keys matter for validity)
|
|
241
|
+
let hasExistingValidKeys = false;
|
|
242
|
+
if (isServerInstalled && requiredEnvVars.length) {
|
|
243
|
+
const serverConfig = mcpSection[server.name];
|
|
244
|
+
hasExistingValidKeys = requiredEnvVars.every((envVar) => {
|
|
245
|
+
const envValue = serverConfig.env?.[envVar] || serverConfig.environment?.[envVar];
|
|
246
|
+
return envValue && envValue.trim() !== '';
|
|
247
|
+
});
|
|
248
|
+
} else if (isServerInstalled && requiredEnvVars.length === 0) {
|
|
249
|
+
// Server has no required keys, so it's always valid
|
|
250
|
+
hasExistingValidKeys = true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// First, check environment variables for existing API keys
|
|
254
|
+
const envApiKeys: Record<string, string> = {};
|
|
255
|
+
const allEnvVars = getAllEnvVars(serverType);
|
|
256
|
+
|
|
257
|
+
for (const envVar of allEnvVars) {
|
|
258
|
+
const envValue = process.env[envVar];
|
|
259
|
+
if (envValue && envValue.trim() !== '') {
|
|
260
|
+
envApiKeys[envVar] = envValue.trim();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Also extract existing keys from MCP config
|
|
265
|
+
const existingConfigKeys: Record<string, string> = {};
|
|
266
|
+
if (isServerInstalled && mcpSection[server.name]) {
|
|
267
|
+
const serverConfig = mcpSection[server.name];
|
|
268
|
+
for (const envVar of allEnvVars) {
|
|
269
|
+
const configValue = serverConfig.env?.[envVar] || serverConfig.environment?.[envVar];
|
|
270
|
+
if (configValue && configValue.trim() !== '') {
|
|
271
|
+
existingConfigKeys[envVar] = configValue.trim();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Merge existing keys: env vars take precedence over config keys
|
|
277
|
+
const existingKeys = { ...existingConfigKeys, ...envApiKeys };
|
|
278
|
+
|
|
279
|
+
// If we have all required keys from environment, use them
|
|
280
|
+
const _hasAllRequiredEnvKeys = requiredEnvVars.every(
|
|
281
|
+
(envVar) => envApiKeys[envVar] && envApiKeys[envVar].trim() !== ''
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
// mcp config is always for configuring - show UI with existing values
|
|
285
|
+
console.log(
|
|
286
|
+
'ā Found existing API keys, you can update them or press Enter to keep current values'
|
|
287
|
+
);
|
|
288
|
+
const apiKeys = await promptForAPIKeys([serverType], existingKeys);
|
|
289
|
+
|
|
290
|
+
// Check if all required environment variables are provided
|
|
291
|
+
const hasAllRequiredKeys = requiredEnvVars.every(
|
|
292
|
+
(envVar) => apiKeys[envVar] && apiKeys[envVar].trim() !== ''
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
// Check if user made any changes to existing keys
|
|
296
|
+
const userMadeChanges = allEnvVars.some((envVar) => {
|
|
297
|
+
const newValue = apiKeys[envVar];
|
|
298
|
+
const existingValue = existingKeys[envVar];
|
|
299
|
+
return newValue !== existingValue;
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// If server is installed with valid keys and user didn't change anything, keep it
|
|
303
|
+
if (isServerInstalled && hasExistingValidKeys && !userMadeChanges) {
|
|
304
|
+
console.log(`ā Keeping ${server.name} (existing API keys are valid)`);
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// For servers with required keys, validate them
|
|
309
|
+
if (requiredEnvVars.length > 0 && !hasAllRequiredKeys) {
|
|
310
|
+
// User didn't provide all required keys
|
|
311
|
+
if (isServerInstalled && !hasExistingValidKeys) {
|
|
312
|
+
// Case 1: Already installed + no keys + user doesn't provide ā DELETE
|
|
313
|
+
console.log(`šļø Removing ${server.name} (no API keys provided)`);
|
|
314
|
+
delete mcpSection[server.name];
|
|
315
|
+
|
|
316
|
+
// Remove MCP section if it's empty
|
|
317
|
+
if (Object.keys(mcpSection).length === 0) {
|
|
318
|
+
deleteNestedProperty(config, mcpConfigPath);
|
|
319
|
+
} else {
|
|
320
|
+
setNestedProperty(config, mcpConfigPath, mcpSection);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
await target.writeConfig(cwd, config);
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
if (isServerInstalled && hasExistingValidKeys) {
|
|
327
|
+
// Case 2: Already installed + has keys + user doesn't provide ā KEEP
|
|
328
|
+
console.log(`ā Keeping ${server.name} (existing API keys are valid)`);
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
// Case 4: Not installed + required keys + user doesn't provide ā SKIP
|
|
332
|
+
console.log(`ā ļø Skipping ${server.name} (required API keys not provided)`);
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// For servers with only optional envVars (like sylphx-flow), always proceed to save
|
|
337
|
+
// if the user provided any values or if the server is being configured
|
|
338
|
+
if (requiredEnvVars.length === 0 && optionalEnvVars.length > 0) {
|
|
339
|
+
const hasUserInput = Object.keys(apiKeys).some((key) => {
|
|
340
|
+
const envValue = apiKeys[key];
|
|
341
|
+
const existingValue = envApiKeys[key];
|
|
342
|
+
return envValue && envValue !== existingValue;
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
if (hasUserInput || !isServerInstalled) {
|
|
346
|
+
console.log(`š§ Updating ${server.name} configuration...`);
|
|
347
|
+
// Proceed to save/update the configuration
|
|
348
|
+
} else {
|
|
349
|
+
console.log(`ā No changes needed for ${server.name}`);
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Get MCP section for update (ensure it exists)
|
|
355
|
+
const mcpSectionForUpdate = mcpSection || {};
|
|
356
|
+
|
|
357
|
+
// Separate secret and non-secret API keys based on server configuration
|
|
358
|
+
const secretEnvVars = getSecretEnvVars(server.id);
|
|
359
|
+
const secretApiKeys: Record<string, string> = {};
|
|
360
|
+
const nonSecretApiKeys: Record<string, string> = {};
|
|
361
|
+
|
|
362
|
+
for (const [key, value] of Object.entries(apiKeys)) {
|
|
363
|
+
if (secretEnvVars.includes(key)) {
|
|
364
|
+
secretApiKeys[key] = value;
|
|
365
|
+
} else {
|
|
366
|
+
nonSecretApiKeys[key] = value;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Convert secret API keys to file references if target supports it
|
|
371
|
+
let processedSecretApiKeys = secretApiKeys;
|
|
372
|
+
const targetConfigOption = targetManager.getTarget(targetId);
|
|
373
|
+
|
|
374
|
+
if (
|
|
375
|
+
targetConfigOption._tag === 'Some' &&
|
|
376
|
+
targetConfigOption.value.setupMCP &&
|
|
377
|
+
targetConfigOption.value.config.installation.useSecretFiles !== false &&
|
|
378
|
+
Object.keys(secretApiKeys).length > 0
|
|
379
|
+
) {
|
|
380
|
+
processedSecretApiKeys = await secretUtils.convertSecretsToFileReferences(cwd, secretApiKeys);
|
|
381
|
+
await secretUtils.addToGitignore(cwd);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Combine processed secret keys with non-secret keys
|
|
385
|
+
const processedApiKeys = { ...nonSecretApiKeys, ...processedSecretApiKeys };
|
|
386
|
+
|
|
387
|
+
// Update server config with API keys (only for local servers)
|
|
388
|
+
const currentConfig = mcpSectionForUpdate[server.name];
|
|
389
|
+
if (currentConfig && currentConfig.type === 'local') {
|
|
390
|
+
// Update existing local config
|
|
391
|
+
mcpSectionForUpdate[server.name] = {
|
|
392
|
+
...currentConfig,
|
|
393
|
+
environment: {
|
|
394
|
+
...(currentConfig.environment || {}),
|
|
395
|
+
...processedApiKeys,
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
} else {
|
|
399
|
+
// Create new config with API keys
|
|
400
|
+
const baseConfig = server.config;
|
|
401
|
+
if (baseConfig.type === 'local') {
|
|
402
|
+
const transformedConfig = target.transformMCPConfig(baseConfig, server.id);
|
|
403
|
+
mcpSectionForUpdate[server.name] = {
|
|
404
|
+
...transformedConfig,
|
|
405
|
+
environment: {
|
|
406
|
+
...(baseConfig.environment || {}),
|
|
407
|
+
...processedApiKeys,
|
|
408
|
+
},
|
|
409
|
+
};
|
|
410
|
+
} else {
|
|
411
|
+
// HTTP server - just add the base config
|
|
412
|
+
const transformedConfig = target.transformMCPConfig(baseConfig, server.id);
|
|
413
|
+
mcpSectionForUpdate[server.name] = transformedConfig;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Update config with new MCP section
|
|
418
|
+
setNestedProperty(config, mcpConfigPath, mcpSectionForUpdate);
|
|
419
|
+
|
|
420
|
+
// Write updated configuration
|
|
421
|
+
await target.writeConfig(cwd, config);
|
|
422
|
+
console.log(`ā Updated ${server.name} with API keys for ${target.name}`);
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Get target-specific help text
|
|
428
|
+
*/
|
|
429
|
+
export function getTargetHelpText(targetId: string): string {
|
|
430
|
+
const targetOption = targetManager.getTarget(targetId);
|
|
431
|
+
if (targetOption._tag === 'Some') {
|
|
432
|
+
return targetOption.value.getHelpText();
|
|
433
|
+
}
|
|
434
|
+
return '';
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Get all available targets help text
|
|
439
|
+
*/
|
|
440
|
+
export function getAllTargetsHelpText(): string {
|
|
441
|
+
const targets = targetManager.getImplementedTargets();
|
|
442
|
+
return targets.map((target) => target.getHelpText()).join('\n\n');
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Validate target and return target ID
|
|
447
|
+
*/
|
|
448
|
+
export function validateTarget(targetId: string): string {
|
|
449
|
+
const targetOption = targetManager.getTarget(targetId);
|
|
450
|
+
|
|
451
|
+
if (targetOption._tag === 'None') {
|
|
452
|
+
throw new Error(
|
|
453
|
+
`Unknown target: ${targetId}. Available targets: ${targetManager.getImplementedTargetIDs().join(', ')}`
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const target = targetOption.value;
|
|
458
|
+
if (!target.isImplemented) {
|
|
459
|
+
throw new Error(
|
|
460
|
+
`Target '${targetId}' is not implemented. Available targets: ${targetManager.getImplementedTargetIDs().join(', ')}`
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
return targetId;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Check if target supports MCP servers
|
|
468
|
+
*/
|
|
469
|
+
export function targetSupportsMCPServers(targetId: string): boolean {
|
|
470
|
+
const targetOption = targetManager.getTarget(targetId);
|
|
471
|
+
if (targetOption._tag === 'None') {
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
474
|
+
return !!targetOption.value.setupMCP;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Get targets that support MCP servers
|
|
479
|
+
*/
|
|
480
|
+
export function getTargetsWithMCPSupport(): string[] {
|
|
481
|
+
const targets = targetManager.getTargetsWithMCPSupport();
|
|
482
|
+
return targets.map((target) => target.id);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Prompt user for API keys interactively
|
|
487
|
+
*/
|
|
488
|
+
async function promptForAPIKeys(
|
|
489
|
+
serverTypes: MCPServerID[],
|
|
490
|
+
existingKeys: Record<string, string> = {}
|
|
491
|
+
): Promise<Record<string, string>> {
|
|
492
|
+
const rl = createInterface({
|
|
493
|
+
input: process.stdin,
|
|
494
|
+
output: process.stdout,
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
const apiKeys: Record<string, string> = { ...existingKeys };
|
|
498
|
+
|
|
499
|
+
for (const serverType of serverTypes) {
|
|
500
|
+
const server = MCP_SERVER_REGISTRY[serverType];
|
|
501
|
+
const allEnvVars = getAllEnvVars(serverType);
|
|
502
|
+
|
|
503
|
+
if (!allEnvVars.length) {
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
console.log(`\nš Configuring API keys for ${server.description}:`);
|
|
508
|
+
|
|
509
|
+
for (const envVar of allEnvVars) {
|
|
510
|
+
const envConfig = server.envVars?.[envVar];
|
|
511
|
+
if (!envConfig) {
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const isRequired = envConfig.required;
|
|
516
|
+
const hasDefault = envConfig.default !== undefined;
|
|
517
|
+
const existingValue = existingKeys[envVar];
|
|
518
|
+
const displayExisting = existingValue
|
|
519
|
+
? ` (current: ${existingValue.substring(0, 9)}${existingValue.length > 9 ? '...' : ''})`
|
|
520
|
+
: '';
|
|
521
|
+
|
|
522
|
+
let promptText: string;
|
|
523
|
+
if (isRequired) {
|
|
524
|
+
if (hasDefault) {
|
|
525
|
+
promptText = `Enter ${envVar} (${envConfig.description}) (required, default: ${envConfig.default}${displayExisting}): `;
|
|
526
|
+
} else {
|
|
527
|
+
promptText = `Enter ${envVar} (${envConfig.description}) (required${displayExisting}): `;
|
|
528
|
+
}
|
|
529
|
+
} else if (hasDefault) {
|
|
530
|
+
promptText = `Enter ${envVar} (${envConfig.description}) (optional, default: ${envConfig.default}${displayExisting}, press Enter to keep current): `;
|
|
531
|
+
} else {
|
|
532
|
+
promptText = `Enter ${envVar} (${envConfig.description}) (optional${displayExisting}, press Enter to keep current): `;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const answer = await new Promise<string>((resolve) => {
|
|
536
|
+
rl.question(promptText, (input) => {
|
|
537
|
+
resolve(input.trim());
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
let finalValue: string | undefined;
|
|
542
|
+
let actionText: string;
|
|
543
|
+
|
|
544
|
+
if (answer) {
|
|
545
|
+
finalValue = answer;
|
|
546
|
+
actionText = `Updated ${envVar}`;
|
|
547
|
+
} else if (existingValue) {
|
|
548
|
+
finalValue = existingValue;
|
|
549
|
+
actionText = `Kept existing ${envVar}`;
|
|
550
|
+
} else if (hasDefault) {
|
|
551
|
+
finalValue = envConfig.default;
|
|
552
|
+
actionText = `Using default for ${envVar}`;
|
|
553
|
+
} else if (isRequired) {
|
|
554
|
+
console.log(`ā ļø Skipped required ${envVar} - server may not function properly`);
|
|
555
|
+
continue;
|
|
556
|
+
} else {
|
|
557
|
+
console.log(`ā¹ļø Skipped optional ${envVar}`);
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (finalValue) {
|
|
562
|
+
apiKeys[envVar] = finalValue;
|
|
563
|
+
console.log(`ā ${actionText}`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
rl.close();
|
|
569
|
+
return apiKeys;
|
|
570
|
+
}
|