@agents-at-scale/ark 0.1.47 → 0.1.50

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 (52) hide show
  1. package/.arkrc.template.yaml +51 -0
  2. package/README.md +2 -0
  3. package/dist/arkServices.js +13 -8
  4. package/dist/commands/chat/index.js +1 -1
  5. package/dist/commands/completion/index.js +17 -1
  6. package/dist/commands/dashboard/index.d.ts +2 -2
  7. package/dist/commands/dashboard/index.js +5 -4
  8. package/dist/commands/export/index.d.ts +3 -0
  9. package/dist/commands/export/index.js +73 -0
  10. package/dist/commands/export/index.spec.d.ts +1 -0
  11. package/dist/commands/export/index.spec.js +145 -0
  12. package/dist/commands/import/index.d.ts +3 -0
  13. package/dist/commands/import/index.js +27 -0
  14. package/dist/commands/import/index.spec.d.ts +1 -0
  15. package/dist/commands/import/index.spec.js +46 -0
  16. package/dist/commands/install/index.js +12 -0
  17. package/dist/commands/memory/index.js +9 -4
  18. package/dist/commands/models/kubernetes/manifest-builder.js +1 -1
  19. package/dist/commands/queries/index.js +4 -4
  20. package/dist/commands/queries/index.spec.d.ts +1 -0
  21. package/dist/commands/queries/index.spec.js +167 -0
  22. package/dist/commands/query/index.js +2 -0
  23. package/dist/components/ChatUI.js +4 -4
  24. package/dist/index.js +4 -0
  25. package/dist/lib/arkApiProxy.d.ts +1 -1
  26. package/dist/lib/arkApiProxy.js +2 -2
  27. package/dist/lib/arkServiceProxy.d.ts +3 -1
  28. package/dist/lib/arkServiceProxy.js +34 -1
  29. package/dist/lib/arkServiceProxy.spec.d.ts +1 -0
  30. package/dist/lib/arkServiceProxy.spec.js +100 -0
  31. package/dist/lib/chatClient.d.ts +1 -0
  32. package/dist/lib/chatClient.js +7 -1
  33. package/dist/lib/config.d.ts +3 -1
  34. package/dist/lib/config.js +21 -7
  35. package/dist/lib/config.spec.js +10 -0
  36. package/dist/lib/executeQuery.d.ts +1 -0
  37. package/dist/lib/executeQuery.js +17 -8
  38. package/dist/lib/kubectl.d.ts +2 -0
  39. package/dist/lib/kubectl.js +6 -0
  40. package/dist/lib/kubectl.spec.js +16 -0
  41. package/dist/lib/types.d.ts +3 -2
  42. package/dist/types/arkService.d.ts +5 -0
  43. package/dist/ui/MainMenu.js +6 -2
  44. package/package.json +4 -2
  45. package/dist/ui/AgentSelector.d.ts +0 -8
  46. package/dist/ui/AgentSelector.js +0 -53
  47. package/dist/ui/ModelSelector.d.ts +0 -8
  48. package/dist/ui/ModelSelector.js +0 -53
  49. package/dist/ui/TeamSelector.d.ts +0 -8
  50. package/dist/ui/TeamSelector.js +0 -55
  51. package/dist/ui/ToolSelector.d.ts +0 -8
  52. package/dist/ui/ToolSelector.js +0 -53
@@ -0,0 +1,51 @@
1
+ # Ark CLI Configuration Template
2
+ # Copy this to ~/.arkrc.yaml (user config) or .arkrc.yaml (project config)
3
+ # All fields are optional - only include what you want to override
4
+
5
+ # Chat configuration
6
+ chat:
7
+ # Enable/disable streaming for chat responses (default: true)
8
+ # streaming: true
9
+
10
+ # Output format: 'text' or 'markdown' (default: text)
11
+ # outputFormat: text
12
+
13
+ # Service configuration
14
+ services:
15
+ # Reuse existing kubectl port-forward processes instead of failing (default: false)
16
+ # reusePortForwards: false
17
+
18
+ # Service overrides - customize specific Ark services
19
+ # Example: Enable localhost-gateway
20
+ # localhost-gateway:
21
+ # enabled: true
22
+
23
+ # Example: Change namespace
24
+ # ark-controller:
25
+ # namespace: custom-namespace
26
+
27
+ # Example: Add custom install arguments
28
+ # ark-api:
29
+ # installArgs:
30
+ # - --set
31
+ # - replicas=3
32
+
33
+ # Example: Configure Agents @ Scale with container registry
34
+ # agents-at-scale:
35
+ # enabled: true
36
+ # installArgs:
37
+ # - --set
38
+ # - containerRegistry.enabled=true
39
+ # - --set
40
+ # - containerRegistry.username=YOUR_USERNAME
41
+ # - --set
42
+ # - containerRegistry.password=YOUR_PASSWORD
43
+
44
+ # Available service properties:
45
+ # enabled: boolean - Enable/disable service installation
46
+ # namespace: string - Kubernetes namespace
47
+ # chartPath: string - Custom Helm chart path
48
+ # installArgs: string[] - Additional Helm install arguments
49
+ # k8sServiceName: string - Kubernetes service name for port forwarding
50
+ # k8sServicePort: number - Kubernetes service port for port forwarding
51
+ # k8sPortForwardLocalPort: number - Local port for port forwarding
package/README.md CHANGED
@@ -46,6 +46,8 @@ To troubleshoot an installation, run `ark status`.
46
46
 
47
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.
48
48
 
49
+ See [`.arkrc.template.yaml`](.arkrc.template.yaml) for a complete reference of all available configuration options.
50
+
49
51
  Example `.arkrc.yaml`:
50
52
 
51
53
  ```yaml
@@ -119,17 +119,20 @@ const defaultArkServices = {
119
119
  k8sDeploymentName: 'ark-mcp',
120
120
  k8sDevDeploymentName: 'ark-mcp-devspace',
121
121
  },
122
- 'ark-cluster-memory': {
123
- name: 'ark-cluster-memory',
124
- helmReleaseName: 'ark-cluster-memory',
122
+ // ark-broker replaces ark-cluster-memory (renamed in v0.1.49). The old release
123
+ // must be uninstalled first to avoid Helm ownership conflicts on shared
124
+ // resources like the ark-config-streaming ConfigMap.
125
+ 'ark-broker': {
126
+ name: 'ark-broker',
127
+ helmReleaseName: 'ark-broker',
125
128
  description: 'In-memory storage service with streaming support for Ark queries',
126
129
  enabled: true,
127
130
  category: 'service',
128
- // namespace: undefined - uses current context namespace
129
- chartPath: `${REGISTRY_BASE}/ark-cluster-memory`,
131
+ chartPath: `${REGISTRY_BASE}/ark-broker`,
130
132
  installArgs: [],
131
- k8sDeploymentName: 'ark-cluster-memory',
132
- k8sDevDeploymentName: 'ark-cluster-memory-devspace',
133
+ prerequisiteUninstalls: [{ releaseName: 'ark-cluster-memory' }],
134
+ k8sDeploymentName: 'ark-broker',
135
+ k8sDevDeploymentName: 'ark-broker-devspace',
133
136
  },
134
137
  'mcp-filesystem': {
135
138
  name: 'mcp-filesystem',
@@ -171,7 +174,9 @@ function applyConfigOverrides(defaults) {
171
174
  const overrides = config?.services || {};
172
175
  const result = {};
173
176
  for (const [key, service] of Object.entries(defaults)) {
174
- result[key] = overrides[key] ? { ...service, ...overrides[key] } : service;
177
+ const override = overrides[key];
178
+ result[key] =
179
+ override && typeof override === 'object' ? { ...service, ...override } : service;
175
180
  }
176
181
  return result;
177
182
  }
@@ -14,7 +14,7 @@ export function createChatCommand(config) {
14
14
  const initialTargetId = targetArg;
15
15
  // Config is passed from main
16
16
  try {
17
- const proxy = new ArkApiProxy();
17
+ const proxy = new ArkApiProxy(undefined, config.services?.reusePortForwards ?? false);
18
18
  const arkApiClient = await proxy.start();
19
19
  render(_jsx(ChatUI, { initialTargetId: initialTargetId, arkApiClient: arkApiClient, arkApiProxy: proxy, config: config }));
20
20
  }
@@ -28,7 +28,7 @@ _ark_completion() {
28
28
 
29
29
  case \${COMP_CWORD} in
30
30
  1)
31
- opts="agents chat cluster completion config dashboard docs generate install marketplace models queries query routes status targets teams tools uninstall help"
31
+ opts="agents chat cluster completion config dashboard docs evaluation export generate import install marketplace memory models queries query routes status targets teams tools uninstall help"
32
32
  COMPREPLY=( $(compgen -W "\${opts}" -- \${cur}) )
33
33
  return 0
34
34
  ;;
@@ -84,6 +84,11 @@ _ark_completion() {
84
84
  COMPREPLY=( $(compgen -W "\${opts}" -- \${cur}) )
85
85
  return 0
86
86
  ;;
87
+ memory)
88
+ opts="list ls delete reset"
89
+ COMPREPLY=( $(compgen -W "\${opts}" -- \${cur}) )
90
+ return 0
91
+ ;;
87
92
  install)
88
93
  opts="marketplace/services/phoenix marketplace/services/langfuse marketplace/agents/noah"
89
94
  COMPREPLY=( $(compgen -W "\${opts}" -- \${cur}) )
@@ -155,9 +160,13 @@ _ark() {
155
160
  'config[Configuration management]' \\
156
161
  'dashboard[Open ARK dashboard]' \\
157
162
  'docs[Open ARK documentation]' \\
163
+ 'evaluation[Execute evaluations against evaluators]' \\
164
+ 'export[Export ARK resources to a file]' \\
158
165
  'generate[Generate ARK resources]' \\
166
+ 'import[Import ARK resources from a file]' \\
159
167
  'install[Install ARK services]' \\
160
168
  'marketplace[Manage marketplace services]' \\
169
+ 'memory[Manage memory sessions and queries]' \\
161
170
  'models[List available models]' \\
162
171
  'queries[Manage query resources]' \\
163
172
  'query[Execute a single query]' \\
@@ -225,6 +234,13 @@ _ark() {
225
234
  'list[List available marketplace services]' \\
226
235
  'ls[List available marketplace services]'
227
236
  ;;
237
+ memory)
238
+ _values 'memory commands' \\
239
+ 'list[List all sessions]' \\
240
+ 'ls[List all sessions]' \\
241
+ 'delete[Delete memory data]' \\
242
+ 'reset[Delete memory data]'
243
+ ;;
228
244
  install)
229
245
  _values 'services to install' \\
230
246
  'marketplace/services/phoenix[Phoenix observability platform]' \\
@@ -1,4 +1,4 @@
1
1
  import { Command } from 'commander';
2
2
  import type { ArkConfig } from '../../lib/config.js';
3
- export declare function openDashboard(): Promise<void>;
4
- export declare function createDashboardCommand(_: ArkConfig): Command;
3
+ export declare function openDashboard(config: ArkConfig): Promise<void>;
4
+ export declare function createDashboardCommand(config: ArkConfig): Command;
@@ -4,11 +4,12 @@ import open from 'open';
4
4
  import ora from 'ora';
5
5
  import { ArkServiceProxy } from '../../lib/arkServiceProxy.js';
6
6
  import { arkServices } from '../../arkServices.js';
7
- export async function openDashboard() {
7
+ export async function openDashboard(config) {
8
8
  const spinner = ora('Connecting to dashboard').start();
9
9
  try {
10
10
  const dashboardService = arkServices['ark-dashboard'];
11
- const proxy = new ArkServiceProxy(dashboardService, 3274); // DASH on phone keypad
11
+ const proxy = new ArkServiceProxy(dashboardService, 3274, // DASH on phone keypad
12
+ config.services?.reusePortForwards ?? false);
12
13
  const url = await proxy.start();
13
14
  spinner.succeed('Dashboard connected');
14
15
  console.log(`ARK dashboard running on: ${chalk.green(url)}`);
@@ -30,10 +31,10 @@ export async function openDashboard() {
30
31
  process.exit(1);
31
32
  }
32
33
  }
33
- export function createDashboardCommand(_) {
34
+ export function createDashboardCommand(config) {
34
35
  const dashboardCommand = new Command('dashboard');
35
36
  dashboardCommand
36
37
  .description('Open the ARK dashboard in your browser')
37
- .action(openDashboard);
38
+ .action(() => openDashboard(config));
38
39
  return dashboardCommand;
39
40
  }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ import type { ArkConfig } from '../../lib/config.js';
3
+ export declare function createExportCommand(config: ArkConfig): Command;
@@ -0,0 +1,73 @@
1
+ import { Command } from 'commander';
2
+ import * as fs from 'fs/promises';
3
+ import yaml from 'yaml';
4
+ import { listResources } from '../../lib/kubectl.js';
5
+ import output from '../../lib/output.js';
6
+ // resource types in dependency order so that they can be loaded correctly
7
+ // by default these will all be exported if not specified; can be overridden with defaultExportTypes in config
8
+ const RESOURCE_ORDER = [
9
+ 'secrets',
10
+ 'tools',
11
+ 'models',
12
+ 'agents',
13
+ 'teams',
14
+ 'evaluators',
15
+ 'mcpservers',
16
+ 'a2aservers',
17
+ ];
18
+ async function exportResources(options, config) {
19
+ try {
20
+ const allResourceTypes = config.defaultExportTypes || RESOURCE_ORDER;
21
+ const outputPath = options.output || 'ark-export.yaml';
22
+ let resourceTypes = options.types
23
+ ? (options.types.split(','))
24
+ : allResourceTypes;
25
+ // ensure that we get resources in the correct order; e.g. agents before teams that use the agents
26
+ resourceTypes.sort((a, b) => {
27
+ return RESOURCE_ORDER.indexOf(a) - RESOURCE_ORDER.indexOf(b);
28
+ });
29
+ output.info(`exporting ark resources to ${outputPath}...`);
30
+ const allResources = [];
31
+ let allResourceCount = 0;
32
+ for (const resourceType of resourceTypes) {
33
+ if (!RESOURCE_ORDER.includes(resourceType)) {
34
+ output.warning(`unknown resource type: ${resourceType}, skipping`);
35
+ continue;
36
+ }
37
+ output.info(`fetching ${resourceType}...`);
38
+ const resources = await listResources(resourceType, {
39
+ namespace: options.namespace,
40
+ labels: options.labels
41
+ });
42
+ const resourceCount = resources.length;
43
+ if (resources.length > 0) {
44
+ output.success(`found ${resourceCount} ${resourceType}`);
45
+ allResources.push(...resources);
46
+ allResourceCount += resourceCount;
47
+ }
48
+ }
49
+ if (allResourceCount === 0) {
50
+ output.warning('no resources found to export');
51
+ return;
52
+ }
53
+ const yamlContent = allResources.map((resource) => yaml.stringify(resource)).join("\n---\n");
54
+ await fs.writeFile(outputPath, yamlContent, 'utf-8');
55
+ output.success(`exported ${allResourceCount} resources to ${outputPath}`);
56
+ }
57
+ catch (error) {
58
+ output.error('export failed:', error instanceof Error ? error.message : error);
59
+ }
60
+ }
61
+ export function createExportCommand(config) {
62
+ const exportCommand = new Command('export');
63
+ exportCommand
64
+ .description('export ARK resources to a file')
65
+ .option('-o, --output <file>', 'output file path', 'ark-export.yaml')
66
+ .option('-n, --namespace <namespace>', 'namespace to export from')
67
+ .option('-t, --types <types>', 'comma-separated list of resource types to export')
68
+ .option('-l, --labels <labels>', 'label selector to filter resources')
69
+ .action(async (options) => {
70
+ await exportResources(options, config);
71
+ });
72
+ return exportCommand;
73
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,145 @@
1
+ import { jest } from '@jest/globals';
2
+ import { Command } from 'commander';
3
+ const mockExeca = jest.fn();
4
+ jest.unstable_mockModule('execa', () => ({
5
+ execa: mockExeca,
6
+ }));
7
+ const mockWriteFile = jest.fn();
8
+ jest.unstable_mockModule('fs/promises', () => ({
9
+ writeFile: mockWriteFile,
10
+ }));
11
+ const mockOutput = {
12
+ info: jest.fn(),
13
+ success: jest.fn(),
14
+ warning: jest.fn(),
15
+ error: jest.fn(),
16
+ };
17
+ jest.unstable_mockModule('../../lib/output.js', () => ({
18
+ default: mockOutput,
19
+ }));
20
+ const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {
21
+ throw new Error('process.exit called');
22
+ }));
23
+ const mockKubectlGetResponse = {
24
+ "apiVersion": "v1",
25
+ "items": [{ "spec": "foo" }],
26
+ "kind": "List",
27
+ "metadata": {
28
+ "resourceVersion": ""
29
+ }
30
+ };
31
+ const { createExportCommand } = await import('./index.js');
32
+ describe('export command', () => {
33
+ const mockConfig = {};
34
+ beforeEach(() => {
35
+ jest.clearAllMocks();
36
+ });
37
+ it('should create export command with correct description', () => {
38
+ const command = createExportCommand(mockConfig);
39
+ expect(command).toBeInstanceOf(Command);
40
+ expect(command.name()).toBe('export');
41
+ expect(command.description()).toBe('export ARK resources to a file');
42
+ });
43
+ it('should export all resource types by default', async () => {
44
+ mockExeca.mockResolvedValue({
45
+ stdout: JSON.stringify(mockKubectlGetResponse),
46
+ });
47
+ mockWriteFile.mockResolvedValue(undefined);
48
+ const command = createExportCommand(mockConfig);
49
+ await command.parseAsync(['node', 'test', '-o', 'test.yaml']);
50
+ const expectedResourceTypes = [
51
+ 'secrets',
52
+ 'tools',
53
+ 'models',
54
+ 'agents',
55
+ 'teams',
56
+ 'evaluators',
57
+ 'mcpservers',
58
+ 'a2aservers',
59
+ ];
60
+ expect(mockExeca).toHaveBeenCalledTimes(expectedResourceTypes.length);
61
+ for (const resourceType of expectedResourceTypes) {
62
+ expect(mockExeca).toHaveBeenCalledWith('kubectl', expect.arrayContaining(['get', resourceType, '-o', 'json']), expect.any(Object));
63
+ expect(mockOutput.success).toHaveBeenCalledWith(`found 1 ${resourceType}`);
64
+ }
65
+ expect(mockWriteFile).toHaveBeenCalledTimes(1);
66
+ });
67
+ it('should export types specified in config in dependency order', async () => {
68
+ mockExeca.mockResolvedValue({
69
+ stdout: JSON.stringify(mockKubectlGetResponse),
70
+ });
71
+ mockWriteFile.mockResolvedValue(undefined);
72
+ const newDefaultTypes = ['teams', 'agents'];
73
+ const modifiedConfig = { defaultExportTypes: newDefaultTypes };
74
+ const command = createExportCommand(modifiedConfig);
75
+ await command.parseAsync(['node', 'test', '-o', 'test.yaml']);
76
+ expect(mockExeca.mock.calls).toEqual([
77
+ ['kubectl', expect.arrayContaining(['get', 'agents']), expect.any(Object)],
78
+ ['kubectl', expect.arrayContaining(['get', 'teams']), expect.any(Object)],
79
+ ]);
80
+ expect(mockWriteFile).toHaveBeenCalledTimes(1);
81
+ });
82
+ it('should filter by resource types when specified and export in order', async () => {
83
+ mockExeca.mockResolvedValue({
84
+ stdout: JSON.stringify(mockKubectlGetResponse),
85
+ });
86
+ mockWriteFile.mockResolvedValue(undefined);
87
+ const command = createExportCommand(mockConfig);
88
+ await command.parseAsync([
89
+ 'node',
90
+ 'test',
91
+ '-t',
92
+ 'agents,models',
93
+ '-o',
94
+ 'test.yaml',
95
+ ]);
96
+ expect(mockExeca.mock.calls).toEqual([
97
+ ['kubectl', expect.arrayContaining(['get', 'models']), expect.any(Object)],
98
+ ['kubectl', expect.arrayContaining(['get', 'agents']), expect.any(Object)],
99
+ ]);
100
+ });
101
+ it('should use namespace filter when specified', async () => {
102
+ mockExeca.mockResolvedValue({
103
+ stdout: JSON.stringify(mockKubectlGetResponse),
104
+ });
105
+ mockWriteFile.mockResolvedValue(undefined);
106
+ const command = createExportCommand(mockConfig);
107
+ await command.parseAsync([
108
+ 'node',
109
+ 'test',
110
+ '-n',
111
+ 'custom-namespace',
112
+ '-t',
113
+ 'agents',
114
+ '-o',
115
+ 'test.yaml',
116
+ ]);
117
+ expect(mockExeca).toHaveBeenCalledWith('kubectl', expect.arrayContaining(['-n', 'custom-namespace']), expect.any(Object));
118
+ expect(mockWriteFile).toHaveBeenCalledTimes(1);
119
+ });
120
+ it('should use label selector when specified', async () => {
121
+ mockExeca.mockResolvedValue({
122
+ stdout: JSON.stringify(mockKubectlGetResponse),
123
+ });
124
+ mockWriteFile.mockResolvedValue(undefined);
125
+ const command = createExportCommand(mockConfig);
126
+ await command.parseAsync([
127
+ 'node',
128
+ 'test',
129
+ '-l',
130
+ 'app=test',
131
+ '-t',
132
+ 'agents',
133
+ '-o',
134
+ 'test.yaml',
135
+ ]);
136
+ expect(mockExeca).toHaveBeenCalledWith('kubectl', expect.arrayContaining(['-l', 'app=test']), expect.any(Object));
137
+ expect(mockWriteFile).toHaveBeenCalledTimes(1);
138
+ });
139
+ it('fails if kubectl get fails for a resource type', async () => {
140
+ mockExeca.mockRejectedValue('Export broke');
141
+ const command = createExportCommand(mockConfig);
142
+ await command.parseAsync(['node', 'test']);
143
+ expect(mockOutput.error).toHaveBeenCalledWith('export failed:', 'Export broke');
144
+ });
145
+ });
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ import type { ArkConfig } from '../../lib/config.js';
3
+ export declare function createImportCommand(_: ArkConfig): Command;
@@ -0,0 +1,27 @@
1
+ import { Command } from 'commander';
2
+ import { execa } from 'execa';
3
+ import output from '../../lib/output.js';
4
+ async function importResources(filepath) {
5
+ try {
6
+ output.info(`importing ark resources from ${filepath}...`);
7
+ const args = ['create', '-f', filepath];
8
+ const result = await execa('kubectl', args, {
9
+ stdio: 'pipe',
10
+ });
11
+ output.success(`imported resources from ${filepath}`);
12
+ }
13
+ catch (error) {
14
+ output.error('import failed:', error instanceof Error ? error.message : error);
15
+ process.exit(1);
16
+ }
17
+ }
18
+ export function createImportCommand(_) {
19
+ const importCommand = new Command('import');
20
+ importCommand
21
+ .description('import ARK resources from a file')
22
+ .argument('<filepath>', 'input file path')
23
+ .action(async (filepath) => {
24
+ await importResources(filepath);
25
+ });
26
+ return importCommand;
27
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,46 @@
1
+ import { jest } from '@jest/globals';
2
+ import { Command } from 'commander';
3
+ const mockExeca = jest.fn();
4
+ jest.unstable_mockModule('execa', () => ({
5
+ execa: mockExeca,
6
+ }));
7
+ const mockOutput = {
8
+ info: jest.fn(),
9
+ success: jest.fn(),
10
+ warning: jest.fn(),
11
+ error: jest.fn(),
12
+ };
13
+ jest.unstable_mockModule('../../lib/output.js', () => ({
14
+ default: mockOutput,
15
+ }));
16
+ const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {
17
+ throw new Error('process.exit called');
18
+ }));
19
+ const { createImportCommand } = await import('./index.js');
20
+ describe('import command', () => {
21
+ const mockConfig = {};
22
+ beforeEach(() => {
23
+ jest.clearAllMocks();
24
+ });
25
+ it('should create import command with correct description', () => {
26
+ const command = createImportCommand(mockConfig);
27
+ expect(command).toBeInstanceOf(Command);
28
+ expect(command.name()).toBe('import');
29
+ expect(command.description()).toBe('import ARK resources from a file');
30
+ });
31
+ it('should use kubectl to import', async () => {
32
+ mockExeca.mockResolvedValue({
33
+ stdout: JSON.stringify({ items: [] }),
34
+ });
35
+ const command = createImportCommand(mockConfig);
36
+ await command.parseAsync(['node', 'test', 'test.yaml']);
37
+ expect(mockExeca).toHaveBeenCalledWith('kubectl', ['create', '-f', 'test.yaml'], expect.any(Object));
38
+ });
39
+ it('exits with error when kubectl create has error', async () => {
40
+ mockExeca.mockRejectedValue('Import broke');
41
+ const command = createImportCommand(mockConfig);
42
+ await expect(command.parseAsync(['node', 'test', 'test.yaml'])).rejects.toThrow('process.exit called');
43
+ expect(mockOutput.error).toHaveBeenCalledWith('import failed:', 'Import broke');
44
+ expect(mockExit).toHaveBeenCalledWith(1);
45
+ });
46
+ });
@@ -10,7 +10,19 @@ import { printNextSteps } from '../../lib/nextSteps.js';
10
10
  import ora from 'ora';
11
11
  import { waitForServicesReady, } from '../../lib/waitForReady.js';
12
12
  import { parseTimeoutToSeconds } from '../../lib/timeout.js';
13
+ async function uninstallPrerequisites(service, verbose = false) {
14
+ if (!service.prerequisiteUninstalls?.length)
15
+ return;
16
+ for (const prereq of service.prerequisiteUninstalls) {
17
+ const helmArgs = ['uninstall', prereq.releaseName, '--ignore-not-found'];
18
+ if (prereq.namespace) {
19
+ helmArgs.push('--namespace', prereq.namespace);
20
+ }
21
+ await execute('helm', helmArgs, { stdio: 'inherit' }, { verbose });
22
+ }
23
+ }
13
24
  async function installService(service, verbose = false) {
25
+ await uninstallPrerequisites(service, verbose);
14
26
  const helmArgs = [
15
27
  'upgrade',
16
28
  '--install',
@@ -1,9 +1,11 @@
1
1
  import { Command } from 'commander';
2
+ import { loadConfig } from '../../lib/config.js';
2
3
  import output from '../../lib/output.js';
3
4
  import { ArkApiProxy } from '../../lib/arkApiProxy.js';
4
5
  export async function listSessions(options) {
5
6
  try {
6
- const proxy = new ArkApiProxy();
7
+ const config = loadConfig();
8
+ const proxy = new ArkApiProxy(undefined, config.services?.reusePortForwards ?? false);
7
9
  const arkApiClient = await proxy.start();
8
10
  const sessions = await arkApiClient.getSessions();
9
11
  if (options.output === 'json') {
@@ -27,7 +29,8 @@ export async function listSessions(options) {
27
29
  }
28
30
  export async function deleteSession(sessionId, options) {
29
31
  try {
30
- const proxy = new ArkApiProxy();
32
+ const config = loadConfig();
33
+ const proxy = new ArkApiProxy(undefined, config.services?.reusePortForwards ?? false);
31
34
  const arkApiClient = await proxy.start();
32
35
  const response = await arkApiClient.deleteSession(sessionId);
33
36
  if (options.output === 'json') {
@@ -44,7 +47,8 @@ export async function deleteSession(sessionId, options) {
44
47
  }
45
48
  export async function deleteQuery(sessionId, queryId, options) {
46
49
  try {
47
- const proxy = new ArkApiProxy();
50
+ const config = loadConfig();
51
+ const proxy = new ArkApiProxy(undefined, config.services?.reusePortForwards ?? false);
48
52
  const arkApiClient = await proxy.start();
49
53
  const response = await arkApiClient.deleteQueryMessages(sessionId, queryId);
50
54
  if (options.output === 'json') {
@@ -61,7 +65,8 @@ export async function deleteQuery(sessionId, queryId, options) {
61
65
  }
62
66
  export async function deleteAll(options) {
63
67
  try {
64
- const proxy = new ArkApiProxy();
68
+ const config = loadConfig();
69
+ const proxy = new ArkApiProxy(undefined, config.services?.reusePortForwards ?? false);
65
70
  const arkApiClient = await proxy.start();
66
71
  const response = await arkApiClient.deleteAllSessions();
67
72
  if (options.output === 'json') {
@@ -11,7 +11,7 @@ export class KubernetesModelManifestBuilder {
11
11
  name: this.modelName,
12
12
  },
13
13
  spec: {
14
- type: config.type,
14
+ provider: config.type, // Use provider field (required as of v0.50.0)
15
15
  model: {
16
16
  value: config.modelValue,
17
17
  },
@@ -26,8 +26,8 @@ async function getQuery(name, options) {
26
26
  try {
27
27
  const query = await getResource('queries', name);
28
28
  if (options.response) {
29
- if (query.status?.responses && query.status.responses.length > 0) {
30
- const response = query.status.responses[0];
29
+ if (query.status?.response) {
30
+ const response = query.status.response;
31
31
  if (options.output === 'markdown') {
32
32
  console.log(renderMarkdown(response.content || ''));
33
33
  }
@@ -40,8 +40,8 @@ async function getQuery(name, options) {
40
40
  }
41
41
  }
42
42
  else if (options.output === 'markdown') {
43
- if (query.status?.responses && query.status.responses.length > 0) {
44
- console.log(renderMarkdown(query.status.responses[0].content || ''));
43
+ if (query.status?.response) {
44
+ console.log(renderMarkdown(query.status.response.content || ''));
45
45
  }
46
46
  else {
47
47
  output.warning('No response available');
@@ -0,0 +1 @@
1
+ export {};