@agents-at-scale/ark 0.1.34 → 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.
Files changed (132) hide show
  1. package/dist/arkServices.d.ts +50 -0
  2. package/dist/arkServices.js +153 -0
  3. package/dist/charts/charts.d.ts +5 -0
  4. package/dist/charts/charts.js +6 -0
  5. package/dist/charts/dependencies.d.ts +6 -0
  6. package/dist/charts/dependencies.js +50 -0
  7. package/dist/charts/types.d.ts +40 -0
  8. package/dist/charts/types.js +1 -0
  9. package/dist/commands/agents/index.d.ts +2 -0
  10. package/dist/commands/agents/index.js +56 -0
  11. package/dist/commands/agents/selector.d.ts +8 -0
  12. package/dist/commands/agents/selector.js +53 -0
  13. package/dist/commands/agents.d.ts +2 -0
  14. package/dist/commands/agents.js +53 -0
  15. package/dist/commands/chat/index.d.ts +2 -0
  16. package/dist/commands/chat/index.js +45 -0
  17. package/dist/commands/chat.d.ts +2 -0
  18. package/dist/commands/chat.js +45 -0
  19. package/dist/commands/cluster/get.d.ts +2 -0
  20. package/dist/commands/cluster/get.js +39 -0
  21. package/dist/commands/cluster/index.js +2 -4
  22. package/dist/commands/completion/index.d.ts +2 -0
  23. package/dist/commands/completion/index.js +268 -0
  24. package/dist/commands/completion.js +159 -2
  25. package/dist/commands/config/index.d.ts +2 -0
  26. package/dist/commands/config/index.js +42 -0
  27. package/dist/commands/config.d.ts +0 -3
  28. package/dist/commands/config.js +38 -321
  29. package/dist/commands/dashboard/index.d.ts +3 -0
  30. package/dist/commands/dashboard/index.js +39 -0
  31. package/dist/commands/dashboard.d.ts +3 -0
  32. package/dist/commands/dashboard.js +39 -0
  33. package/dist/commands/dev/index.d.ts +2 -0
  34. package/dist/commands/dev/index.js +9 -0
  35. package/dist/commands/dev/tool/check.d.ts +2 -0
  36. package/dist/commands/dev/tool/check.js +142 -0
  37. package/dist/commands/dev/tool/clean.d.ts +2 -0
  38. package/dist/commands/dev/tool/clean.js +153 -0
  39. package/dist/commands/dev/tool/generate.d.ts +2 -0
  40. package/dist/commands/dev/tool/generate.js +28 -0
  41. package/dist/commands/dev/tool/index.d.ts +2 -0
  42. package/dist/commands/dev/tool/index.js +14 -0
  43. package/dist/commands/dev/tool/init.d.ts +2 -0
  44. package/dist/commands/dev/tool/init.js +320 -0
  45. package/dist/commands/dev/tool/shared.d.ts +5 -0
  46. package/dist/commands/dev/tool/shared.js +256 -0
  47. package/dist/commands/dev/tool/status.d.ts +2 -0
  48. package/dist/commands/dev/tool/status.js +136 -0
  49. package/dist/commands/dev/tool.d.ts +2 -0
  50. package/dist/commands/dev/tool.js +559 -0
  51. package/dist/commands/generate/config.js +5 -24
  52. package/dist/commands/generate/generators/mcpserver.d.ts +2 -1
  53. package/dist/commands/generate/generators/mcpserver.js +26 -5
  54. package/dist/commands/install/index.d.ts +6 -0
  55. package/dist/commands/install/index.js +165 -0
  56. package/dist/commands/install.d.ts +3 -0
  57. package/dist/commands/install.js +147 -0
  58. package/dist/commands/models/create.d.ts +1 -0
  59. package/dist/commands/models/create.js +213 -0
  60. package/dist/commands/models/index.d.ts +2 -0
  61. package/dist/commands/models/index.js +65 -0
  62. package/dist/commands/models/selector.d.ts +8 -0
  63. package/dist/commands/models/selector.js +53 -0
  64. package/dist/commands/routes/index.d.ts +2 -0
  65. package/dist/commands/routes/index.js +101 -0
  66. package/dist/commands/routes.d.ts +2 -0
  67. package/dist/commands/routes.js +101 -0
  68. package/dist/commands/status/index.d.ts +3 -0
  69. package/dist/commands/status/index.js +33 -0
  70. package/dist/commands/status.d.ts +3 -0
  71. package/dist/commands/status.js +33 -0
  72. package/dist/commands/targets/index.d.ts +2 -0
  73. package/dist/commands/targets/index.js +65 -0
  74. package/dist/commands/targets.d.ts +2 -0
  75. package/dist/commands/targets.js +65 -0
  76. package/dist/commands/teams/index.d.ts +2 -0
  77. package/dist/commands/teams/index.js +54 -0
  78. package/dist/commands/teams/selector.d.ts +8 -0
  79. package/dist/commands/teams/selector.js +55 -0
  80. package/dist/commands/tools/index.d.ts +2 -0
  81. package/dist/commands/tools/index.js +54 -0
  82. package/dist/commands/tools/selector.d.ts +8 -0
  83. package/dist/commands/tools/selector.js +53 -0
  84. package/dist/commands/uninstall/index.d.ts +2 -0
  85. package/dist/commands/uninstall/index.js +84 -0
  86. package/dist/commands/uninstall.d.ts +2 -0
  87. package/dist/commands/uninstall.js +83 -0
  88. package/dist/components/ChatUI.d.ts +16 -0
  89. package/dist/components/ChatUI.js +801 -0
  90. package/dist/components/StatusView.d.ts +10 -0
  91. package/dist/components/StatusView.js +39 -0
  92. package/dist/components/statusChecker.d.ts +10 -13
  93. package/dist/components/statusChecker.js +128 -65
  94. package/dist/config.js +3 -10
  95. package/dist/index.d.ts +1 -1
  96. package/dist/index.js +31 -36
  97. package/dist/lib/arkApiClient.d.ts +53 -0
  98. package/dist/lib/arkApiClient.js +102 -0
  99. package/dist/lib/arkApiProxy.d.ts +9 -0
  100. package/dist/lib/arkApiProxy.js +22 -0
  101. package/dist/lib/arkServiceProxy.d.ts +14 -0
  102. package/dist/lib/arkServiceProxy.js +93 -0
  103. package/dist/lib/arkStatus.d.ts +5 -0
  104. package/dist/lib/arkStatus.js +20 -0
  105. package/dist/lib/chatClient.d.ts +33 -0
  106. package/dist/lib/chatClient.js +101 -0
  107. package/dist/lib/cluster.d.ts +2 -1
  108. package/dist/lib/cluster.js +27 -3
  109. package/dist/lib/commandUtils.d.ts +4 -0
  110. package/dist/lib/commandUtils.js +18 -0
  111. package/dist/lib/commandUtils.test.d.ts +1 -0
  112. package/dist/lib/commandUtils.test.js +44 -0
  113. package/dist/lib/config.d.ts +24 -80
  114. package/dist/lib/config.js +68 -205
  115. package/dist/lib/config.test.d.ts +1 -0
  116. package/dist/lib/config.test.js +93 -0
  117. package/dist/lib/dev/tools/analyzer.d.ts +30 -0
  118. package/dist/lib/dev/tools/analyzer.js +190 -0
  119. package/dist/lib/dev/tools/discover_tools.py +392 -0
  120. package/dist/lib/dev/tools/mcp-types.d.ts +28 -0
  121. package/dist/lib/dev/tools/mcp-types.js +86 -0
  122. package/dist/lib/dev/tools/types.d.ts +50 -0
  123. package/dist/lib/dev/tools/types.js +1 -0
  124. package/dist/lib/output.d.ts +36 -0
  125. package/dist/lib/output.js +89 -0
  126. package/dist/lib/types.d.ts +8 -3
  127. package/dist/types/types.d.ts +40 -0
  128. package/dist/types/types.js +1 -0
  129. package/dist/ui/MainMenu.js +158 -90
  130. package/dist/ui/statusFormatter.d.ts +4 -1
  131. package/dist/ui/statusFormatter.js +91 -19
  132. package/package.json +16 -4
@@ -0,0 +1,6 @@
1
+ import { Command } from 'commander';
2
+ export declare function installArk(options?: {
3
+ yes?: boolean;
4
+ waitForReady?: string;
5
+ }): Promise<void>;
6
+ export declare function createInstallCommand(): Command;
@@ -0,0 +1,165 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { execa } from 'execa';
4
+ import inquirer from 'inquirer';
5
+ import { isCommandAvailable } from '../../lib/commandUtils.js';
6
+ import { getClusterInfo } from '../../lib/cluster.js';
7
+ import output from '../../lib/output.js';
8
+ import { getInstallableServices, arkDependencies } from '../../arkServices.js';
9
+ import { isArkReady } from '../../lib/arkStatus.js';
10
+ import ora from 'ora';
11
+ export async function installArk(options = {}) {
12
+ // Validate that --wait-for-ready requires -y
13
+ if (options.waitForReady && !options.yes) {
14
+ output.error('--wait-for-ready requires -y flag for non-interactive mode');
15
+ process.exit(1);
16
+ }
17
+ // Check if helm is installed
18
+ const helmInstalled = await isCommandAvailable('helm');
19
+ if (!helmInstalled) {
20
+ output.error('helm is not installed. please install helm first:');
21
+ output.info('https://helm.sh/docs/intro/install/');
22
+ process.exit(1);
23
+ }
24
+ // Check if kubectl is installed (needed for some dependencies)
25
+ const kubectlInstalled = await isCommandAvailable('kubectl');
26
+ if (!kubectlInstalled) {
27
+ output.error('kubectl is not installed. please install kubectl first:');
28
+ output.info('https://kubernetes.io/docs/tasks/tools/');
29
+ process.exit(1);
30
+ }
31
+ // Check cluster connectivity
32
+ const clusterInfo = await getClusterInfo();
33
+ if (clusterInfo.error) {
34
+ output.error('no kubernetes cluster detected');
35
+ output.info('please ensure you have a running cluster and kubectl is configured.');
36
+ output.info('');
37
+ output.info('for local development, we recommend minikube:');
38
+ output.info('• install: https://minikube.sigs.k8s.io/docs/start');
39
+ output.info('• start cluster: minikube start');
40
+ output.info('');
41
+ output.info('alternatively, you can use kind or docker desktop.');
42
+ process.exit(1);
43
+ }
44
+ // Show cluster info
45
+ output.success(`connected to cluster: ${chalk.bold(clusterInfo.context)}`);
46
+ output.info(`type: ${clusterInfo.type}`);
47
+ output.info(`namespace: ${clusterInfo.namespace}`);
48
+ if (clusterInfo.ip) {
49
+ output.info(`ip: ${clusterInfo.ip}`);
50
+ }
51
+ console.log(); // Add blank line after cluster info
52
+ // Ask about installing dependencies
53
+ const shouldInstallDeps = options.yes || (await inquirer.prompt([
54
+ {
55
+ type: 'confirm',
56
+ name: 'shouldInstallDeps',
57
+ message: 'install required dependencies (cert-manager, gateway api)?',
58
+ default: true,
59
+ },
60
+ ])).shouldInstallDeps;
61
+ if (shouldInstallDeps) {
62
+ for (const dep of Object.values(arkDependencies)) {
63
+ output.info(`installing ${dep.description || dep.name}...`);
64
+ try {
65
+ await execa(dep.command, dep.args, {
66
+ stdio: 'inherit',
67
+ });
68
+ output.success(`${dep.name} completed`);
69
+ console.log(); // Add blank line after dependency
70
+ }
71
+ catch {
72
+ console.log(); // Add blank line after error
73
+ process.exit(1);
74
+ }
75
+ }
76
+ }
77
+ // Get installable services and iterate through them
78
+ const services = getInstallableServices();
79
+ for (const service of Object.values(services)) {
80
+ // Ask for confirmation
81
+ const shouldInstall = options.yes || (await inquirer.prompt([
82
+ {
83
+ type: 'confirm',
84
+ name: 'shouldInstall',
85
+ message: `install ${chalk.bold(service.name)}? ${service.description ? chalk.gray(`(${service.description.toLowerCase()})`) : ''}`,
86
+ default: true,
87
+ },
88
+ ])).shouldInstall;
89
+ if (!shouldInstall) {
90
+ output.warning(`skipping ${service.name}`);
91
+ continue;
92
+ }
93
+ try {
94
+ // Build helm arguments
95
+ const helmArgs = [
96
+ 'upgrade',
97
+ '--install',
98
+ service.helmReleaseName,
99
+ service.chartPath,
100
+ '--namespace',
101
+ service.namespace,
102
+ ];
103
+ // Add any additional args from the service definition
104
+ if (service.installArgs) {
105
+ helmArgs.push(...service.installArgs);
106
+ }
107
+ // Run helm upgrade --install with streaming output
108
+ await execa('helm', helmArgs, {
109
+ stdio: 'inherit',
110
+ });
111
+ console.log(); // Add blank line after command output
112
+ }
113
+ catch {
114
+ // Continue with remaining services on error
115
+ console.log(); // Add blank line after error output
116
+ }
117
+ }
118
+ // Wait for ARK to be ready if requested
119
+ if (options.waitForReady) {
120
+ // Parse timeout value (e.g., '30s', '2m', '60')
121
+ const parseTimeout = (value) => {
122
+ const match = value.match(/^(\d+)([sm])?$/);
123
+ if (!match) {
124
+ throw new Error('Invalid timeout format. Use format like 30s or 2m');
125
+ }
126
+ const num = parseInt(match[1], 10);
127
+ const unit = match[2] || 's';
128
+ return unit === 'm' ? num * 60 : num;
129
+ };
130
+ try {
131
+ const timeoutSeconds = parseTimeout(options.waitForReady);
132
+ const startTime = Date.now();
133
+ const endTime = startTime + timeoutSeconds * 1000;
134
+ const spinner = ora(`Waiting for ARK to be ready (timeout: ${timeoutSeconds}s)...`).start();
135
+ while (Date.now() < endTime) {
136
+ if (await isArkReady()) {
137
+ spinner.succeed('ARK is ready!');
138
+ return;
139
+ }
140
+ const elapsed = Math.floor((Date.now() - startTime) / 1000);
141
+ spinner.text = `Waiting for ARK to be ready (${elapsed}/${timeoutSeconds}s)...`;
142
+ // Wait 2 seconds before checking again
143
+ await new Promise(resolve => setTimeout(resolve, 2000));
144
+ }
145
+ // Timeout reached
146
+ spinner.fail(`ARK did not become ready within ${timeoutSeconds} seconds`);
147
+ process.exit(1);
148
+ }
149
+ catch (error) {
150
+ output.error(`Failed to wait for ready: ${error instanceof Error ? error.message : 'Unknown error'}`);
151
+ process.exit(1);
152
+ }
153
+ }
154
+ }
155
+ export function createInstallCommand() {
156
+ const command = new Command('install');
157
+ command
158
+ .description('Install ARK components using Helm')
159
+ .option('-y, --yes', 'automatically confirm all installations')
160
+ .option('--wait-for-ready <timeout>', 'wait for ARK to be ready after installation (e.g., 30s, 2m)')
161
+ .action(async (options) => {
162
+ await installArk(options);
163
+ });
164
+ return command;
165
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function installArk(): Promise<void>;
3
+ export declare function createInstallCommand(): Command;
@@ -0,0 +1,147 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { execa } from 'execa';
4
+ import inquirer from 'inquirer';
5
+ import { isCommandAvailable } from '../lib/commandUtils.js';
6
+ import { getClusterInfo } from '../lib/cluster.js';
7
+ import output from '../lib/output.js';
8
+ import { getInstallableServices, arkDependencies } from '../arkServices.js';
9
+ import { createModel } from './models/create.js';
10
+ export async function installArk() {
11
+ // Check if helm is installed
12
+ const helmInstalled = await isCommandAvailable('helm');
13
+ if (!helmInstalled) {
14
+ output.error('helm is not installed. please install helm first:');
15
+ output.info('https://helm.sh/docs/intro/install/');
16
+ process.exit(1);
17
+ }
18
+ // Check if kubectl is installed (needed for some dependencies)
19
+ const kubectlInstalled = await isCommandAvailable('kubectl');
20
+ if (!kubectlInstalled) {
21
+ output.error('kubectl is not installed. please install kubectl first:');
22
+ output.info('https://kubernetes.io/docs/tasks/tools/');
23
+ process.exit(1);
24
+ }
25
+ // Check cluster connectivity
26
+ const clusterInfo = await getClusterInfo();
27
+ if (clusterInfo.error) {
28
+ output.error('no kubernetes cluster detected');
29
+ output.info('please ensure you have a running cluster and kubectl is configured.');
30
+ output.info('');
31
+ output.info('for local development, we recommend minikube:');
32
+ output.info('• install: https://minikube.sigs.k8s.io/docs/start');
33
+ output.info('• start cluster: minikube start');
34
+ output.info('');
35
+ output.info('alternatively, you can use kind or docker desktop.');
36
+ process.exit(1);
37
+ }
38
+ // Show cluster info
39
+ output.success(`connected to cluster: ${chalk.bold(clusterInfo.context)}`);
40
+ output.info(`type: ${clusterInfo.type}`);
41
+ output.info(`namespace: ${clusterInfo.namespace}`);
42
+ if (clusterInfo.ip) {
43
+ output.info(`ip: ${clusterInfo.ip}`);
44
+ }
45
+ console.log(); // Add blank line after cluster info
46
+ // Ask about installing dependencies
47
+ const { shouldInstallDeps } = await inquirer.prompt([
48
+ {
49
+ type: 'confirm',
50
+ name: 'shouldInstallDeps',
51
+ message: 'install required dependencies (cert-manager, gateway api)?',
52
+ default: true,
53
+ },
54
+ ]);
55
+ if (shouldInstallDeps) {
56
+ for (const dep of Object.values(arkDependencies)) {
57
+ output.info(`installing ${dep.description || dep.name}...`);
58
+ try {
59
+ await execa(dep.command, dep.args, {
60
+ stdio: 'inherit',
61
+ });
62
+ output.success(`${dep.name} completed`);
63
+ console.log(); // Add blank line after dependency
64
+ }
65
+ catch {
66
+ console.log(); // Add blank line after error
67
+ process.exit(1);
68
+ }
69
+ }
70
+ }
71
+ // Get installable services and iterate through them
72
+ const services = getInstallableServices();
73
+ for (const service of Object.values(services)) {
74
+ // Ask for confirmation
75
+ const { shouldInstall } = await inquirer.prompt([
76
+ {
77
+ type: 'confirm',
78
+ name: 'shouldInstall',
79
+ message: `install ${chalk.bold(service.name)}? ${service.description ? chalk.gray(`(${service.description.toLowerCase()})`) : ''}`,
80
+ default: true,
81
+ },
82
+ ]);
83
+ if (!shouldInstall) {
84
+ output.warning(`skipping ${service.name}`);
85
+ continue;
86
+ }
87
+ try {
88
+ // Build helm arguments
89
+ const helmArgs = [
90
+ 'upgrade',
91
+ '--install',
92
+ service.helmReleaseName,
93
+ service.chartPath,
94
+ '--namespace',
95
+ service.namespace,
96
+ ];
97
+ // Add any additional args from the service definition
98
+ if (service.installArgs) {
99
+ helmArgs.push(...service.installArgs);
100
+ }
101
+ // Run helm upgrade --install with streaming output
102
+ await execa('helm', helmArgs, {
103
+ stdio: 'inherit',
104
+ });
105
+ console.log(); // Add blank line after command output
106
+ }
107
+ catch {
108
+ // Continue with remaining services on error
109
+ console.log(); // Add blank line after error output
110
+ }
111
+ }
112
+ // Check for default model after installing services
113
+ output.info('checking for default model...');
114
+ let modelExists = false;
115
+ try {
116
+ await execa('kubectl', ['get', 'model', 'default'], { stdio: 'pipe' });
117
+ modelExists = true;
118
+ output.success('default model already configured');
119
+ }
120
+ catch {
121
+ output.warning('no default model found');
122
+ }
123
+ if (!modelExists) {
124
+ const { shouldCreateModel } = await inquirer.prompt([
125
+ {
126
+ type: 'confirm',
127
+ name: 'shouldCreateModel',
128
+ message: 'would you like to create a default model?',
129
+ default: true,
130
+ },
131
+ ]);
132
+ if (shouldCreateModel) {
133
+ await createModel('default');
134
+ }
135
+ else {
136
+ output.warning('skipping model creation');
137
+ output.info('you can create a model later using ark models create or the dashboard');
138
+ }
139
+ }
140
+ }
141
+ export function createInstallCommand() {
142
+ const command = new Command('install');
143
+ command.description('Install ARK components using Helm').action(async () => {
144
+ await installArk();
145
+ });
146
+ return command;
147
+ }
@@ -0,0 +1 @@
1
+ export declare function createModel(modelName?: string): Promise<boolean>;
@@ -0,0 +1,213 @@
1
+ import { execa } from 'execa';
2
+ import inquirer from 'inquirer';
3
+ import output from '../../lib/output.js';
4
+ export async function createModel(modelName) {
5
+ // Step 1: Get model name if not provided
6
+ if (!modelName) {
7
+ const nameAnswer = await inquirer.prompt([
8
+ {
9
+ type: 'input',
10
+ name: 'modelName',
11
+ message: 'model name:',
12
+ default: 'default',
13
+ validate: (input) => {
14
+ if (!input)
15
+ return 'model name is required';
16
+ // Kubernetes name validation
17
+ if (!/^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/.test(input)) {
18
+ return 'model name must be a valid Kubernetes resource name';
19
+ }
20
+ return true;
21
+ },
22
+ },
23
+ ]);
24
+ modelName = nameAnswer.modelName;
25
+ }
26
+ // Check if model already exists
27
+ try {
28
+ await execa('kubectl', ['get', 'model', modelName], { stdio: 'pipe' });
29
+ output.warning(`model ${modelName} already exists`);
30
+ const { overwrite } = await inquirer.prompt([
31
+ {
32
+ type: 'confirm',
33
+ name: 'overwrite',
34
+ message: `overwrite existing model ${modelName}?`,
35
+ default: false,
36
+ },
37
+ ]);
38
+ if (!overwrite) {
39
+ output.info('model creation cancelled');
40
+ return false;
41
+ }
42
+ }
43
+ catch {
44
+ // Model doesn't exist, continue
45
+ }
46
+ // Step 2: Choose model type
47
+ const { modelType } = await inquirer.prompt([
48
+ {
49
+ type: 'list',
50
+ name: 'modelType',
51
+ message: 'select model provider:',
52
+ choices: [
53
+ { name: 'Azure OpenAI', value: 'azure' },
54
+ { name: 'OpenAI', value: 'openai' },
55
+ ],
56
+ default: 'azure',
57
+ },
58
+ ]);
59
+ // Step 3: Get common parameters
60
+ const commonAnswers = await inquirer.prompt([
61
+ {
62
+ type: 'input',
63
+ name: 'modelVersion',
64
+ message: 'model version:',
65
+ default: 'gpt-4o-mini',
66
+ },
67
+ {
68
+ type: 'input',
69
+ name: 'baseUrl',
70
+ message: 'base URL:',
71
+ validate: (input) => {
72
+ if (!input)
73
+ return 'base URL is required';
74
+ try {
75
+ new URL(input);
76
+ return true;
77
+ }
78
+ catch {
79
+ return 'please enter a valid URL';
80
+ }
81
+ },
82
+ },
83
+ ]);
84
+ // Remove trailing slash from base URL
85
+ const baseUrl = commonAnswers.baseUrl.replace(/\/$/, '');
86
+ // Step 4: Get provider-specific parameters
87
+ let apiVersion = '';
88
+ if (modelType === 'azure') {
89
+ const azureAnswers = await inquirer.prompt([
90
+ {
91
+ type: 'input',
92
+ name: 'apiVersion',
93
+ message: 'Azure API version:',
94
+ default: '2024-12-01-preview',
95
+ },
96
+ ]);
97
+ apiVersion = azureAnswers.apiVersion;
98
+ }
99
+ // Step 5: Get API key (password input)
100
+ const { apiKey } = await inquirer.prompt([
101
+ {
102
+ type: 'password',
103
+ name: 'apiKey',
104
+ message: 'API key:',
105
+ mask: '*',
106
+ validate: (input) => {
107
+ if (!input)
108
+ return 'API key is required';
109
+ return true;
110
+ },
111
+ },
112
+ ]);
113
+ // Step 6: Create the Kubernetes secret
114
+ const secretName = `${modelName}-model-api-key`;
115
+ output.info(`creating secret ${secretName}...`);
116
+ try {
117
+ // Delete existing secret if it exists (update scenario)
118
+ await execa('kubectl', ['delete', 'secret', secretName], {
119
+ stdio: 'pipe',
120
+ }).catch(() => {
121
+ // Ignore error if secret doesn't exist
122
+ });
123
+ // Create the secret
124
+ await execa('kubectl', [
125
+ 'create',
126
+ 'secret',
127
+ 'generic',
128
+ secretName,
129
+ `--from-literal=api-key=${apiKey}`,
130
+ ], { stdio: 'pipe' });
131
+ output.success(`secret ${secretName} created`);
132
+ }
133
+ catch (error) {
134
+ output.error('failed to create secret');
135
+ console.error(error);
136
+ return false;
137
+ }
138
+ // Step 7: Create the Model resource
139
+ output.info(`creating model ${modelName}...`);
140
+ const modelManifest = {
141
+ apiVersion: 'ark.mckinsey.com/v1alpha1',
142
+ kind: 'Model',
143
+ metadata: {
144
+ name: modelName,
145
+ },
146
+ spec: {
147
+ type: modelType,
148
+ model: {
149
+ value: commonAnswers.modelVersion,
150
+ },
151
+ config: {},
152
+ },
153
+ };
154
+ // Add provider-specific config
155
+ if (modelType === 'azure') {
156
+ modelManifest.spec.config.azure = {
157
+ apiKey: {
158
+ valueFrom: {
159
+ secretKeyRef: {
160
+ name: secretName,
161
+ key: 'api-key',
162
+ },
163
+ },
164
+ },
165
+ baseUrl: {
166
+ value: baseUrl,
167
+ },
168
+ apiVersion: {
169
+ value: apiVersion,
170
+ },
171
+ };
172
+ }
173
+ else {
174
+ modelManifest.spec.config.openai = {
175
+ apiKey: {
176
+ valueFrom: {
177
+ secretKeyRef: {
178
+ name: secretName,
179
+ key: 'api-key',
180
+ },
181
+ },
182
+ },
183
+ baseUrl: {
184
+ value: baseUrl,
185
+ },
186
+ };
187
+ }
188
+ try {
189
+ // Apply the model manifest using kubectl
190
+ const manifestJson = JSON.stringify(modelManifest);
191
+ await execa('kubectl', ['apply', '-f', '-'], {
192
+ input: manifestJson,
193
+ stdio: ['pipe', 'pipe', 'pipe'],
194
+ });
195
+ output.success(`model ${modelName} created successfully`);
196
+ console.log();
197
+ output.info('you can now use this model with ARK agents and queries');
198
+ return true;
199
+ }
200
+ catch (error) {
201
+ output.error('failed to create model');
202
+ console.error(error);
203
+ // Try to clean up the secret if model creation failed
204
+ try {
205
+ await execa('kubectl', ['delete', 'secret', secretName], { stdio: 'pipe' });
206
+ output.info(`cleaned up secret ${secretName}`);
207
+ }
208
+ catch {
209
+ // Ignore cleanup errors
210
+ }
211
+ return false;
212
+ }
213
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createModelsCommand(): Command;
@@ -0,0 +1,65 @@
1
+ import { Command } from 'commander';
2
+ import { execa } from 'execa';
3
+ import output from '../../lib/output.js';
4
+ import { createModel } from './create.js';
5
+ async function listModels(options) {
6
+ try {
7
+ // Use kubectl to get models
8
+ const result = await execa('kubectl', ['get', 'models', '-o', 'json'], {
9
+ stdio: 'pipe',
10
+ });
11
+ const data = JSON.parse(result.stdout);
12
+ const models = data.items || [];
13
+ if (options.output === 'json') {
14
+ // Output the raw items for JSON format
15
+ console.log(JSON.stringify(models, null, 2));
16
+ }
17
+ else {
18
+ if (models.length === 0) {
19
+ output.info('No models found');
20
+ return;
21
+ }
22
+ // Just output the model names
23
+ models.forEach((model) => {
24
+ console.log(model.metadata.name);
25
+ });
26
+ }
27
+ }
28
+ catch (error) {
29
+ if (error instanceof Error && error.message.includes('the server doesn\'t have a resource type')) {
30
+ output.error('Model CRDs not installed. Is the ARK controller running?');
31
+ }
32
+ else {
33
+ output.error(`Failed to list models: ${error instanceof Error ? error.message : 'Unknown error'}`);
34
+ }
35
+ process.exit(1);
36
+ }
37
+ }
38
+ export function createModelsCommand() {
39
+ const modelsCommand = new Command('models');
40
+ modelsCommand
41
+ .description('List available models')
42
+ .option('-o, --output <format>', 'Output format (json)', 'text')
43
+ .action(async (options) => {
44
+ await listModels(options);
45
+ });
46
+ const listCommand = new Command('list');
47
+ listCommand
48
+ .alias('ls')
49
+ .description('List available models')
50
+ .option('-o, --output <format>', 'Output format (json)', 'text')
51
+ .action(async (options) => {
52
+ await listModels(options);
53
+ });
54
+ modelsCommand.addCommand(listCommand);
55
+ // Add create command
56
+ const createCommand = new Command('create');
57
+ createCommand
58
+ .description('Create a new model')
59
+ .argument('[name]', 'Model name (optional)')
60
+ .action(async (name) => {
61
+ await createModel(name);
62
+ });
63
+ modelsCommand.addCommand(createCommand);
64
+ return modelsCommand;
65
+ }
@@ -0,0 +1,8 @@
1
+ import { Model, ArkApiClient } from '../../lib/arkApiClient.js';
2
+ interface ModelSelectorProps {
3
+ arkApiClient: ArkApiClient;
4
+ onSelect: (model: Model) => void;
5
+ onExit: () => void;
6
+ }
7
+ export declare function ModelSelector({ arkApiClient, onSelect, onExit, }: ModelSelectorProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect } from 'react';
3
+ import { Box, Text, useInput } from 'ink';
4
+ export function ModelSelector({ arkApiClient, onSelect, onExit, }) {
5
+ const [selectedIndex, setSelectedIndex] = useState(0);
6
+ const [models, setModels] = useState([]);
7
+ const [loading, setLoading] = useState(true);
8
+ const [error, setError] = useState(null);
9
+ useEffect(() => {
10
+ arkApiClient
11
+ .getModels()
12
+ .then((fetchedModels) => {
13
+ setModels(fetchedModels);
14
+ setLoading(false);
15
+ })
16
+ .catch((err) => {
17
+ setError(err.message || 'Failed to fetch models');
18
+ setLoading(false);
19
+ });
20
+ }, [arkApiClient]);
21
+ useInput((input, key) => {
22
+ if (key.escape) {
23
+ onExit();
24
+ }
25
+ else if (key.upArrow || input === 'k') {
26
+ setSelectedIndex((prev) => (prev === 0 ? models.length - 1 : prev - 1));
27
+ }
28
+ else if (key.downArrow || input === 'j') {
29
+ setSelectedIndex((prev) => (prev === models.length - 1 ? 0 : prev + 1));
30
+ }
31
+ else if (key.return) {
32
+ onSelect(models[selectedIndex]);
33
+ }
34
+ else {
35
+ // Handle number keys for quick selection
36
+ const num = parseInt(input, 10);
37
+ if (!isNaN(num) && num >= 1 && num <= models.length) {
38
+ onSelect(models[num - 1]);
39
+ }
40
+ }
41
+ });
42
+ if (loading) {
43
+ return (_jsx(Box, { children: _jsx(Text, { children: "Loading models..." }) }));
44
+ }
45
+ if (error) {
46
+ return (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["Error: ", error] }) }));
47
+ }
48
+ if (models.length === 0) {
49
+ return (_jsx(Box, { children: _jsx(Text, { children: "No models available" }) }));
50
+ }
51
+ const selectedModel = models[selectedIndex];
52
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 2, paddingY: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Select Model" }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { dimColor: true, children: "Choose a model to start a conversation with" }) }), _jsx(Box, { flexDirection: "column", children: models.map((model, index) => (_jsx(Box, { marginBottom: 0, children: _jsxs(Text, { color: index === selectedIndex ? 'green' : undefined, children: [index === selectedIndex ? '❯ ' : ' ', index + 1, ". ", model.name, model.type ? ` (${model.type})` : ''] }) }, model.name))) }), selectedModel && selectedModel.model && (_jsx(Box, { marginTop: 1, paddingLeft: 2, children: _jsxs(Text, { dimColor: true, wrap: "wrap", children: ["Model: ", selectedModel.model] }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter to confirm \u00B7 Number to select \u00B7 Esc to exit" }) })] }));
53
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createRoutesCommand(): Command;