@agents-at-scale/ark 0.1.33 → 0.1.35-rc.1
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/arkServices.d.ts +50 -0
- package/dist/arkServices.js +153 -0
- package/dist/charts/charts.d.ts +5 -0
- package/dist/charts/charts.js +6 -0
- package/dist/charts/dependencies.d.ts +6 -0
- package/dist/charts/dependencies.js +50 -0
- package/dist/charts/types.d.ts +40 -0
- package/dist/charts/types.js +1 -0
- package/dist/commands/agents/index.d.ts +2 -0
- package/dist/commands/agents/index.js +56 -0
- package/dist/commands/agents/selector.d.ts +8 -0
- package/dist/commands/agents/selector.js +53 -0
- package/dist/commands/agents.d.ts +2 -0
- package/dist/commands/agents.js +53 -0
- package/dist/commands/chat/index.d.ts +2 -0
- package/dist/commands/chat/index.js +45 -0
- package/dist/commands/chat.d.ts +2 -0
- package/dist/commands/chat.js +45 -0
- package/dist/commands/cluster/get.d.ts +2 -0
- package/dist/commands/cluster/get.js +39 -0
- package/dist/commands/cluster/index.js +2 -4
- package/dist/commands/completion/index.d.ts +2 -0
- package/dist/commands/completion/index.js +268 -0
- package/dist/commands/completion.js +159 -2
- package/dist/commands/config/index.d.ts +2 -0
- package/dist/commands/config/index.js +42 -0
- package/dist/commands/config.d.ts +0 -3
- package/dist/commands/config.js +38 -321
- package/dist/commands/dashboard/index.d.ts +3 -0
- package/dist/commands/dashboard/index.js +39 -0
- package/dist/commands/dashboard.d.ts +3 -0
- package/dist/commands/dashboard.js +39 -0
- package/dist/commands/dev/index.d.ts +2 -0
- package/dist/commands/dev/index.js +9 -0
- package/dist/commands/dev/tool/check.d.ts +2 -0
- package/dist/commands/dev/tool/check.js +142 -0
- package/dist/commands/dev/tool/clean.d.ts +2 -0
- package/dist/commands/dev/tool/clean.js +153 -0
- package/dist/commands/dev/tool/generate.d.ts +2 -0
- package/dist/commands/dev/tool/generate.js +28 -0
- package/dist/commands/dev/tool/index.d.ts +2 -0
- package/dist/commands/dev/tool/index.js +14 -0
- package/dist/commands/dev/tool/init.d.ts +2 -0
- package/dist/commands/dev/tool/init.js +320 -0
- package/dist/commands/dev/tool/shared.d.ts +5 -0
- package/dist/commands/dev/tool/shared.js +256 -0
- package/dist/commands/dev/tool/status.d.ts +2 -0
- package/dist/commands/dev/tool/status.js +136 -0
- package/dist/commands/dev/tool.d.ts +2 -0
- package/dist/commands/dev/tool.js +559 -0
- package/dist/commands/generate/config.js +5 -24
- package/dist/commands/generate/generators/mcpserver.d.ts +2 -1
- package/dist/commands/generate/generators/mcpserver.js +26 -5
- package/dist/commands/install/index.d.ts +6 -0
- package/dist/commands/install/index.js +165 -0
- package/dist/commands/install.d.ts +3 -0
- package/dist/commands/install.js +147 -0
- package/dist/commands/models/create.d.ts +1 -0
- package/dist/commands/models/create.js +213 -0
- package/dist/commands/models/index.d.ts +2 -0
- package/dist/commands/models/index.js +65 -0
- package/dist/commands/models/selector.d.ts +8 -0
- package/dist/commands/models/selector.js +53 -0
- package/dist/commands/routes/index.d.ts +2 -0
- package/dist/commands/routes/index.js +101 -0
- package/dist/commands/routes.d.ts +2 -0
- package/dist/commands/routes.js +101 -0
- package/dist/commands/status/index.d.ts +3 -0
- package/dist/commands/status/index.js +33 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.js +33 -0
- package/dist/commands/targets/index.d.ts +2 -0
- package/dist/commands/targets/index.js +65 -0
- package/dist/commands/targets.d.ts +2 -0
- package/dist/commands/targets.js +65 -0
- package/dist/commands/teams/index.d.ts +2 -0
- package/dist/commands/teams/index.js +54 -0
- package/dist/commands/teams/selector.d.ts +8 -0
- package/dist/commands/teams/selector.js +55 -0
- package/dist/commands/tools/index.d.ts +2 -0
- package/dist/commands/tools/index.js +54 -0
- package/dist/commands/tools/selector.d.ts +8 -0
- package/dist/commands/tools/selector.js +53 -0
- package/dist/commands/uninstall/index.d.ts +2 -0
- package/dist/commands/uninstall/index.js +84 -0
- package/dist/commands/uninstall.d.ts +2 -0
- package/dist/commands/uninstall.js +83 -0
- package/dist/components/ChatUI.d.ts +16 -0
- package/dist/components/ChatUI.js +801 -0
- package/dist/components/StatusView.d.ts +10 -0
- package/dist/components/StatusView.js +39 -0
- package/dist/components/statusChecker.d.ts +10 -13
- package/dist/components/statusChecker.js +128 -65
- package/dist/config.js +3 -10
- package/dist/index.d.ts +1 -1
- package/dist/index.js +31 -36
- package/dist/lib/arkApiClient.d.ts +53 -0
- package/dist/lib/arkApiClient.js +102 -0
- package/dist/lib/arkApiProxy.d.ts +9 -0
- package/dist/lib/arkApiProxy.js +22 -0
- package/dist/lib/arkServiceProxy.d.ts +14 -0
- package/dist/lib/arkServiceProxy.js +93 -0
- package/dist/lib/arkStatus.d.ts +5 -0
- package/dist/lib/arkStatus.js +20 -0
- package/dist/lib/chatClient.d.ts +33 -0
- package/dist/lib/chatClient.js +101 -0
- package/dist/lib/cluster.d.ts +2 -1
- package/dist/lib/cluster.js +27 -3
- package/dist/lib/commandUtils.d.ts +4 -0
- package/dist/lib/commandUtils.js +18 -0
- package/dist/lib/commandUtils.test.d.ts +1 -0
- package/dist/lib/commandUtils.test.js +44 -0
- package/dist/lib/config.d.ts +24 -80
- package/dist/lib/config.js +68 -205
- package/dist/lib/config.test.d.ts +1 -0
- package/dist/lib/config.test.js +93 -0
- package/dist/lib/dev/tools/analyzer.d.ts +30 -0
- package/dist/lib/dev/tools/analyzer.js +190 -0
- package/dist/lib/dev/tools/discover_tools.py +392 -0
- package/dist/lib/dev/tools/mcp-types.d.ts +28 -0
- package/dist/lib/dev/tools/mcp-types.js +86 -0
- package/dist/lib/dev/tools/types.d.ts +50 -0
- package/dist/lib/dev/tools/types.js +1 -0
- package/dist/lib/output.d.ts +36 -0
- package/dist/lib/output.js +89 -0
- package/dist/lib/types.d.ts +8 -3
- package/dist/types/types.d.ts +40 -0
- package/dist/types/types.js +1 -0
- package/dist/ui/MainMenu.js +158 -90
- package/dist/ui/statusFormatter.d.ts +4 -1
- package/dist/ui/statusFormatter.js +91 -19
- package/package.json +16 -4
package/dist/lib/config.js
CHANGED
|
@@ -1,223 +1,86 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration management for ARK CLI
|
|
3
|
-
*/
|
|
4
1
|
import fs from 'fs';
|
|
5
2
|
import path from 'path';
|
|
6
3
|
import os from 'os';
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
constructor() {
|
|
27
|
-
this.configPath = this.getConfigPath();
|
|
28
|
-
this.config = this.loadConfig();
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Get the path to the configuration file
|
|
32
|
-
*/
|
|
33
|
-
getConfigPath() {
|
|
34
|
-
const configDir = process.env.ARK_CONFIG_DIR || path.join(os.homedir(), '.config', 'ark');
|
|
35
|
-
// Ensure config directory exists
|
|
36
|
-
if (!fs.existsSync(configDir)) {
|
|
37
|
-
fs.mkdirSync(configDir, { recursive: true, mode: 0o755 });
|
|
38
|
-
}
|
|
39
|
-
return path.join(configDir, 'config.json');
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Load configuration from file or create with defaults
|
|
43
|
-
*/
|
|
44
|
-
loadConfig() {
|
|
4
|
+
import yaml from 'yaml';
|
|
5
|
+
/**
|
|
6
|
+
* Load configuration from multiple sources with proper precedence:
|
|
7
|
+
* 1. Defaults
|
|
8
|
+
* 2. ~/.arkrc.yaml (user config)
|
|
9
|
+
* 3. .arkrc.yaml (project config)
|
|
10
|
+
* 4. Environment variables (override all)
|
|
11
|
+
*/
|
|
12
|
+
export function loadConfig() {
|
|
13
|
+
// Start with defaults
|
|
14
|
+
const config = {
|
|
15
|
+
chat: {
|
|
16
|
+
streaming: true,
|
|
17
|
+
outputFormat: 'text',
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
// Load user config from home directory
|
|
21
|
+
const userConfigPath = path.join(os.homedir(), '.arkrc.yaml');
|
|
22
|
+
if (fs.existsSync(userConfigPath)) {
|
|
45
23
|
try {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const userConfig = JSON.parse(configContent);
|
|
49
|
-
// Merge with defaults to ensure all properties exist
|
|
50
|
-
return { ...DEFAULT_CONFIG, ...userConfig };
|
|
51
|
-
}
|
|
24
|
+
const userConfig = yaml.parse(fs.readFileSync(userConfigPath, 'utf-8'));
|
|
25
|
+
mergeConfig(config, userConfig);
|
|
52
26
|
}
|
|
53
|
-
catch (
|
|
54
|
-
|
|
27
|
+
catch (_e) {
|
|
28
|
+
// Silently ignore invalid config files
|
|
55
29
|
}
|
|
56
|
-
// Return defaults and save them
|
|
57
|
-
this.saveConfig(DEFAULT_CONFIG);
|
|
58
|
-
return { ...DEFAULT_CONFIG };
|
|
59
30
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
saveConfig(config) {
|
|
31
|
+
// Load project config from current directory
|
|
32
|
+
const projectConfigPath = path.join(process.cwd(), '.arkrc.yaml');
|
|
33
|
+
if (fs.existsSync(projectConfigPath)) {
|
|
64
34
|
try {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
fs.writeFileSync(this.configPath, configContent, {
|
|
68
|
-
mode: 0o600, // Owner read/write only
|
|
69
|
-
flag: 'w',
|
|
70
|
-
});
|
|
35
|
+
const projectConfig = yaml.parse(fs.readFileSync(projectConfigPath, 'utf-8'));
|
|
36
|
+
mergeConfig(config, projectConfig);
|
|
71
37
|
}
|
|
72
|
-
catch (
|
|
73
|
-
|
|
74
|
-
'Check file permissions',
|
|
75
|
-
'Ensure config directory exists',
|
|
76
|
-
'Verify disk space',
|
|
77
|
-
]);
|
|
38
|
+
catch (_e) {
|
|
39
|
+
// Silently ignore invalid config files
|
|
78
40
|
}
|
|
79
41
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Update configuration
|
|
88
|
-
*/
|
|
89
|
-
updateConfig(updates) {
|
|
90
|
-
this.config = { ...this.config, ...updates };
|
|
91
|
-
this.saveConfig(this.config);
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Reset configuration to defaults
|
|
95
|
-
*/
|
|
96
|
-
resetConfig() {
|
|
97
|
-
this.config = { ...DEFAULT_CONFIG };
|
|
98
|
-
this.saveConfig(this.config);
|
|
42
|
+
// Apply environment variable overrides
|
|
43
|
+
if (process.env.ARK_CHAT_STREAMING !== undefined) {
|
|
44
|
+
config.chat = config.chat || {};
|
|
45
|
+
config.chat.streaming =
|
|
46
|
+
process.env.ARK_CHAT_STREAMING === '1' ||
|
|
47
|
+
process.env.ARK_CHAT_STREAMING === 'true';
|
|
99
48
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Set a specific configuration value
|
|
108
|
-
*/
|
|
109
|
-
set(key, value) {
|
|
110
|
-
this.config[key] = value;
|
|
111
|
-
this.saveConfig(this.config);
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Validate configuration values
|
|
115
|
-
*/
|
|
116
|
-
validateConfig() {
|
|
117
|
-
const config = this.config;
|
|
118
|
-
// Validate project type
|
|
119
|
-
if (!['empty', 'with-samples'].includes(config.defaultProjectType)) {
|
|
120
|
-
throw new ValidationError(`Invalid defaultProjectType: ${config.defaultProjectType}`, 'defaultProjectType', ['Must be "empty" or "with-samples"']);
|
|
121
|
-
}
|
|
122
|
-
// Validate model provider
|
|
123
|
-
const validProviders = ['azure', 'openai', 'claude', 'gemini', 'custom'];
|
|
124
|
-
if (!validProviders.includes(config.defaultModelProvider)) {
|
|
125
|
-
throw new ValidationError(`Invalid defaultModelProvider: ${config.defaultModelProvider}`, 'defaultModelProvider', [`Must be one of: ${validProviders.join(', ')}`]);
|
|
126
|
-
}
|
|
127
|
-
// Validate numeric values
|
|
128
|
-
if (config.maxConcurrentFiles < 1 || config.maxConcurrentFiles > 100) {
|
|
129
|
-
throw new ValidationError(`Invalid maxConcurrentFiles: ${config.maxConcurrentFiles}`, 'maxConcurrentFiles', ['Must be between 1 and 100']);
|
|
130
|
-
}
|
|
131
|
-
// Validate paths
|
|
132
|
-
if (config.defaultDestination) {
|
|
133
|
-
SecurityUtils.validatePath(config.defaultDestination, 'default destination');
|
|
134
|
-
}
|
|
135
|
-
if (config.templateDirectory) {
|
|
136
|
-
SecurityUtils.validatePath(config.templateDirectory, 'template directory');
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Get environment variable overrides
|
|
141
|
-
*/
|
|
142
|
-
getEnvironmentOverrides() {
|
|
143
|
-
const overrides = {};
|
|
144
|
-
// Check for environment variable overrides
|
|
145
|
-
if (process.env.ARK_DEFAULT_PROJECT_TYPE) {
|
|
146
|
-
const projectType = process.env.ARK_DEFAULT_PROJECT_TYPE;
|
|
147
|
-
if (['empty', 'with-samples'].includes(projectType)) {
|
|
148
|
-
overrides.defaultProjectType = projectType;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
if (process.env.ARK_DEFAULT_DESTINATION) {
|
|
152
|
-
overrides.defaultDestination = process.env.ARK_DEFAULT_DESTINATION;
|
|
49
|
+
if (process.env.ARK_CHAT_OUTPUT_FORMAT !== undefined) {
|
|
50
|
+
config.chat = config.chat || {};
|
|
51
|
+
const format = process.env.ARK_CHAT_OUTPUT_FORMAT.toLowerCase();
|
|
52
|
+
if (format === 'markdown' || format === 'text') {
|
|
53
|
+
config.chat.outputFormat = format;
|
|
153
54
|
}
|
|
154
|
-
if (process.env.ARK_SKIP_GIT) {
|
|
155
|
-
overrides.skipGitByDefault = process.env.ARK_SKIP_GIT === 'true';
|
|
156
|
-
}
|
|
157
|
-
if (process.env.ARK_SKIP_MODELS) {
|
|
158
|
-
overrides.skipModelsbyDefault = process.env.ARK_SKIP_MODELS === 'true';
|
|
159
|
-
}
|
|
160
|
-
if (process.env.ARK_COLOR_OUTPUT) {
|
|
161
|
-
overrides.colorOutput = process.env.ARK_COLOR_OUTPUT !== 'false';
|
|
162
|
-
}
|
|
163
|
-
if (process.env.ARK_VERBOSE) {
|
|
164
|
-
overrides.verboseOutput = process.env.ARK_VERBOSE === 'true';
|
|
165
|
-
}
|
|
166
|
-
if (process.env.ARK_DEFAULT_MODEL_PROVIDER) {
|
|
167
|
-
const provider = process.env.ARK_DEFAULT_MODEL_PROVIDER;
|
|
168
|
-
const validProviders = [
|
|
169
|
-
'azure',
|
|
170
|
-
'openai',
|
|
171
|
-
'claude',
|
|
172
|
-
'gemini',
|
|
173
|
-
'custom',
|
|
174
|
-
];
|
|
175
|
-
if (validProviders.includes(provider)) {
|
|
176
|
-
overrides.defaultModelProvider =
|
|
177
|
-
provider;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
return overrides;
|
|
181
55
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
exportConfig() {
|
|
193
|
-
return JSON.stringify(this.config, null, 2);
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Import configuration from backup
|
|
197
|
-
*/
|
|
198
|
-
importConfig(configJson) {
|
|
199
|
-
try {
|
|
200
|
-
const importedConfig = JSON.parse(configJson);
|
|
201
|
-
// Validate the imported config
|
|
202
|
-
const tempManager = new ConfigManager();
|
|
203
|
-
tempManager.config = { ...DEFAULT_CONFIG, ...importedConfig };
|
|
204
|
-
tempManager.validateConfig();
|
|
205
|
-
// If validation passes, update our config
|
|
206
|
-
this.config = tempManager.config;
|
|
207
|
-
this.saveConfig(this.config);
|
|
56
|
+
return config;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Merge source config into target config (mutates target)
|
|
60
|
+
*/
|
|
61
|
+
function mergeConfig(target, source) {
|
|
62
|
+
if (source.chat) {
|
|
63
|
+
target.chat = target.chat || {};
|
|
64
|
+
if (source.chat.streaming !== undefined) {
|
|
65
|
+
target.chat.streaming = source.chat.streaming;
|
|
208
66
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
'Check JSON syntax',
|
|
212
|
-
'Ensure all required fields are present',
|
|
213
|
-
'Verify configuration values are valid',
|
|
214
|
-
]);
|
|
67
|
+
if (source.chat.outputFormat !== undefined) {
|
|
68
|
+
target.chat.outputFormat = source.chat.outputFormat;
|
|
215
69
|
}
|
|
216
70
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get the paths checked for config files
|
|
74
|
+
*/
|
|
75
|
+
export function getConfigPaths() {
|
|
76
|
+
return {
|
|
77
|
+
user: path.join(os.homedir(), '.arkrc.yaml'),
|
|
78
|
+
project: path.join(process.cwd(), '.arkrc.yaml'),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Format config as YAML for display
|
|
83
|
+
*/
|
|
84
|
+
export function formatConfig(config) {
|
|
85
|
+
return yaml.stringify(config);
|
|
223
86
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
|
|
2
|
+
import { loadConfig } from './config.js';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import os from 'os';
|
|
6
|
+
describe('Config', () => {
|
|
7
|
+
const originalEnv = process.env;
|
|
8
|
+
const testProjectConfig = path.join(process.cwd(), '.arkrc.yaml');
|
|
9
|
+
const testUserConfig = path.join(os.homedir(), '.arkrc.yaml');
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
// Reset environment
|
|
12
|
+
process.env = { ...originalEnv };
|
|
13
|
+
// Clean up any existing test configs
|
|
14
|
+
if (fs.existsSync(testProjectConfig)) {
|
|
15
|
+
fs.unlinkSync(testProjectConfig);
|
|
16
|
+
}
|
|
17
|
+
if (fs.existsSync(testUserConfig)) {
|
|
18
|
+
fs.unlinkSync(testUserConfig);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
// Restore environment
|
|
23
|
+
process.env = originalEnv;
|
|
24
|
+
// Clean up test configs
|
|
25
|
+
if (fs.existsSync(testProjectConfig)) {
|
|
26
|
+
fs.unlinkSync(testProjectConfig);
|
|
27
|
+
}
|
|
28
|
+
if (fs.existsSync(testUserConfig)) {
|
|
29
|
+
fs.unlinkSync(testUserConfig);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
it('should load default config when no files or env vars exist', () => {
|
|
33
|
+
const config = loadConfig();
|
|
34
|
+
expect(config.chat?.streaming).toBe(true);
|
|
35
|
+
expect(config.chat?.outputFormat).toBe('text');
|
|
36
|
+
});
|
|
37
|
+
it('should override defaults with environment variables', () => {
|
|
38
|
+
process.env.ARK_CHAT_STREAMING = '0';
|
|
39
|
+
process.env.ARK_CHAT_OUTPUT_FORMAT = 'markdown';
|
|
40
|
+
const config = loadConfig();
|
|
41
|
+
expect(config.chat?.streaming).toBe(false);
|
|
42
|
+
expect(config.chat?.outputFormat).toBe('markdown');
|
|
43
|
+
});
|
|
44
|
+
it('should accept "1" and "true" for streaming', () => {
|
|
45
|
+
process.env.ARK_CHAT_STREAMING = '1';
|
|
46
|
+
let config = loadConfig();
|
|
47
|
+
expect(config.chat?.streaming).toBe(true);
|
|
48
|
+
process.env.ARK_CHAT_STREAMING = 'true';
|
|
49
|
+
config = loadConfig();
|
|
50
|
+
expect(config.chat?.streaming).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
it('should load project config file', () => {
|
|
53
|
+
const configContent = `
|
|
54
|
+
chat:
|
|
55
|
+
streaming: false
|
|
56
|
+
outputFormat: markdown
|
|
57
|
+
`;
|
|
58
|
+
fs.writeFileSync(testProjectConfig, configContent);
|
|
59
|
+
const config = loadConfig();
|
|
60
|
+
expect(config.chat?.streaming).toBe(false);
|
|
61
|
+
expect(config.chat?.outputFormat).toBe('markdown');
|
|
62
|
+
});
|
|
63
|
+
it('should prioritize env vars over config files', () => {
|
|
64
|
+
const configContent = `
|
|
65
|
+
chat:
|
|
66
|
+
streaming: false
|
|
67
|
+
outputFormat: markdown
|
|
68
|
+
`;
|
|
69
|
+
fs.writeFileSync(testProjectConfig, configContent);
|
|
70
|
+
process.env.ARK_CHAT_STREAMING = '1';
|
|
71
|
+
process.env.ARK_CHAT_OUTPUT_FORMAT = 'text';
|
|
72
|
+
const config = loadConfig();
|
|
73
|
+
expect(config.chat?.streaming).toBe(true);
|
|
74
|
+
expect(config.chat?.outputFormat).toBe('text');
|
|
75
|
+
});
|
|
76
|
+
it('should handle invalid config files gracefully', () => {
|
|
77
|
+
fs.writeFileSync(testProjectConfig, 'invalid: yaml: content: {{{');
|
|
78
|
+
// Should fall back to defaults without throwing
|
|
79
|
+
const config = loadConfig();
|
|
80
|
+
expect(config.chat?.streaming).toBe(true);
|
|
81
|
+
expect(config.chat?.outputFormat).toBe('text');
|
|
82
|
+
});
|
|
83
|
+
it('should handle partial configs', () => {
|
|
84
|
+
const configContent = `
|
|
85
|
+
chat:
|
|
86
|
+
streaming: false
|
|
87
|
+
`;
|
|
88
|
+
fs.writeFileSync(testProjectConfig, configContent);
|
|
89
|
+
const config = loadConfig();
|
|
90
|
+
expect(config.chat?.streaming).toBe(false);
|
|
91
|
+
expect(config.chat?.outputFormat).toBe('text'); // Should use default
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ArkDevToolStatus, DiscoveryResult, ProjectDiscoveryResult } from './types.js';
|
|
2
|
+
export declare class ArkDevToolAnalyzer {
|
|
3
|
+
private discoverToolsScript;
|
|
4
|
+
constructor();
|
|
5
|
+
/**
|
|
6
|
+
* Analyze a tool directory and return its status
|
|
7
|
+
*/
|
|
8
|
+
analyzeToolDirectory(toolPath: string): Promise<ArkDevToolStatus>;
|
|
9
|
+
/**
|
|
10
|
+
* Get project information by checking for Python project files
|
|
11
|
+
*/
|
|
12
|
+
private getProjectInfo;
|
|
13
|
+
/**
|
|
14
|
+
* Discover project configuration
|
|
15
|
+
*/
|
|
16
|
+
discoverProject(targetPath: string): Promise<ProjectDiscoveryResult | undefined>;
|
|
17
|
+
/**
|
|
18
|
+
* Discover tools using the Python script
|
|
19
|
+
*/
|
|
20
|
+
discoverTools(targetPath: string): Promise<DiscoveryResult | undefined>;
|
|
21
|
+
/**
|
|
22
|
+
* Recursively find all MCP tools in a project
|
|
23
|
+
* This is a naive implementation that searches all Python files in the project tree
|
|
24
|
+
*/
|
|
25
|
+
findProjectTools(projectRoot: string): Promise<any>;
|
|
26
|
+
/**
|
|
27
|
+
* Extract all tools from discovery result
|
|
28
|
+
*/
|
|
29
|
+
private extractTools;
|
|
30
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
export class ArkDevToolAnalyzer {
|
|
8
|
+
constructor() {
|
|
9
|
+
// The Python script is always adjacent to this file
|
|
10
|
+
// In dev: src/lib/dev/tools/discover_tools.py
|
|
11
|
+
// In prod: dist/lib/dev/tools/discover_tools.py (copied by postbuild)
|
|
12
|
+
this.discoverToolsScript = path.join(__dirname, 'discover_tools.py');
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Analyze a tool directory and return its status
|
|
16
|
+
*/
|
|
17
|
+
async analyzeToolDirectory(toolPath) {
|
|
18
|
+
const absolutePath = path.resolve(toolPath);
|
|
19
|
+
// Check if path exists
|
|
20
|
+
if (!fs.existsSync(absolutePath)) {
|
|
21
|
+
throw new Error(`Path not found: ${absolutePath}`);
|
|
22
|
+
}
|
|
23
|
+
// Get project info
|
|
24
|
+
const projectInfo = this.getProjectInfo(absolutePath);
|
|
25
|
+
// Discover tools using Python script
|
|
26
|
+
const discovery = await this.discoverTools(absolutePath);
|
|
27
|
+
// Extract all tools from discovery
|
|
28
|
+
const tools = this.extractTools(discovery);
|
|
29
|
+
return {
|
|
30
|
+
...projectInfo,
|
|
31
|
+
discovery,
|
|
32
|
+
tools,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get project information by checking for Python project files
|
|
37
|
+
*/
|
|
38
|
+
getProjectInfo(dirPath) {
|
|
39
|
+
const info = {
|
|
40
|
+
path: dirPath,
|
|
41
|
+
platform: 'python3',
|
|
42
|
+
projectType: 'unknown',
|
|
43
|
+
hasVenv: false,
|
|
44
|
+
fastMCP: false,
|
|
45
|
+
};
|
|
46
|
+
// Check for virtual environment
|
|
47
|
+
info.hasVenv =
|
|
48
|
+
fs.existsSync(path.join(dirPath, '.venv')) ||
|
|
49
|
+
fs.existsSync(path.join(dirPath, 'venv'));
|
|
50
|
+
// Check Python project type and FastMCP presence
|
|
51
|
+
const pyprojectPath = path.join(dirPath, 'pyproject.toml');
|
|
52
|
+
const requirementsPath = path.join(dirPath, 'requirements.txt');
|
|
53
|
+
if (fs.existsSync(pyprojectPath)) {
|
|
54
|
+
info.projectType = 'pyproject';
|
|
55
|
+
const content = fs.readFileSync(pyprojectPath, 'utf-8');
|
|
56
|
+
if (content.includes('fastmcp')) {
|
|
57
|
+
info.fastMCP = true;
|
|
58
|
+
// Try to extract version
|
|
59
|
+
const versionMatch = content.match(/fastmcp[>=<~]*([0-9.]+)/);
|
|
60
|
+
if (versionMatch) {
|
|
61
|
+
info.fastMCPVersion = versionMatch[1];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else if (fs.existsSync(requirementsPath)) {
|
|
66
|
+
info.projectType = 'requirements';
|
|
67
|
+
const content = fs.readFileSync(requirementsPath, 'utf-8');
|
|
68
|
+
if (content.includes('fastmcp')) {
|
|
69
|
+
info.fastMCP = true;
|
|
70
|
+
const versionMatch = content.match(/fastmcp[>=<~]*([0-9.]+)/);
|
|
71
|
+
if (versionMatch) {
|
|
72
|
+
info.fastMCPVersion = versionMatch[1];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return info;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Discover project configuration
|
|
80
|
+
*/
|
|
81
|
+
async discoverProject(targetPath) {
|
|
82
|
+
try {
|
|
83
|
+
// Check if Python is available
|
|
84
|
+
try {
|
|
85
|
+
execSync('python3 --version', { stdio: 'ignore' });
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
console.warn('Python 3 not found');
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
// Check if discover_tools.py exists
|
|
92
|
+
if (!fs.existsSync(this.discoverToolsScript)) {
|
|
93
|
+
console.warn(`discover_tools.py not found at ${this.discoverToolsScript}`);
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
// Run the discovery script with 'project' command
|
|
97
|
+
const result = execSync(`python3 "${this.discoverToolsScript}" project "${targetPath}"`, {
|
|
98
|
+
encoding: 'utf-8',
|
|
99
|
+
maxBuffer: 1024 * 1024, // 1MB buffer
|
|
100
|
+
});
|
|
101
|
+
return JSON.parse(result);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
console.error('Project discovery failed:', error);
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Discover tools using the Python script
|
|
110
|
+
*/
|
|
111
|
+
async discoverTools(targetPath) {
|
|
112
|
+
try {
|
|
113
|
+
// Check if Python is available
|
|
114
|
+
try {
|
|
115
|
+
execSync('python3 --version', { stdio: 'ignore' });
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
console.warn('Python 3 not found, skipping tool discovery');
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
// Check if discover_tools.py exists
|
|
122
|
+
if (!fs.existsSync(this.discoverToolsScript)) {
|
|
123
|
+
console.warn(`discover_tools.py not found at ${this.discoverToolsScript}`);
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
// Run the discovery script with 'tools' command
|
|
127
|
+
const result = execSync(`python3 "${this.discoverToolsScript}" tools "${targetPath}"`, {
|
|
128
|
+
encoding: 'utf-8',
|
|
129
|
+
maxBuffer: 1024 * 1024 * 10, // 10MB buffer
|
|
130
|
+
});
|
|
131
|
+
return JSON.parse(result);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
console.error('Tool discovery failed:', error);
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Recursively find all MCP tools in a project
|
|
140
|
+
* This is a naive implementation that searches all Python files in the project tree
|
|
141
|
+
*/
|
|
142
|
+
async findProjectTools(projectRoot) {
|
|
143
|
+
try {
|
|
144
|
+
// Check if Python is available
|
|
145
|
+
try {
|
|
146
|
+
execSync('python3 --version', { stdio: 'ignore' });
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
console.warn('Python 3 not found');
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
// Check if discover_tools.py exists
|
|
153
|
+
if (!fs.existsSync(this.discoverToolsScript)) {
|
|
154
|
+
console.warn(`discover_tools.py not found at ${this.discoverToolsScript}`);
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
// Run the discovery script with 'project-tools' command
|
|
158
|
+
const result = execSync(`python3 "${this.discoverToolsScript}" project-tools "${projectRoot}"`, {
|
|
159
|
+
encoding: 'utf-8',
|
|
160
|
+
maxBuffer: 1024 * 1024 * 10, // 10MB buffer
|
|
161
|
+
});
|
|
162
|
+
return JSON.parse(result);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
console.error('Project tools discovery failed:', error);
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Extract all tools from discovery result
|
|
171
|
+
*/
|
|
172
|
+
extractTools(discovery) {
|
|
173
|
+
if (!discovery)
|
|
174
|
+
return [];
|
|
175
|
+
// Check if it's a directory result
|
|
176
|
+
if ('files' in discovery) {
|
|
177
|
+
const dirResult = discovery;
|
|
178
|
+
const tools = [];
|
|
179
|
+
for (const file of dirResult.files) {
|
|
180
|
+
if (file.success && file.tools) {
|
|
181
|
+
tools.push(...file.tools);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return tools;
|
|
185
|
+
}
|
|
186
|
+
// Single file result
|
|
187
|
+
const fileResult = discovery;
|
|
188
|
+
return fileResult.success ? fileResult.tools : [];
|
|
189
|
+
}
|
|
190
|
+
}
|