@agents-at-scale/ark 0.1.36-rc1 → 0.1.36
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/package.json +1 -1
- package/dist/charts/charts.d.ts +0 -5
- package/dist/charts/charts.js +0 -6
- package/dist/charts/dependencies.d.ts +0 -6
- package/dist/charts/dependencies.js +0 -50
- package/dist/charts/types.d.ts +0 -40
- package/dist/charts/types.js +0 -1
- package/dist/commands/agents/selector.d.ts +0 -8
- package/dist/commands/agents/selector.js +0 -53
- package/dist/commands/agents.d.ts +0 -2
- package/dist/commands/agents.js +0 -53
- package/dist/commands/chat.d.ts +0 -2
- package/dist/commands/chat.js +0 -45
- package/dist/commands/cluster/get-ip.d.ts +0 -2
- package/dist/commands/cluster/get-ip.js +0 -32
- package/dist/commands/cluster/get-type.d.ts +0 -2
- package/dist/commands/cluster/get-type.js +0 -26
- package/dist/commands/completion.d.ts +0 -2
- package/dist/commands/completion.js +0 -265
- package/dist/commands/config.d.ts +0 -2
- package/dist/commands/config.js +0 -44
- package/dist/commands/dashboard.d.ts +0 -3
- package/dist/commands/dashboard.js +0 -39
- package/dist/commands/dev/index.d.ts +0 -3
- package/dist/commands/dev/index.js +0 -9
- package/dist/commands/dev/tool/check.d.ts +0 -2
- package/dist/commands/dev/tool/check.js +0 -142
- package/dist/commands/dev/tool/clean.d.ts +0 -2
- package/dist/commands/dev/tool/clean.js +0 -153
- package/dist/commands/dev/tool/generate.d.ts +0 -2
- package/dist/commands/dev/tool/generate.js +0 -28
- package/dist/commands/dev/tool/index.d.ts +0 -2
- package/dist/commands/dev/tool/index.js +0 -14
- package/dist/commands/dev/tool/init.d.ts +0 -2
- package/dist/commands/dev/tool/init.js +0 -320
- package/dist/commands/dev/tool/shared.d.ts +0 -5
- package/dist/commands/dev/tool/shared.js +0 -258
- package/dist/commands/dev/tool/status.d.ts +0 -2
- package/dist/commands/dev/tool/status.js +0 -136
- package/dist/commands/dev/tool-generate.spec.d.ts +0 -1
- package/dist/commands/dev/tool-generate.spec.js +0 -163
- package/dist/commands/dev/tool.d.ts +0 -2
- package/dist/commands/dev/tool.js +0 -559
- package/dist/commands/dev/tool.spec.d.ts +0 -1
- package/dist/commands/dev/tool.spec.js +0 -48
- package/dist/commands/install.d.ts +0 -3
- package/dist/commands/install.js +0 -147
- package/dist/commands/models/selector.d.ts +0 -8
- package/dist/commands/models/selector.js +0 -53
- package/dist/commands/routes.d.ts +0 -2
- package/dist/commands/routes.js +0 -101
- package/dist/commands/status.d.ts +0 -3
- package/dist/commands/status.js +0 -33
- package/dist/commands/targets.d.ts +0 -2
- package/dist/commands/targets.js +0 -65
- package/dist/commands/teams/selector.d.ts +0 -8
- package/dist/commands/teams/selector.js +0 -55
- package/dist/commands/tools/selector.d.ts +0 -8
- package/dist/commands/tools/selector.js +0 -53
- package/dist/commands/uninstall.d.ts +0 -2
- package/dist/commands/uninstall.js +0 -83
- package/dist/components/DashboardCLI.d.ts +0 -3
- package/dist/components/DashboardCLI.js +0 -149
- package/dist/components/StatusView.d.ts +0 -10
- package/dist/components/StatusView.js +0 -39
- package/dist/config.d.ts +0 -23
- package/dist/config.js +0 -92
- package/dist/lib/arkClient.d.ts +0 -32
- package/dist/lib/arkClient.js +0 -43
- package/dist/lib/commandUtils.d.ts +0 -4
- package/dist/lib/commandUtils.js +0 -18
- package/dist/lib/commandUtils.test.d.ts +0 -1
- package/dist/lib/commandUtils.test.js +0 -44
- package/dist/lib/config.test.d.ts +0 -1
- package/dist/lib/config.test.js +0 -93
- package/dist/lib/consts.d.ts +0 -9
- package/dist/lib/consts.js +0 -13
- package/dist/lib/consts.spec.d.ts +0 -1
- package/dist/lib/consts.spec.js +0 -15
- package/dist/lib/dev/tools/analyzer.d.ts +0 -30
- package/dist/lib/dev/tools/analyzer.js +0 -190
- package/dist/lib/dev/tools/discover_tools.py +0 -392
- package/dist/lib/dev/tools/mcp-types.d.ts +0 -28
- package/dist/lib/dev/tools/mcp-types.js +0 -86
- package/dist/lib/dev/tools/types.d.ts +0 -50
- package/dist/lib/dev/tools/types.js +0 -1
- package/dist/lib/exec.d.ts +0 -1
- package/dist/lib/exec.js +0 -9
- package/dist/lib/gatewayManager.d.ts +0 -24
- package/dist/lib/gatewayManager.js +0 -85
- package/dist/lib/kubernetes.d.ts +0 -28
- package/dist/lib/kubernetes.js +0 -122
- package/dist/lib/portUtils.d.ts +0 -8
- package/dist/lib/portUtils.js +0 -39
- package/dist/lib/progress.d.ts +0 -128
- package/dist/lib/progress.js +0 -273
- package/dist/lib/queryRunner.d.ts +0 -22
- package/dist/lib/queryRunner.js +0 -142
- package/dist/lib/wrappers/git.d.ts +0 -2
- package/dist/lib/wrappers/git.js +0 -43
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import ora from 'ora';
|
|
5
|
-
import inquirer from 'inquirer';
|
|
6
|
-
import fs from 'fs';
|
|
7
|
-
import yaml from 'yaml';
|
|
8
|
-
import output from '../../../lib/output.js';
|
|
9
|
-
import { ArkDevToolAnalyzer } from '../../../lib/dev/tools/analyzer.js';
|
|
10
|
-
import { generateProjectFiles } from './shared.js';
|
|
11
|
-
async function initTool(toolPath) {
|
|
12
|
-
const absolutePath = path.resolve(toolPath);
|
|
13
|
-
const analyzer = new ArkDevToolAnalyzer();
|
|
14
|
-
// Check if .ark.yaml already exists
|
|
15
|
-
const arkConfigPath = path.join(absolutePath, '.ark.yaml');
|
|
16
|
-
if (fs.existsSync(arkConfigPath)) {
|
|
17
|
-
const { overwrite } = await inquirer.prompt([
|
|
18
|
-
{
|
|
19
|
-
type: 'confirm',
|
|
20
|
-
name: 'overwrite',
|
|
21
|
-
message: chalk.yellow('.ark.yaml already exists. Overwrite?'),
|
|
22
|
-
default: false,
|
|
23
|
-
},
|
|
24
|
-
]);
|
|
25
|
-
if (!overwrite) {
|
|
26
|
-
console.log(chalk.gray('Initialization cancelled'));
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
// Initialize configuration object
|
|
31
|
-
const arkConfig = {
|
|
32
|
-
version: '1.0',
|
|
33
|
-
project: {},
|
|
34
|
-
};
|
|
35
|
-
// Step 1: Check if path exists and is a directory
|
|
36
|
-
const checkSpinner = ora('Checking project path...').start();
|
|
37
|
-
const project = await analyzer.discoverProject(absolutePath);
|
|
38
|
-
if (!project || !project.exists) {
|
|
39
|
-
checkSpinner.fail('Path not found');
|
|
40
|
-
output.error(`Path not found: ${absolutePath}`);
|
|
41
|
-
process.exit(1);
|
|
42
|
-
}
|
|
43
|
-
if (!project.is_directory) {
|
|
44
|
-
checkSpinner.fail('Path is not a directory');
|
|
45
|
-
output.error(`Path is not a directory: ${absolutePath}`);
|
|
46
|
-
process.exit(1);
|
|
47
|
-
}
|
|
48
|
-
checkSpinner.succeed('Project path verified');
|
|
49
|
-
arkConfig.project.path = absolutePath;
|
|
50
|
-
// Step 2: Detect platform
|
|
51
|
-
console.log();
|
|
52
|
-
if (!project.platform) {
|
|
53
|
-
console.log(chalk.yellow('⚠ No Python project files found (pyproject.toml or requirements.txt)'));
|
|
54
|
-
const { continueWithoutProject } = await inquirer.prompt([
|
|
55
|
-
{
|
|
56
|
-
type: 'confirm',
|
|
57
|
-
name: 'continueWithoutProject',
|
|
58
|
-
message: 'Continue without project files?',
|
|
59
|
-
default: false,
|
|
60
|
-
},
|
|
61
|
-
]);
|
|
62
|
-
if (!continueWithoutProject) {
|
|
63
|
-
console.log(chalk.gray('Initialization cancelled'));
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
// Set defaults but don't create anything
|
|
67
|
-
arkConfig.project.platform = 'python3';
|
|
68
|
-
arkConfig.project.type = 'none';
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
console.log(chalk.green(`✓ Detected platform: ${chalk.white(project.platform)}`));
|
|
72
|
-
console.log(chalk.gray(` Found from: ${project.project_type === 'pyproject' ? 'pyproject.toml' : 'requirements.txt'}`));
|
|
73
|
-
const { confirmPlatform } = await inquirer.prompt([
|
|
74
|
-
{
|
|
75
|
-
type: 'confirm',
|
|
76
|
-
name: 'confirmPlatform',
|
|
77
|
-
message: `Confirm platform is ${project.platform}?`,
|
|
78
|
-
default: true,
|
|
79
|
-
},
|
|
80
|
-
]);
|
|
81
|
-
if (!confirmPlatform) {
|
|
82
|
-
console.log(chalk.gray('Initialization cancelled'));
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
arkConfig.project.platform = project.platform;
|
|
86
|
-
arkConfig.project.type = project.project_type;
|
|
87
|
-
// Step 3: Project metadata
|
|
88
|
-
if (project.project_name) {
|
|
89
|
-
console.log();
|
|
90
|
-
console.log(chalk.green(`✓ Found project name: ${chalk.white(project.project_name)}`));
|
|
91
|
-
console.log(chalk.gray(` From: pyproject.toml [project.name]`));
|
|
92
|
-
const { confirmName } = await inquirer.prompt([
|
|
93
|
-
{
|
|
94
|
-
type: 'confirm',
|
|
95
|
-
name: 'confirmName',
|
|
96
|
-
message: `Save project name as "${project.project_name}"?`,
|
|
97
|
-
default: true,
|
|
98
|
-
},
|
|
99
|
-
]);
|
|
100
|
-
if (confirmName) {
|
|
101
|
-
arkConfig.project.name = project.project_name;
|
|
102
|
-
}
|
|
103
|
-
if (project.project_version) {
|
|
104
|
-
console.log();
|
|
105
|
-
console.log(chalk.green(`✓ Found project version: ${chalk.white(project.project_version)}`));
|
|
106
|
-
console.log(chalk.gray(` From: pyproject.toml [project.version]`));
|
|
107
|
-
const { confirmVersion } = await inquirer.prompt([
|
|
108
|
-
{
|
|
109
|
-
type: 'confirm',
|
|
110
|
-
name: 'confirmVersion',
|
|
111
|
-
message: `Save project version as "${project.project_version}"?`,
|
|
112
|
-
default: true,
|
|
113
|
-
},
|
|
114
|
-
]);
|
|
115
|
-
if (confirmVersion) {
|
|
116
|
-
arkConfig.project.version = project.project_version;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
// Step 4: Check for FastMCP
|
|
121
|
-
console.log();
|
|
122
|
-
if (project.has_fastmcp) {
|
|
123
|
-
console.log(chalk.green(`✓ Found FastMCP framework: ${chalk.white(`v${project.fastmcp_version || 'unknown'}`)}`));
|
|
124
|
-
console.log(chalk.gray(` From: ${project.project_type === 'pyproject' ? 'pyproject.toml dependencies' : 'requirements.txt'}`));
|
|
125
|
-
const { confirmFramework } = await inquirer.prompt([
|
|
126
|
-
{
|
|
127
|
-
type: 'confirm',
|
|
128
|
-
name: 'confirmFramework',
|
|
129
|
-
message: `Save framework as FastMCP v${project.fastmcp_version}?`,
|
|
130
|
-
default: true,
|
|
131
|
-
},
|
|
132
|
-
]);
|
|
133
|
-
if (confirmFramework) {
|
|
134
|
-
arkConfig.project.framework = 'fastmcp';
|
|
135
|
-
arkConfig.project.frameworkVersion = project.fastmcp_version;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
console.log(chalk.yellow('⚠ FastMCP not found in dependencies'));
|
|
140
|
-
console.log(chalk.gray(` Checked: ${project.project_type === 'pyproject' ? 'pyproject.toml' : 'requirements.txt'}`));
|
|
141
|
-
const { recordMissing } = await inquirer.prompt([
|
|
142
|
-
{
|
|
143
|
-
type: 'confirm',
|
|
144
|
-
name: 'recordMissing',
|
|
145
|
-
message: 'Record that FastMCP is not installed?',
|
|
146
|
-
default: true,
|
|
147
|
-
},
|
|
148
|
-
]);
|
|
149
|
-
if (recordMissing) {
|
|
150
|
-
arkConfig.project.framework = 'none';
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
// Step 5: MCP Transport Configuration (only if FastMCP is detected)
|
|
155
|
-
if (arkConfig.project.framework === 'fastmcp') {
|
|
156
|
-
// Try to detect transport from existing code
|
|
157
|
-
let detectedTransport = null;
|
|
158
|
-
const pythonFiles = fs
|
|
159
|
-
.readdirSync(absolutePath)
|
|
160
|
-
.filter((f) => f.endsWith('.py'));
|
|
161
|
-
for (const file of pythonFiles) {
|
|
162
|
-
const content = fs.readFileSync(path.join(absolutePath, file), 'utf-8');
|
|
163
|
-
if (content.includes('transport="sse"')) {
|
|
164
|
-
detectedTransport = 'sse';
|
|
165
|
-
break;
|
|
166
|
-
}
|
|
167
|
-
else if (content.includes('transport="http"')) {
|
|
168
|
-
detectedTransport = 'http';
|
|
169
|
-
break;
|
|
170
|
-
}
|
|
171
|
-
else if (content.includes('transport="stdio"') ||
|
|
172
|
-
content.includes('.run()')) {
|
|
173
|
-
detectedTransport = 'stdio';
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
if (detectedTransport) {
|
|
178
|
-
console.log(chalk.green(`✓ MCP: Detected transport ${chalk.white(detectedTransport)}`));
|
|
179
|
-
const { confirmTransport } = await inquirer.prompt([
|
|
180
|
-
{
|
|
181
|
-
type: 'confirm',
|
|
182
|
-
name: 'confirmTransport',
|
|
183
|
-
message: `Use transport "${detectedTransport}"?`,
|
|
184
|
-
default: true,
|
|
185
|
-
},
|
|
186
|
-
]);
|
|
187
|
-
if (confirmTransport) {
|
|
188
|
-
arkConfig.mcp = { transport: detectedTransport };
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
// If not detected or not confirmed, ask
|
|
192
|
-
if (!arkConfig.mcp) {
|
|
193
|
-
const { transport } = await inquirer.prompt([
|
|
194
|
-
{
|
|
195
|
-
type: 'list',
|
|
196
|
-
name: 'transport',
|
|
197
|
-
message: 'Select MCP transport for deployment:',
|
|
198
|
-
choices: [
|
|
199
|
-
{
|
|
200
|
-
name: 'SSE (Server-Sent Events) - Recommended for Kubernetes',
|
|
201
|
-
value: 'sse',
|
|
202
|
-
short: 'SSE',
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
name: 'HTTP - Stateless request/response',
|
|
206
|
-
value: 'http',
|
|
207
|
-
short: 'HTTP',
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
name: 'STDIO - Standard input/output for CLI tools',
|
|
211
|
-
value: 'stdio',
|
|
212
|
-
short: 'STDIO',
|
|
213
|
-
},
|
|
214
|
-
],
|
|
215
|
-
default: 'sse',
|
|
216
|
-
},
|
|
217
|
-
]);
|
|
218
|
-
arkConfig.mcp = { transport };
|
|
219
|
-
}
|
|
220
|
-
// Ask for port if not stdio
|
|
221
|
-
if (arkConfig.mcp.transport !== 'stdio') {
|
|
222
|
-
const { port } = await inquirer.prompt([
|
|
223
|
-
{
|
|
224
|
-
type: 'input',
|
|
225
|
-
name: 'port',
|
|
226
|
-
message: 'MCP server port:',
|
|
227
|
-
default: '8080',
|
|
228
|
-
validate: (input) => {
|
|
229
|
-
const num = parseInt(input);
|
|
230
|
-
if (isNaN(num) || num < 1 || num > 65535) {
|
|
231
|
-
return 'Please enter a valid port number (1-65535)';
|
|
232
|
-
}
|
|
233
|
-
return true;
|
|
234
|
-
},
|
|
235
|
-
},
|
|
236
|
-
]);
|
|
237
|
-
arkConfig.mcp.port = parseInt(port);
|
|
238
|
-
// Show configuration snippet
|
|
239
|
-
console.log();
|
|
240
|
-
console.log(chalk.yellow('📝 Add this to your MCP server code:'));
|
|
241
|
-
console.log(chalk.gray('─'.repeat(40)));
|
|
242
|
-
if (arkConfig.mcp.transport === 'sse') {
|
|
243
|
-
console.log(chalk.green(`if __name__ == "__main__":
|
|
244
|
-
import os
|
|
245
|
-
port = int(os.environ.get("PORT", "${arkConfig.mcp.port}"))
|
|
246
|
-
mcp.run(transport="sse", host="0.0.0.0", port=port)`));
|
|
247
|
-
}
|
|
248
|
-
else if (arkConfig.mcp.transport === 'http') {
|
|
249
|
-
console.log(chalk.green(`if __name__ == "__main__":
|
|
250
|
-
import os
|
|
251
|
-
port = int(os.environ.get("PORT", "${arkConfig.mcp.port}"))
|
|
252
|
-
mcp.run(transport="http", host="0.0.0.0", port=port, path="/")`));
|
|
253
|
-
}
|
|
254
|
-
console.log(chalk.gray('─'.repeat(40)));
|
|
255
|
-
console.log(chalk.dim('The PORT environment variable will be set by Kubernetes'));
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
// Step 6: Write .ark.yaml
|
|
259
|
-
console.log();
|
|
260
|
-
const writeSpinner = ora('Writing .ark.yaml configuration...').start();
|
|
261
|
-
try {
|
|
262
|
-
const yamlContent = yaml.stringify(arkConfig);
|
|
263
|
-
fs.writeFileSync(arkConfigPath, yamlContent, 'utf-8');
|
|
264
|
-
writeSpinner.succeed('.ark.yaml created successfully');
|
|
265
|
-
console.log();
|
|
266
|
-
console.log(chalk.blue('Configuration Summary:'));
|
|
267
|
-
console.log(chalk.gray('─'.repeat(40)));
|
|
268
|
-
console.log(`Platform: ${arkConfig.project.platform}`);
|
|
269
|
-
console.log(`Type: ${arkConfig.project.type}`);
|
|
270
|
-
if (arkConfig.project.name) {
|
|
271
|
-
console.log(`Name: ${arkConfig.project.name}`);
|
|
272
|
-
console.log(`Version: ${arkConfig.project.version || 'unknown'}`);
|
|
273
|
-
}
|
|
274
|
-
if (arkConfig.project.framework) {
|
|
275
|
-
console.log(`Framework: ${arkConfig.project.framework}`);
|
|
276
|
-
}
|
|
277
|
-
if (arkConfig.mcp) {
|
|
278
|
-
console.log(`Transport: ${arkConfig.mcp.transport}`);
|
|
279
|
-
if (arkConfig.mcp.port) {
|
|
280
|
-
console.log(`Port: ${arkConfig.mcp.port}`);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
console.log(chalk.gray('─'.repeat(40)));
|
|
284
|
-
console.log();
|
|
285
|
-
console.log(chalk.green('✓ Initialization complete!'));
|
|
286
|
-
// Step 6: Ask about generating project files
|
|
287
|
-
console.log();
|
|
288
|
-
const { generateFiles } = await inquirer.prompt([
|
|
289
|
-
{
|
|
290
|
-
type: 'confirm',
|
|
291
|
-
name: 'generateFiles',
|
|
292
|
-
message: 'Generate project files (Dockerfile, .dockerignore, etc.)?',
|
|
293
|
-
default: true,
|
|
294
|
-
},
|
|
295
|
-
]);
|
|
296
|
-
if (generateFiles) {
|
|
297
|
-
console.log();
|
|
298
|
-
await generateProjectFiles(absolutePath, {
|
|
299
|
-
interactive: true,
|
|
300
|
-
dryRun: false,
|
|
301
|
-
overwrite: false,
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
console.log();
|
|
305
|
-
console.log(' • Edit ' + chalk.cyan('.ark.yaml') + ' to update configuration');
|
|
306
|
-
}
|
|
307
|
-
catch (error) {
|
|
308
|
-
writeSpinner.fail('Failed to write .ark.yaml');
|
|
309
|
-
output.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
310
|
-
process.exit(1);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
export function createInitCommand() {
|
|
314
|
-
const initCommand = new Command('init');
|
|
315
|
-
initCommand
|
|
316
|
-
.description('Initialize an MCP tool project with .ark.yaml configuration')
|
|
317
|
-
.argument('<path>', 'Path to the tool directory')
|
|
318
|
-
.action(initTool);
|
|
319
|
-
return initCommand;
|
|
320
|
-
}
|
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
import ora from 'ora';
|
|
5
|
-
import fs from 'fs';
|
|
6
|
-
import yaml from 'yaml';
|
|
7
|
-
import { execSync } from 'child_process';
|
|
8
|
-
import output from '../../../lib/output.js';
|
|
9
|
-
export async function generateProjectFiles(toolPath, options = {
|
|
10
|
-
interactive: true,
|
|
11
|
-
dryRun: false,
|
|
12
|
-
overwrite: false,
|
|
13
|
-
}) {
|
|
14
|
-
const absolutePath = path.resolve(toolPath);
|
|
15
|
-
const arkConfigPath = path.join(absolutePath, '.ark.yaml');
|
|
16
|
-
// Check if .ark.yaml exists
|
|
17
|
-
if (!fs.existsSync(arkConfigPath)) {
|
|
18
|
-
output.error('.ark.yaml not found. Run "ark dev tool init" first.');
|
|
19
|
-
process.exit(1);
|
|
20
|
-
}
|
|
21
|
-
// Load .ark.yaml
|
|
22
|
-
const arkConfig = yaml.parse(fs.readFileSync(arkConfigPath, 'utf-8'));
|
|
23
|
-
const generateSpinner = options.dryRun
|
|
24
|
-
? null
|
|
25
|
-
: ora('Generating project files...').start();
|
|
26
|
-
try {
|
|
27
|
-
// Find template directory - templates are in the source tree
|
|
28
|
-
const currentFile = fileURLToPath(import.meta.url);
|
|
29
|
-
const distDir = path.dirname(path.dirname(path.dirname(path.dirname(currentFile)))); // Goes to dist/
|
|
30
|
-
const arkCliDir = path.dirname(distDir); // Goes to ark-cli/
|
|
31
|
-
const templateDir = path.join(arkCliDir, 'templates', 'python-mcp-tool');
|
|
32
|
-
if (!fs.existsSync(templateDir)) {
|
|
33
|
-
if (generateSpinner) {
|
|
34
|
-
generateSpinner.fail('Template directory not found');
|
|
35
|
-
}
|
|
36
|
-
console.log(chalk.yellow('Could not find templates at: ' + templateDir));
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
// Use a Set to track all generated items (both files and directories)
|
|
40
|
-
const generatedItems = new Set();
|
|
41
|
-
const skippedItems = new Set();
|
|
42
|
-
const errors = [];
|
|
43
|
-
// Process all files and directories in the template directory
|
|
44
|
-
processTemplateDirectory(templateDir, absolutePath, arkConfig, options, {
|
|
45
|
-
generatedItems,
|
|
46
|
-
skippedItems,
|
|
47
|
-
errors,
|
|
48
|
-
});
|
|
49
|
-
if (!options.dryRun) {
|
|
50
|
-
if (generatedItems.size > 0) {
|
|
51
|
-
generateSpinner.succeed(`Generated ${generatedItems.size} file(s)`);
|
|
52
|
-
// Show the generated files after stopping the spinner
|
|
53
|
-
if (options.interactive && generatedItems.size > 0) {
|
|
54
|
-
// Sort items for consistent display
|
|
55
|
-
const sortedItems = Array.from(generatedItems).sort();
|
|
56
|
-
sortedItems.forEach((item) => {
|
|
57
|
-
console.log(chalk.green(` ✓ Generated ${item}`));
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
else if (skippedItems.size > 0) {
|
|
62
|
-
generateSpinner.warn(`No new files generated (${skippedItems.size} already exist)`);
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
generateSpinner.warn('No files to generate');
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
if (errors.length > 0 && options.interactive) {
|
|
69
|
-
console.log(chalk.red('Errors:'));
|
|
70
|
-
errors.forEach((e) => console.log(chalk.red(` - ${e}`)));
|
|
71
|
-
}
|
|
72
|
-
return generatedItems.size > 0;
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
if (!options.dryRun && generateSpinner) {
|
|
76
|
-
generateSpinner.fail('Failed to generate project files');
|
|
77
|
-
}
|
|
78
|
-
if (options.interactive) {
|
|
79
|
-
console.log(chalk.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
80
|
-
}
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
function processTemplateDirectory(sourceDir, targetDir, arkConfig, options, stats, rootTargetDir) {
|
|
85
|
-
// Track the root target directory for relative path calculations
|
|
86
|
-
const actualRootTargetDir = rootTargetDir || targetDir;
|
|
87
|
-
// Check if this directory needs to be created
|
|
88
|
-
const dirExists = fs.existsSync(targetDir);
|
|
89
|
-
// Create target directory if it doesn't exist
|
|
90
|
-
if (!options.dryRun && !dirExists) {
|
|
91
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
92
|
-
// Track the directory creation (relative to root)
|
|
93
|
-
const relativePath = path.relative(actualRootTargetDir, targetDir);
|
|
94
|
-
if (relativePath) {
|
|
95
|
-
// Don't add empty string for root directory
|
|
96
|
-
stats.generatedItems.add(`${relativePath}/`);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
|
|
100
|
-
for (const entry of entries) {
|
|
101
|
-
const sourcePath = path.join(sourceDir, entry.name);
|
|
102
|
-
if (entry.isDirectory()) {
|
|
103
|
-
// Recursively process subdirectories
|
|
104
|
-
const targetSubDir = path.join(targetDir, entry.name);
|
|
105
|
-
if (options.dryRun) {
|
|
106
|
-
const relativePath = path.relative(actualRootTargetDir, targetSubDir);
|
|
107
|
-
console.log(chalk.cyan(`\n=== ${relativePath}/ directory ===`));
|
|
108
|
-
console.log(`Directory: ${entry.name}`);
|
|
109
|
-
console.log(chalk.cyan(`=== END ${relativePath}/ directory ===\n`));
|
|
110
|
-
}
|
|
111
|
-
processTemplateDirectory(sourcePath, targetSubDir, arkConfig, options, stats, actualRootTargetDir);
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
// Process file - check if it's a template
|
|
115
|
-
const isTemplate = entry.name.startsWith('template.');
|
|
116
|
-
const targetFileName = isTemplate
|
|
117
|
-
? entry.name.replace('template.', '')
|
|
118
|
-
: entry.name;
|
|
119
|
-
const targetPath = path.join(targetDir, targetFileName);
|
|
120
|
-
// Make target path relative to the original target directory for display
|
|
121
|
-
const displayPath = path.relative(actualRootTargetDir, targetPath);
|
|
122
|
-
// Check if file already exists
|
|
123
|
-
const fileExists = fs.existsSync(targetPath);
|
|
124
|
-
if (!options.dryRun && !options.overwrite && fileExists) {
|
|
125
|
-
if (options.interactive) {
|
|
126
|
-
console.log(chalk.yellow(` Skipping ${displayPath} (already exists)`));
|
|
127
|
-
}
|
|
128
|
-
stats.skippedItems.add(displayPath);
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
try {
|
|
132
|
-
let content;
|
|
133
|
-
if (isTemplate) {
|
|
134
|
-
// Process template file with helm - pass the actual root target directory
|
|
135
|
-
content = processTemplateFile(sourcePath, targetFileName, arkConfig, options, actualRootTargetDir);
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
// Regular file, just read it
|
|
139
|
-
content = fs.readFileSync(sourcePath, 'utf-8');
|
|
140
|
-
}
|
|
141
|
-
// In dry-run mode, print to stdout; otherwise write the file
|
|
142
|
-
if (options.dryRun) {
|
|
143
|
-
console.log(chalk.cyan(`\n=== ${displayPath} ===`));
|
|
144
|
-
console.log(content);
|
|
145
|
-
console.log(chalk.cyan(`=== END ${displayPath} ===\n`));
|
|
146
|
-
stats.generatedItems.add(displayPath);
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
fs.writeFileSync(targetPath, content);
|
|
150
|
-
stats.generatedItems.add(displayPath);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
catch (err) {
|
|
154
|
-
const errorMsg = `${displayPath}: ${err instanceof Error ? err.message : 'Unknown error'}`;
|
|
155
|
-
stats.errors.push(errorMsg);
|
|
156
|
-
if (options.dryRun) {
|
|
157
|
-
console.log(chalk.red(`Error processing ${displayPath}: ${err instanceof Error ? err.message : 'Unknown error'}`));
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
function processTemplateFile(templatePath, targetFileName, arkConfig, options, rootTargetDir) {
|
|
164
|
-
// Prepare consistent values structure for all templates
|
|
165
|
-
const projectName = arkConfig.project?.name ||
|
|
166
|
-
path.basename(rootTargetDir || path.dirname(templatePath));
|
|
167
|
-
const values = {
|
|
168
|
-
project: {
|
|
169
|
-
name: projectName,
|
|
170
|
-
type: arkConfig.project?.type || 'pyproject',
|
|
171
|
-
platform: arkConfig.project?.platform || 'python3',
|
|
172
|
-
version: arkConfig.project?.version || '0.1.0',
|
|
173
|
-
framework: arkConfig.project?.framework || 'fastmcp',
|
|
174
|
-
description: arkConfig.project?.description || `${projectName} MCP tool`,
|
|
175
|
-
},
|
|
176
|
-
python: {
|
|
177
|
-
version: '3.11', // Default Python version
|
|
178
|
-
module_name: projectName.replace(/-/g, '_'), // Convert kebab-case to snake_case
|
|
179
|
-
},
|
|
180
|
-
mcp: {
|
|
181
|
-
transport: arkConfig.mcp?.transport || 'sse', // Default to SSE for Kubernetes
|
|
182
|
-
port: arkConfig.mcp?.port || 8080,
|
|
183
|
-
healthCheck: arkConfig.mcp?.transport !== 'stdio', // No health checks for stdio
|
|
184
|
-
},
|
|
185
|
-
devspace: {
|
|
186
|
-
namespace: 'default',
|
|
187
|
-
image: {
|
|
188
|
-
repository: projectName, // Default repository name
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
};
|
|
192
|
-
// Create temp directory for helm processing
|
|
193
|
-
const tempDir = path.join('/tmp', `ark-helm-${Date.now()}`);
|
|
194
|
-
const tempChartDir = path.join(tempDir, 'chart');
|
|
195
|
-
const tempTemplatesDir = path.join(tempChartDir, 'templates');
|
|
196
|
-
fs.mkdirSync(tempTemplatesDir, { recursive: true });
|
|
197
|
-
try {
|
|
198
|
-
// Write minimal Chart.yaml
|
|
199
|
-
fs.writeFileSync(path.join(tempChartDir, 'Chart.yaml'), 'apiVersion: v2\nname: temp\nversion: 0.1.0\n');
|
|
200
|
-
// Write values file
|
|
201
|
-
const tempValuesFile = path.join(tempDir, 'values.yaml');
|
|
202
|
-
fs.writeFileSync(tempValuesFile, yaml.stringify(values));
|
|
203
|
-
// Determine if this is a YAML file
|
|
204
|
-
const isYamlFile = targetFileName.endsWith('.yaml') || targetFileName.endsWith('.yml');
|
|
205
|
-
// For dotfiles, replace the leading dot with 'dot' for helm processing
|
|
206
|
-
const helmTemplateName = targetFileName.startsWith('.')
|
|
207
|
-
? 'dot' + targetFileName.substring(1)
|
|
208
|
-
: targetFileName;
|
|
209
|
-
if (isYamlFile) {
|
|
210
|
-
// Copy YAML files directly
|
|
211
|
-
fs.copyFileSync(templatePath, path.join(tempTemplatesDir, helmTemplateName));
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
// Wrap non-YAML content in a YAML structure for helm
|
|
215
|
-
const originalContent = fs.readFileSync(templatePath, 'utf-8');
|
|
216
|
-
const wrappedContent = `# Wrapped for helm processing\ncontent: |\n${originalContent
|
|
217
|
-
.split('\n')
|
|
218
|
-
.map((line) => ' ' + line)
|
|
219
|
-
.join('\n')}`;
|
|
220
|
-
fs.writeFileSync(path.join(tempTemplatesDir, helmTemplateName + '.yaml'), wrappedContent);
|
|
221
|
-
}
|
|
222
|
-
// Run helm template to process the file
|
|
223
|
-
const actualHelmFile = isYamlFile
|
|
224
|
-
? helmTemplateName
|
|
225
|
-
: helmTemplateName + '.yaml';
|
|
226
|
-
const helmCommand = `helm template temp ${tempChartDir} --values ${tempValuesFile} -s templates/${actualHelmFile}`;
|
|
227
|
-
let content;
|
|
228
|
-
try {
|
|
229
|
-
content = execSync(helmCommand, {
|
|
230
|
-
encoding: 'utf-8',
|
|
231
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
232
|
-
});
|
|
233
|
-
// Remove the YAML document separator that helm adds
|
|
234
|
-
content = content.replace(/^---\n/, '');
|
|
235
|
-
// Remove helm's source comment
|
|
236
|
-
content = content.replace(/^# Source:.*\n/gm, '');
|
|
237
|
-
// For non-YAML files, extract the content from the wrapped YAML
|
|
238
|
-
if (!isYamlFile) {
|
|
239
|
-
// Parse the YAML to extract the content field
|
|
240
|
-
const yamlContent = yaml.parse(content);
|
|
241
|
-
content = yamlContent.content || '';
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
catch (helmError) {
|
|
245
|
-
const errorMsg = helmError.stderr ||
|
|
246
|
-
helmError.message ||
|
|
247
|
-
'Unknown error';
|
|
248
|
-
throw new Error(`Failed to template ${targetFileName}: ${errorMsg}`);
|
|
249
|
-
}
|
|
250
|
-
return content;
|
|
251
|
-
}
|
|
252
|
-
finally {
|
|
253
|
-
// Clean up temp directory
|
|
254
|
-
if (fs.existsSync(tempDir)) {
|
|
255
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|