@agents-at-scale/ark 0.1.47 → 0.1.49

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 (45) hide show
  1. package/.arkrc.template.yaml +51 -0
  2. package/README.md +2 -0
  3. package/dist/arkServices.js +9 -7
  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/memory/index.js +9 -4
  17. package/dist/commands/query/index.js +2 -0
  18. package/dist/index.js +4 -0
  19. package/dist/lib/arkApiProxy.d.ts +1 -1
  20. package/dist/lib/arkApiProxy.js +2 -2
  21. package/dist/lib/arkServiceProxy.d.ts +3 -1
  22. package/dist/lib/arkServiceProxy.js +34 -1
  23. package/dist/lib/arkServiceProxy.spec.d.ts +1 -0
  24. package/dist/lib/arkServiceProxy.spec.js +100 -0
  25. package/dist/lib/chatClient.d.ts +1 -0
  26. package/dist/lib/chatClient.js +7 -1
  27. package/dist/lib/config.d.ts +3 -1
  28. package/dist/lib/config.js +21 -7
  29. package/dist/lib/config.spec.js +10 -0
  30. package/dist/lib/executeQuery.d.ts +1 -0
  31. package/dist/lib/executeQuery.js +13 -2
  32. package/dist/lib/kubectl.d.ts +2 -0
  33. package/dist/lib/kubectl.js +6 -0
  34. package/dist/lib/kubectl.spec.js +16 -0
  35. package/dist/lib/types.d.ts +1 -0
  36. package/dist/ui/MainMenu.js +6 -2
  37. package/package.json +4 -2
  38. package/dist/ui/AgentSelector.d.ts +0 -8
  39. package/dist/ui/AgentSelector.js +0 -53
  40. package/dist/ui/ModelSelector.d.ts +0 -8
  41. package/dist/ui/ModelSelector.js +0 -53
  42. package/dist/ui/TeamSelector.d.ts +0 -8
  43. package/dist/ui/TeamSelector.js +0 -55
  44. package/dist/ui/ToolSelector.d.ts +0 -8
  45. 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,17 @@ 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': {
123
+ name: 'ark-broker',
124
+ helmReleaseName: 'ark-broker',
125
125
  description: 'In-memory storage service with streaming support for Ark queries',
126
126
  enabled: true,
127
127
  category: 'service',
128
128
  // namespace: undefined - uses current context namespace
129
- chartPath: `${REGISTRY_BASE}/ark-cluster-memory`,
129
+ chartPath: `${REGISTRY_BASE}/ark-broker`,
130
130
  installArgs: [],
131
- k8sDeploymentName: 'ark-cluster-memory',
132
- k8sDevDeploymentName: 'ark-cluster-memory-devspace',
131
+ k8sDeploymentName: 'ark-broker',
132
+ k8sDevDeploymentName: 'ark-broker-devspace',
133
133
  },
134
134
  'mcp-filesystem': {
135
135
  name: 'mcp-filesystem',
@@ -171,7 +171,9 @@ function applyConfigOverrides(defaults) {
171
171
  const overrides = config?.services || {};
172
172
  const result = {};
173
173
  for (const [key, service] of Object.entries(defaults)) {
174
- result[key] = overrides[key] ? { ...service, ...overrides[key] } : service;
174
+ const override = overrides[key];
175
+ result[key] =
176
+ override && typeof override === 'object' ? { ...service, ...override } : service;
175
177
  }
176
178
  return result;
177
179
  }
@@ -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
+ });
@@ -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,6 +11,7 @@ export function createQueryCommand(config) {
11
11
  .option('-o, --output <format>', 'Output format: yaml, json, name or events (shows structured event data)')
12
12
  .option('--timeout <timeout>', 'Query timeout (e.g., 30s, 5m, 1h)')
13
13
  .option('--session-id <sessionId>', 'Session ID to associate with the query for conversation continuity')
14
+ .option('--conversation-id <conversationId>', 'Conversation ID to associate with the query for memory continuity')
14
15
  .action(async (target, message, options) => {
15
16
  const parsed = parseTarget(target);
16
17
  if (!parsed) {
@@ -24,6 +25,7 @@ export function createQueryCommand(config) {
24
25
  outputFormat: options.output,
25
26
  timeout: options.timeout || config.queryTimeout,
26
27
  sessionId: options.sessionId,
28
+ conversationId: options.conversationId,
27
29
  });
28
30
  });
29
31
  return queryCommand;
package/dist/index.js CHANGED
@@ -14,7 +14,9 @@ import { createCompletionCommand } from './commands/completion/index.js';
14
14
  import { createDashboardCommand } from './commands/dashboard/index.js';
15
15
  import { createDocsCommand } from './commands/docs/index.js';
16
16
  import { createEvaluationCommand } from './commands/evaluation/index.js';
17
+ import { createExportCommand } from './commands/export/index.js';
17
18
  import { createGenerateCommand } from './commands/generate/index.js';
19
+ import { createImportCommand } from './commands/import/index.js';
18
20
  import { createInstallCommand } from './commands/install/index.js';
19
21
  import { createMarketplaceCommand } from './commands/marketplace/index.js';
20
22
  import { createMemoryCommand } from './commands/memory/index.js';
@@ -48,7 +50,9 @@ async function main() {
48
50
  program.addCommand(createDashboardCommand(config));
49
51
  program.addCommand(createDocsCommand(config));
50
52
  program.addCommand(createEvaluationCommand(config));
53
+ program.addCommand(createExportCommand(config));
51
54
  program.addCommand(createGenerateCommand(config));
55
+ program.addCommand(createImportCommand(config));
52
56
  program.addCommand(createInstallCommand(config));
53
57
  program.addCommand(createMarketplaceCommand(config));
54
58
  program.addCommand(createMemoryCommand(config));
@@ -1,7 +1,7 @@
1
1
  import { ArkApiClient } from './arkApiClient.js';
2
2
  export declare class ArkApiProxy {
3
3
  private serviceProxy;
4
- constructor(localPort?: number);
4
+ constructor(localPort?: number, reusePortForwards?: boolean);
5
5
  start(): Promise<ArkApiClient>;
6
6
  stop(): void;
7
7
  isRunning(): boolean;
@@ -2,9 +2,9 @@ import { ArkApiClient } from './arkApiClient.js';
2
2
  import { ArkServiceProxy } from './arkServiceProxy.js';
3
3
  import { arkServices } from '../arkServices.js';
4
4
  export class ArkApiProxy {
5
- constructor(localPort) {
5
+ constructor(localPort, reusePortForwards = false) {
6
6
  const arkApiService = arkServices['ark-api'];
7
- this.serviceProxy = new ArkServiceProxy(arkApiService, localPort);
7
+ this.serviceProxy = new ArkServiceProxy(arkApiService, localPort, reusePortForwards);
8
8
  }
9
9
  async start() {
10
10
  const arkApiUrl = await this.serviceProxy.start();
@@ -1,11 +1,13 @@
1
1
  import { ArkService } from '../arkServices.js';
2
2
  export declare class ArkServiceProxy {
3
+ private reusePortForwards;
3
4
  private kubectlProcess?;
4
5
  private localPort;
5
6
  private isReady;
6
7
  private service;
7
- constructor(service: ArkService, localPort?: number);
8
+ constructor(service: ArkService, localPort?: number, reusePortForwards?: boolean);
8
9
  private getRandomPort;
10
+ private checkExistingPortForward;
9
11
  start(): Promise<string>;
10
12
  stop(): void;
11
13
  isRunning(): boolean;