@agents-at-scale/ark 0.1.36-rc1 → 0.1.37

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/README.md +53 -70
  2. package/dist/arkServices.d.ts +3 -27
  3. package/dist/arkServices.js +31 -3
  4. package/dist/arkServices.spec.js +118 -10
  5. package/dist/commands/completion/index.js +0 -2
  6. package/dist/commands/install/index.js +49 -58
  7. package/dist/commands/models/create.d.ts +9 -1
  8. package/dist/commands/models/create.js +97 -90
  9. package/dist/commands/models/create.spec.js +9 -37
  10. package/dist/commands/models/index.js +8 -2
  11. package/dist/commands/models/index.spec.js +1 -1
  12. package/dist/commands/status/index.d.ts +3 -1
  13. package/dist/commands/status/index.js +54 -2
  14. package/dist/components/ChatUI.js +19 -1
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.js +1 -1
  17. package/dist/lib/arkApiClient.d.ts +1 -2
  18. package/dist/lib/arkApiClient.js +5 -6
  19. package/dist/lib/config.d.ts +4 -0
  20. package/dist/lib/config.js +9 -0
  21. package/dist/lib/nextSteps.js +1 -1
  22. package/dist/lib/nextSteps.spec.js +1 -1
  23. package/dist/lib/security.js +4 -0
  24. package/dist/lib/startup.js +6 -2
  25. package/dist/lib/startup.spec.js +1 -1
  26. package/dist/lib/timeout.d.ts +1 -0
  27. package/dist/lib/timeout.js +20 -0
  28. package/dist/lib/timeout.spec.js +14 -0
  29. package/dist/lib/waitForReady.d.ts +8 -0
  30. package/dist/lib/waitForReady.js +32 -0
  31. package/dist/lib/waitForReady.spec.js +104 -0
  32. package/dist/types/arkService.d.ts +27 -0
  33. package/package.json +3 -3
  34. package/dist/charts/charts.d.ts +0 -5
  35. package/dist/charts/charts.js +0 -6
  36. package/dist/charts/dependencies.d.ts +0 -6
  37. package/dist/charts/dependencies.js +0 -50
  38. package/dist/charts/types.d.ts +0 -40
  39. package/dist/commands/agents/selector.d.ts +0 -8
  40. package/dist/commands/agents/selector.js +0 -53
  41. package/dist/commands/agents.d.ts +0 -2
  42. package/dist/commands/agents.js +0 -53
  43. package/dist/commands/chat.d.ts +0 -2
  44. package/dist/commands/chat.js +0 -45
  45. package/dist/commands/cluster/get-ip.d.ts +0 -2
  46. package/dist/commands/cluster/get-ip.js +0 -32
  47. package/dist/commands/cluster/get-type.d.ts +0 -2
  48. package/dist/commands/cluster/get-type.js +0 -26
  49. package/dist/commands/completion.d.ts +0 -2
  50. package/dist/commands/completion.js +0 -265
  51. package/dist/commands/config.d.ts +0 -2
  52. package/dist/commands/config.js +0 -44
  53. package/dist/commands/dashboard.d.ts +0 -3
  54. package/dist/commands/dashboard.js +0 -39
  55. package/dist/commands/dev/index.d.ts +0 -3
  56. package/dist/commands/dev/index.js +0 -9
  57. package/dist/commands/dev/tool/check.d.ts +0 -2
  58. package/dist/commands/dev/tool/check.js +0 -142
  59. package/dist/commands/dev/tool/clean.d.ts +0 -2
  60. package/dist/commands/dev/tool/clean.js +0 -153
  61. package/dist/commands/dev/tool/generate.d.ts +0 -2
  62. package/dist/commands/dev/tool/generate.js +0 -28
  63. package/dist/commands/dev/tool/index.d.ts +0 -2
  64. package/dist/commands/dev/tool/index.js +0 -14
  65. package/dist/commands/dev/tool/init.d.ts +0 -2
  66. package/dist/commands/dev/tool/init.js +0 -320
  67. package/dist/commands/dev/tool/shared.d.ts +0 -5
  68. package/dist/commands/dev/tool/shared.js +0 -258
  69. package/dist/commands/dev/tool/status.d.ts +0 -2
  70. package/dist/commands/dev/tool/status.js +0 -136
  71. package/dist/commands/dev/tool-generate.spec.js +0 -163
  72. package/dist/commands/dev/tool.d.ts +0 -2
  73. package/dist/commands/dev/tool.js +0 -559
  74. package/dist/commands/dev/tool.spec.js +0 -48
  75. package/dist/commands/install.d.ts +0 -3
  76. package/dist/commands/install.js +0 -147
  77. package/dist/commands/models/selector.d.ts +0 -8
  78. package/dist/commands/models/selector.js +0 -53
  79. package/dist/commands/routes.d.ts +0 -2
  80. package/dist/commands/routes.js +0 -101
  81. package/dist/commands/status.d.ts +0 -3
  82. package/dist/commands/status.js +0 -33
  83. package/dist/commands/targets.d.ts +0 -2
  84. package/dist/commands/targets.js +0 -65
  85. package/dist/commands/teams/selector.d.ts +0 -8
  86. package/dist/commands/teams/selector.js +0 -55
  87. package/dist/commands/tools/selector.d.ts +0 -8
  88. package/dist/commands/tools/selector.js +0 -53
  89. package/dist/commands/uninstall.d.ts +0 -2
  90. package/dist/commands/uninstall.js +0 -83
  91. package/dist/components/DashboardCLI.d.ts +0 -3
  92. package/dist/components/DashboardCLI.js +0 -149
  93. package/dist/components/StatusView.d.ts +0 -10
  94. package/dist/components/StatusView.js +0 -39
  95. package/dist/config.d.ts +0 -23
  96. package/dist/config.js +0 -92
  97. package/dist/lib/arkClient.d.ts +0 -32
  98. package/dist/lib/arkClient.js +0 -43
  99. package/dist/lib/commandUtils.d.ts +0 -4
  100. package/dist/lib/commandUtils.js +0 -18
  101. package/dist/lib/commandUtils.test.d.ts +0 -1
  102. package/dist/lib/commandUtils.test.js +0 -44
  103. package/dist/lib/config.test.d.ts +0 -1
  104. package/dist/lib/config.test.js +0 -93
  105. package/dist/lib/consts.d.ts +0 -9
  106. package/dist/lib/consts.js +0 -13
  107. package/dist/lib/consts.spec.d.ts +0 -1
  108. package/dist/lib/consts.spec.js +0 -15
  109. package/dist/lib/dev/tools/analyzer.d.ts +0 -30
  110. package/dist/lib/dev/tools/analyzer.js +0 -190
  111. package/dist/lib/dev/tools/discover_tools.py +0 -392
  112. package/dist/lib/dev/tools/mcp-types.d.ts +0 -28
  113. package/dist/lib/dev/tools/mcp-types.js +0 -86
  114. package/dist/lib/dev/tools/types.d.ts +0 -50
  115. package/dist/lib/dev/tools/types.js +0 -1
  116. package/dist/lib/exec.d.ts +0 -1
  117. package/dist/lib/exec.js +0 -9
  118. package/dist/lib/gatewayManager.d.ts +0 -24
  119. package/dist/lib/gatewayManager.js +0 -85
  120. package/dist/lib/kubernetes.d.ts +0 -28
  121. package/dist/lib/kubernetes.js +0 -122
  122. package/dist/lib/portUtils.d.ts +0 -8
  123. package/dist/lib/portUtils.js +0 -39
  124. package/dist/lib/progress.d.ts +0 -128
  125. package/dist/lib/progress.js +0 -273
  126. package/dist/lib/queryRunner.d.ts +0 -22
  127. package/dist/lib/queryRunner.js +0 -142
  128. package/dist/lib/wrappers/git.d.ts +0 -2
  129. package/dist/lib/wrappers/git.js +0 -43
  130. /package/dist/{charts/types.js → lib/timeout.spec.d.ts} +0 -0
  131. /package/dist/{commands/dev/tool-generate.spec.d.ts → lib/waitForReady.spec.d.ts} +0 -0
  132. /package/dist/{commands/dev/tool.spec.d.ts → types/arkService.js} +0 -0
package/README.md CHANGED
@@ -1,95 +1,78 @@
1
- # ARK CLI
1
+ <div align="center">
2
+ <h1 align="center"><code>ark</code></h1>
3
+ <h4 align="center">Ark Command Line Interface</h4>
2
4
 
3
- Interactive terminal interface for ARK agents.
5
+ <hr>
4
6
 
5
- ## Prerequisites
7
+ <p align="center">
8
+ <a href="#quickstart">Quickstart</a> •
9
+ <a href="https://mckinsey.github.io/agents-at-scale-ark/">Documentation</a>
10
+ </p>
11
+ <p align="center">
12
+ <a href="https://github.com/mckinsey/agents-at-scale-ark/actions/workflows/cicd.yaml"><img src="https://github.com/mckinsey/agents-at-scale-ark/actions/workflows/cicd.yaml/badge.svg" alt="CI/CD"></a>
13
+ <a href="https://www.npmjs.com/package/@agents-at-scale/ark"><img src="https://img.shields.io/npm/v/@agents-at-scale/ark.svg" alt="npm version"></a>
14
+ </p>
15
+ </div>
6
16
 
7
- 1. **ARK system deployed** in your Kubernetes cluster
8
- 2. **Gateway setup** for service discovery:
9
- ```bash
10
- # From agents-at-scale project root
11
- make localhost-gateway-install
12
- ```
17
+ ## Quickstart
13
18
 
14
- ## Installation
19
+ Ensure you have [Node.js](https://nodejs.org/en/download) and [Helm](https://helm.sh/docs/intro/install/) installed. Then run the following commands to install Ark:
15
20
 
16
21
  ```bash
17
- cd tools/ark-cli
18
- npm run build
19
- npm install -g .
20
- ```
21
-
22
- ## Usage
23
-
24
- ```bash
25
- # Interactive mode (no arguments)
26
- ark
22
+ # Install the 'ark' CLI:
23
+ npm install -g @agents-at-scale/ark
27
24
 
28
- # Show help
29
- ark --help
30
- ark cluster --help
25
+ # Install Ark:
26
+ ark install
31
27
 
32
- # Check system status
33
- ark check status
28
+ # Optionally configure a 'default' model to use for agents:
29
+ ark models create default
34
30
 
35
- # Generate (project, agent, team, etc)
36
- ark generate
31
+ # Run the dashboard:
32
+ ark dashboard
33
+ ```
37
34
 
38
- # Cluster operations
39
- ark cluster get-type
40
- ark cluster get-ip
41
- ark cluster get-ip --verbose
42
- ark cluster get-ip --context minikube
35
+ In most cases the default installation options will be sufficient. This will install the Ark dependencies, the controller, the APIs and the dashboard. You can optionally setup a `default` model that will be the default used by agents. You will need a Kubernetes cluster to install Ark into, you can use [Minikube](https://minikube.sigs.k8s.io/docs/start), [Kind](https://kind.sigs.k8s.io/docs/user/quick-start/), [Docker Desktop](https://docs.docker.com/desktop/kubernetes/) or similar to run a local cluster. The `install` command will warn if any required dependencies are missing.
43
36
 
44
- # Show shell autocomplete options.
45
- ark autocomplete
46
- ```
37
+ User guides, developer guides, operations guides and API reference documentation is all available at:
47
38
 
48
- ## Configuration
39
+ https://mckinsey.github.io/agents-at-scale-ark/
49
40
 
50
- ARK CLI automatically detects services via:
41
+ The [Quickstart](https://mckinsey.github.io/agents-at-scale-ark/quickstart/) guide will walk you through the process of configuring a model, creating an agent and running basic queries.
51
42
 
52
- 1. **localhost-gateway** (when running) - `*.127.0.0.1.nip.io:8080`
53
- 2. **Kubernetes service discovery** - for internal services
54
- 3. **Default localhost URLs** - fallback
43
+ To troubleshoot an installation, run `ark status`.
55
44
 
56
- Settings stored in `~/.config/ark-cli/config.json`
45
+ ## Configuration
57
46
 
58
- ## Development
47
+ You can customize Ark service installations using a `.arkrc.yaml` file in your home directory (`~/.arkrc.yaml`) or project directory. This allows you to override service properties like enabled status, namespace, or chart location.
59
48
 
60
- **Note:** All make commands must be run from the repository root directory.
49
+ Example `.arkrc.yaml`:
61
50
 
62
- ```bash
63
- # From repository root
64
- make ark-cli-install # Build and install globally
65
- make ark-cli-build # Build only
66
- make clean # Clean build artifacts and stamp files
67
- make ark-cli-uninstall # Remove global installation
68
-
69
- # From tools/ark-cli directory
70
- npm run lint # Run linting and formatting
71
- npm run lint:check # Check linting without fixing
51
+ ```yaml
52
+ services:
53
+ localhost-gateway:
54
+ enabled: true
55
+ ark-controller:
56
+ namespace: custom-namespace
72
57
  ```
73
58
 
74
- ## Troubleshooting
59
+ This example enables the `localhost-gateway` service (disabled by default) and changes the namespace for `ark-controller`.
75
60
 
76
- Enable debug logging to see detailed configuration discovery and service resolution:
61
+ ### Installing Agents @ Scale
77
62
 
78
- ```bash
79
- # Debug configuration discovery and service resolution
80
- DEBUG=ark:config ark check status
63
+ To install the Agents @ Scale platform with JFrog container registry credentials:
81
64
 
82
- # Debug all ARK CLI components
83
- DEBUG=ark:* ark check status
84
-
85
- # Keep debug enabled for multiple commands
86
- export DEBUG=ark:config
87
- ark check status
88
- ark dashboard
65
+ ```yaml
66
+ services:
67
+ agents-at-scale:
68
+ enabled: true
69
+ installArgs:
70
+ - --set
71
+ - containerRegistry.enabled=true
72
+ - --set
73
+ - containerRegistry.username=YOUR_USERNAME
74
+ - --set
75
+ - containerRegistry.password=YOUR_PASSWORD
89
76
  ```
90
77
 
91
- **Common debug scenarios:**
92
-
93
- - **Wrong URL detected**: See which discovery method is being used
94
- - **Service timeouts**: Check localhost-gateway and kubernetes connectivity
95
- - **Config issues**: Trace fallback logic through multiple discovery methods
78
+ Replace `YOUR_USERNAME` and `YOUR_PASSWORD` with your JFrog credentials.
@@ -1,39 +1,15 @@
1
1
  /**
2
2
  * Centralized ARK service definitions used by both install and status commands
3
3
  */
4
- export interface ArkService {
5
- name: string;
6
- helmReleaseName: string;
7
- description: string;
8
- enabled: boolean;
9
- namespace?: string;
10
- chartPath?: string;
11
- installArgs?: string[];
12
- k8sServiceName?: string;
13
- k8sServicePort?: number;
14
- k8sPortForwardLocalPort?: number;
15
- k8sDeploymentName?: string;
16
- k8sDevDeploymentName?: string;
17
- }
18
- export interface ServiceCollection {
19
- [key: string]: ArkService;
20
- }
21
- export interface ArkDependency {
22
- name: string;
23
- command: string;
24
- args: string[];
25
- description: string;
26
- }
27
- export interface DependencyCollection {
28
- [key: string]: ArkDependency;
29
- }
4
+ import type { ArkService, ServiceCollection, ArkDependency, DependencyCollection } from './types/arkService.js';
5
+ export type { ArkService, ServiceCollection, ArkDependency, DependencyCollection, };
30
6
  /**
31
7
  * Dependencies that should be installed before ARK services
32
8
  * Note: Dependencies will be installed in the order they are defined here
33
9
  */
34
10
  export declare const arkDependencies: DependencyCollection;
35
11
  /**
36
- * Core ARK services with their installation and status check configurations
12
+ * Core ARK services - initialized with defaults and config overrides applied
37
13
  */
38
14
  export declare const arkServices: ServiceCollection;
39
15
  /**
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Centralized ARK service definitions used by both install and status commands
3
3
  */
4
+ import { loadConfig } from './lib/config.js';
4
5
  const REGISTRY_BASE = 'oci://ghcr.io/mckinsey/agents-at-scale-ark/charts';
5
6
  /**
6
7
  * Dependencies that should be installed before ARK services
@@ -53,14 +54,15 @@ export const arkDependencies = {
53
54
  },
54
55
  };
55
56
  /**
56
- * Core ARK services with their installation and status check configurations
57
+ * Default ARK services with their installation and status check configurations
57
58
  */
58
- export const arkServices = {
59
+ const defaultArkServices = {
59
60
  'ark-controller': {
60
61
  name: 'ark-controller',
61
62
  helmReleaseName: 'ark-controller',
62
63
  description: 'Core Ark controller for managing AI resources',
63
64
  enabled: true,
65
+ category: 'core',
64
66
  namespace: 'ark-system',
65
67
  chartPath: `${REGISTRY_BASE}/ark-controller`,
66
68
  installArgs: ['--create-namespace', '--set', 'rbac.enable=true'],
@@ -72,7 +74,7 @@ export const arkServices = {
72
74
  helmReleaseName: 'ark-api',
73
75
  description: 'API layer for interacting with Ark resources',
74
76
  enabled: true,
75
- // namespace: undefined - uses current context namespace
77
+ category: 'service',
76
78
  chartPath: `${REGISTRY_BASE}/ark-api`,
77
79
  installArgs: [],
78
80
  k8sServiceName: 'ark-api',
@@ -86,6 +88,7 @@ export const arkServices = {
86
88
  helmReleaseName: 'ark-dashboard',
87
89
  description: 'Ark Dashboard',
88
90
  enabled: true,
91
+ category: 'service',
89
92
  // namespace: undefined - uses current context namespace
90
93
  chartPath: `${REGISTRY_BASE}/ark-dashboard`,
91
94
  installArgs: [],
@@ -100,6 +103,7 @@ export const arkServices = {
100
103
  helmReleaseName: 'ark-api-a2a',
101
104
  description: 'Ark API agent-to-agent communication service',
102
105
  enabled: false, // Disabled - not currently used
106
+ category: 'service',
103
107
  // namespace: undefined - uses current context namespace
104
108
  // Note: This service might be installed as part of ark-api or separately
105
109
  },
@@ -108,22 +112,46 @@ export const arkServices = {
108
112
  helmReleaseName: 'ark-mcp',
109
113
  description: 'Ark Model Context Protocol server',
110
114
  enabled: true,
115
+ category: 'service',
111
116
  // namespace: undefined - uses current context namespace
112
117
  chartPath: `${REGISTRY_BASE}/ark-mcp`,
113
118
  installArgs: [],
114
119
  k8sDeploymentName: 'ark-mcp',
115
120
  k8sDevDeploymentName: 'ark-mcp-devspace',
116
121
  },
122
+ 'agents-at-scale': {
123
+ name: 'agents-at-scale',
124
+ helmReleaseName: 'agents-at-scale',
125
+ description: 'Agents @ Scale Platform',
126
+ enabled: false,
127
+ category: 'service',
128
+ chartPath: 'oci://ghcr.io/mck-private/qb-fm-labs-legacyx/charts/legacyx',
129
+ installArgs: [],
130
+ },
117
131
  'localhost-gateway': {
118
132
  name: 'localhost-gateway',
119
133
  helmReleaseName: 'localhost-gateway',
120
134
  description: 'Gateway for local development clusters',
121
135
  enabled: false, // Disabled - not needed for most users
136
+ category: 'service',
122
137
  namespace: 'ark-system',
123
138
  chartPath: `${REGISTRY_BASE}/localhost-gateway`,
124
139
  installArgs: [],
125
140
  },
126
141
  };
142
+ function applyConfigOverrides(defaults) {
143
+ const config = loadConfig();
144
+ const overrides = config?.services || {};
145
+ const result = {};
146
+ for (const [key, service] of Object.entries(defaults)) {
147
+ result[key] = overrides[key] ? { ...service, ...overrides[key] } : service;
148
+ }
149
+ return result;
150
+ }
151
+ /**
152
+ * Core ARK services - initialized with defaults and config overrides applied
153
+ */
154
+ export const arkServices = applyConfigOverrides(defaultArkServices);
127
155
  /**
128
156
  * Get services that can be installed via Helm charts (only enabled services)
129
157
  */
@@ -1,24 +1,132 @@
1
- import { arkDependencies, arkServices, getInstallableServices, } from './arkServices.js';
1
+ import { jest } from '@jest/globals';
2
+ const mockLoadConfig = jest.fn();
3
+ jest.unstable_mockModule('./lib/config.js', () => ({
4
+ loadConfig: mockLoadConfig,
5
+ }));
6
+ const { arkDependencies, arkServices: originalArkServices, getInstallableServices, } = await import('./arkServices.js');
2
7
  describe('arkServices', () => {
8
+ beforeEach(() => {
9
+ jest.clearAllMocks();
10
+ });
3
11
  it('exports arkDependencies with expected structure', () => {
4
12
  expect(arkDependencies).toBeDefined();
5
13
  expect(arkDependencies['cert-manager']).toBeDefined();
6
14
  expect(arkDependencies['cert-manager'].command).toBe('helm');
7
15
  });
8
16
  it('exports arkServices with expected structure', () => {
9
- expect(arkServices).toBeDefined();
10
- expect(arkServices['ark-controller']).toBeDefined();
11
- expect(arkServices['ark-controller'].namespace).toBe('ark-system');
12
- // User services should have undefined namespace (use current context)
13
- expect(arkServices['ark-api'].namespace).toBeUndefined();
14
- expect(arkServices['ark-dashboard'].namespace).toBeUndefined();
15
- // System services should have explicit namespace
16
- expect(arkServices['localhost-gateway'].namespace).toBe('ark-system');
17
+ expect(originalArkServices).toBeDefined();
18
+ expect(originalArkServices['ark-controller']).toBeDefined();
19
+ expect(originalArkServices['ark-controller'].namespace).toBe('ark-system');
20
+ expect(originalArkServices['ark-api'].namespace).toBeUndefined();
21
+ expect(originalArkServices['ark-dashboard'].namespace).toBeUndefined();
22
+ expect(originalArkServices['localhost-gateway'].namespace).toBe('ark-system');
17
23
  });
18
24
  it('getInstallableServices returns services with chartPath', () => {
19
25
  const installable = getInstallableServices();
20
26
  expect(installable['ark-controller']).toBeDefined();
21
27
  expect(installable['ark-api']).toBeDefined();
22
- expect(installable['ark-api-a2a']).toBeUndefined(); // no chartPath
28
+ expect(installable['ark-api-a2a']).toBeUndefined();
29
+ });
30
+ describe('applyConfigOverrides', () => {
31
+ it('applies no overrides when config has no services section', async () => {
32
+ mockLoadConfig.mockReturnValue({});
33
+ jest.resetModules();
34
+ const { arkServices } = await import('./arkServices.js');
35
+ expect(arkServices['ark-controller'].enabled).toBe(true);
36
+ expect(arkServices['ark-api'].enabled).toBe(true);
37
+ });
38
+ it('applies overrides to enable a disabled service', async () => {
39
+ mockLoadConfig.mockReturnValue({
40
+ services: {
41
+ 'localhost-gateway': { enabled: true },
42
+ },
43
+ });
44
+ jest.resetModules();
45
+ const { arkServices } = await import('./arkServices.js');
46
+ expect(arkServices['localhost-gateway'].enabled).toBe(true);
47
+ });
48
+ it('applies overrides to disable an enabled service', async () => {
49
+ mockLoadConfig.mockReturnValue({
50
+ services: {
51
+ 'ark-controller': { enabled: false },
52
+ },
53
+ });
54
+ jest.resetModules();
55
+ const { arkServices } = await import('./arkServices.js');
56
+ expect(arkServices['ark-controller'].enabled).toBe(false);
57
+ });
58
+ it('applies overrides to multiple services', async () => {
59
+ mockLoadConfig.mockReturnValue({
60
+ services: {
61
+ 'ark-controller': { enabled: false },
62
+ 'ark-api': { enabled: false },
63
+ 'localhost-gateway': { enabled: true },
64
+ },
65
+ });
66
+ jest.resetModules();
67
+ const { arkServices } = await import('./arkServices.js');
68
+ expect(arkServices['ark-controller'].enabled).toBe(false);
69
+ expect(arkServices['ark-api'].enabled).toBe(false);
70
+ expect(arkServices['localhost-gateway'].enabled).toBe(true);
71
+ });
72
+ it('applies overrides to namespace', async () => {
73
+ mockLoadConfig.mockReturnValue({
74
+ services: {
75
+ 'ark-api': { namespace: 'custom-namespace' },
76
+ },
77
+ });
78
+ jest.resetModules();
79
+ const { arkServices } = await import('./arkServices.js');
80
+ expect(arkServices['ark-api'].namespace).toBe('custom-namespace');
81
+ });
82
+ it('applies overrides to chartPath', async () => {
83
+ mockLoadConfig.mockReturnValue({
84
+ services: {
85
+ 'ark-controller': {
86
+ chartPath: 'oci://custom-registry/charts/ark-controller',
87
+ },
88
+ },
89
+ });
90
+ jest.resetModules();
91
+ const { arkServices } = await import('./arkServices.js');
92
+ expect(arkServices['ark-controller'].chartPath).toBe('oci://custom-registry/charts/ark-controller');
93
+ });
94
+ it('applies partial overrides without affecting other properties', async () => {
95
+ mockLoadConfig.mockReturnValue({
96
+ services: {
97
+ 'ark-controller': { enabled: false },
98
+ },
99
+ });
100
+ jest.resetModules();
101
+ const { arkServices } = await import('./arkServices.js');
102
+ expect(arkServices['ark-controller'].enabled).toBe(false);
103
+ expect(arkServices['ark-controller'].namespace).toBe('ark-system');
104
+ expect(arkServices['ark-controller'].category).toBe('core');
105
+ expect(arkServices['ark-controller'].description).toBe('Core Ark controller for managing AI resources');
106
+ });
107
+ it('applies overrides to installArgs', async () => {
108
+ mockLoadConfig.mockReturnValue({
109
+ services: {
110
+ 'ark-controller': { installArgs: ['--set', 'custom.value=true'] },
111
+ },
112
+ });
113
+ jest.resetModules();
114
+ const { arkServices } = await import('./arkServices.js');
115
+ expect(arkServices['ark-controller'].installArgs).toEqual([
116
+ '--set',
117
+ 'custom.value=true',
118
+ ]);
119
+ });
120
+ it('does not affect services without overrides', async () => {
121
+ mockLoadConfig.mockReturnValue({
122
+ services: {
123
+ 'ark-controller': { enabled: false },
124
+ },
125
+ });
126
+ jest.resetModules();
127
+ const { arkServices } = await import('./arkServices.js');
128
+ expect(arkServices['ark-api'].enabled).toBe(true);
129
+ expect(arkServices['ark-dashboard'].enabled).toBe(true);
130
+ });
23
131
  });
24
132
  });
@@ -114,7 +114,6 @@ complete -F _ark_completion ark
114
114
  .description('Generate zsh completion script')
115
115
  .action(() => {
116
116
  // Shell script requires escaped $ characters
117
- /* eslint-disable no-useless-escape */
118
117
  console.log(`
119
118
  #compdef ark
120
119
 
@@ -224,7 +223,6 @@ _ark() {
224
223
 
225
224
  _ark
226
225
  `.trim());
227
- /* eslint-enable no-useless-escape */
228
226
  });
229
227
  return completion;
230
228
  }
@@ -4,10 +4,11 @@ import { execute } from '../../lib/commands.js';
4
4
  import inquirer from 'inquirer';
5
5
  import { showNoClusterError } from '../../lib/startup.js';
6
6
  import output from '../../lib/output.js';
7
- import { getInstallableServices, arkDependencies, } from '../../arkServices.js';
8
- import { isArkReady } from '../../lib/arkStatus.js';
7
+ import { getInstallableServices, arkDependencies, arkServices, } from '../../arkServices.js';
9
8
  import { printNextSteps } from '../../lib/nextSteps.js';
10
9
  import ora from 'ora';
10
+ import { waitForServicesReady, } from '../../lib/waitForReady.js';
11
+ import { parseTimeoutToSeconds } from '../../lib/timeout.js';
11
12
  async function installService(service, verbose = false) {
12
13
  const helmArgs = [
13
14
  'upgrade',
@@ -67,6 +68,12 @@ export async function installArk(config, serviceName, options = {}) {
67
68
  console.log(chalk.cyan.bold('\nSelect components to install:'));
68
69
  console.log(chalk.gray('Use arrow keys to navigate, space to toggle, enter to confirm\n'));
69
70
  // Build choices for the checkbox prompt
71
+ const coreServices = Object.values(arkServices)
72
+ .filter((s) => s.category === 'core')
73
+ .sort((a, b) => a.name.localeCompare(b.name));
74
+ const otherServices = Object.values(arkServices)
75
+ .filter((s) => s.category === 'service')
76
+ .sort((a, b) => a.name.localeCompare(b.name));
70
77
  const allChoices = [
71
78
  new inquirer.Separator(chalk.bold('──── Dependencies ────')),
72
79
  {
@@ -80,32 +87,17 @@ export async function installArk(config, serviceName, options = {}) {
80
87
  checked: true,
81
88
  },
82
89
  new inquirer.Separator(chalk.bold('──── Ark Core ────')),
83
- {
84
- name: `ark-controller ${chalk.gray('- Core Ark controller')}`,
85
- value: 'ark-controller',
86
- checked: true,
87
- },
90
+ ...coreServices.map((service) => ({
91
+ name: `${service.name} ${chalk.gray(`- ${service.description}`)}`,
92
+ value: service.helmReleaseName,
93
+ checked: Boolean(service.enabled),
94
+ })),
88
95
  new inquirer.Separator(chalk.bold('──── Ark Services ────')),
89
- {
90
- name: `ark-api ${chalk.gray('- API service')}`,
91
- value: 'ark-api',
92
- checked: true,
93
- },
94
- {
95
- name: `ark-dashboard ${chalk.gray('- Web dashboard')}`,
96
- value: 'ark-dashboard',
97
- checked: true,
98
- },
99
- {
100
- name: `ark-mcp ${chalk.gray('- MCP services')}`,
101
- value: 'ark-mcp',
102
- checked: true,
103
- },
104
- {
105
- name: `localhost-gateway ${chalk.gray('- Gateway for local access')}`,
106
- value: 'localhost-gateway',
107
- checked: true,
108
- },
96
+ ...otherServices.map((service) => ({
97
+ name: `${service.name} ${chalk.gray(`- ${service.description}`)}`,
98
+ value: service.helmReleaseName,
99
+ checked: Boolean(service.enabled),
100
+ })),
109
101
  ];
110
102
  let selectedComponents = [];
111
103
  try {
@@ -190,11 +182,9 @@ export async function installArk(config, serviceName, options = {}) {
190
182
  }
191
183
  }
192
184
  // Install selected services
193
- const services = getInstallableServices();
194
- for (const service of Object.values(services)) {
195
- // Check if this service was selected
196
- const serviceKey = service.helmReleaseName;
197
- if (!selectedComponents.includes(serviceKey)) {
185
+ for (const serviceName of selectedComponents) {
186
+ const service = Object.values(arkServices).find((s) => s.helmReleaseName === serviceName);
187
+ if (!service || !service.chartPath) {
198
188
  continue;
199
189
  }
200
190
  output.info(`installing ${service.name}...`);
@@ -203,8 +193,8 @@ export async function installArk(config, serviceName, options = {}) {
203
193
  console.log(); // Add blank line after command output
204
194
  }
205
195
  catch {
206
- // Continue with remaining services on error
207
196
  console.log(); // Add blank line after error output
197
+ process.exit(1);
208
198
  }
209
199
  }
210
200
  }
@@ -234,8 +224,8 @@ export async function installArk(config, serviceName, options = {}) {
234
224
  console.log(); // Add blank line after command output
235
225
  }
236
226
  catch {
237
- // Continue with remaining services on error
238
227
  console.log(); // Add blank line after error output
228
+ process.exit(1);
239
229
  }
240
230
  }
241
231
  }
@@ -245,34 +235,35 @@ export async function installArk(config, serviceName, options = {}) {
245
235
  }
246
236
  // Wait for Ark to be ready if requested
247
237
  if (options.waitForReady) {
248
- // Parse timeout value (e.g., '30s', '2m', '60')
249
- const parseTimeout = (value) => {
250
- const match = value.match(/^(\d+)([sm])?$/);
251
- if (!match) {
252
- throw new Error('Invalid timeout format. Use format like 30s or 2m');
253
- }
254
- const num = parseInt(match[1], 10);
255
- const unit = match[2] || 's';
256
- return unit === 'm' ? num * 60 : num;
257
- };
258
238
  try {
259
- const timeoutSeconds = parseTimeout(options.waitForReady);
260
- const startTime = Date.now();
261
- const endTime = startTime + timeoutSeconds * 1000;
239
+ const timeoutSeconds = parseTimeoutToSeconds(options.waitForReady);
240
+ const servicesToWait = Object.values(arkServices).filter((s) => s.enabled &&
241
+ s.category === 'core' &&
242
+ s.k8sDeploymentName &&
243
+ s.namespace);
262
244
  const spinner = ora(`Waiting for Ark to be ready (timeout: ${timeoutSeconds}s)...`).start();
263
- while (Date.now() < endTime) {
264
- if (await isArkReady()) {
265
- spinner.succeed('Ark is ready!');
266
- return;
267
- }
245
+ const statusMap = new Map();
246
+ servicesToWait.forEach((s) => statusMap.set(s.name, false));
247
+ const startTime = Date.now();
248
+ const result = await waitForServicesReady(servicesToWait, timeoutSeconds, (progress) => {
249
+ statusMap.set(progress.serviceName, progress.ready);
268
250
  const elapsed = Math.floor((Date.now() - startTime) / 1000);
269
- spinner.text = `Waiting for Ark to be ready (${elapsed}/${timeoutSeconds}s)...`;
270
- // Wait 2 seconds before checking again
271
- await new Promise((resolve) => setTimeout(resolve, 2000));
251
+ const lines = servicesToWait.map((s) => {
252
+ const ready = statusMap.get(s.name);
253
+ const icon = ready ? '✓' : '⋯';
254
+ const status = ready ? 'ready' : 'waiting...';
255
+ const color = ready ? chalk.green : chalk.yellow;
256
+ return ` ${color(icon)} ${chalk.bold(s.name)} ${chalk.blue(`(${s.namespace})`)} - ${status}`;
257
+ });
258
+ spinner.text = `Waiting for Ark to be ready (${elapsed}/${timeoutSeconds}s)...\n${lines.join('\n')}`;
259
+ });
260
+ if (result) {
261
+ spinner.succeed('Ark is ready');
262
+ }
263
+ else {
264
+ spinner.fail(`Ark did not become ready within ${timeoutSeconds} seconds`);
265
+ process.exit(1);
272
266
  }
273
- // Timeout reached
274
- spinner.fail(`Ark did not become ready within ${timeoutSeconds} seconds`);
275
- process.exit(1);
276
267
  }
277
268
  catch (error) {
278
269
  output.error(`Failed to wait for ready: ${error instanceof Error ? error.message : 'Unknown error'}`);
@@ -1 +1,9 @@
1
- export declare function createModel(modelName?: string): Promise<boolean>;
1
+ export interface CreateModelOptions {
2
+ type?: string;
3
+ model?: string;
4
+ baseUrl?: string;
5
+ apiKey?: string;
6
+ apiVersion?: string;
7
+ yes?: boolean;
8
+ }
9
+ export declare function createModel(modelName?: string, options?: CreateModelOptions): Promise<boolean>;