@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
@@ -1,6 +1,10 @@
1
1
  import { spawn } from 'child_process';
2
+ import find from 'find-process';
3
+ import Debug from 'debug';
4
+ const debug = Debug('ark:service-proxy');
2
5
  export class ArkServiceProxy {
3
- constructor(service, localPort) {
6
+ constructor(service, localPort, reusePortForwards = false) {
7
+ this.reusePortForwards = reusePortForwards;
4
8
  this.isReady = false;
5
9
  this.service = service;
6
10
  this.localPort =
@@ -9,10 +13,39 @@ export class ArkServiceProxy {
9
13
  getRandomPort() {
10
14
  return Math.floor(Math.random() * (65535 - 1024) + 1024);
11
15
  }
16
+ async checkExistingPortForward() {
17
+ try {
18
+ const processes = await find('port', this.localPort);
19
+ if (processes.length === 0) {
20
+ return false;
21
+ }
22
+ const kubectlProcess = processes.find((proc) => proc.cmd?.includes('kubectl') && proc.cmd?.includes('port-forward'));
23
+ if (kubectlProcess) {
24
+ debug(`Reusing existing kubectl port-forward on port ${this.localPort} (PID: ${kubectlProcess.pid})`);
25
+ this.isReady = true;
26
+ return true;
27
+ }
28
+ const processInfo = processes[0];
29
+ throw new Error(`${this.service.name} port forward failed: port ${this.localPort} is already in use by ${processInfo.name} (PID: ${processInfo.pid})`);
30
+ }
31
+ catch (error) {
32
+ if (error instanceof Error && error.message.includes('already in use')) {
33
+ throw error;
34
+ }
35
+ debug(`Error checking for existing port-forward: ${error}`);
36
+ return false;
37
+ }
38
+ }
12
39
  async start() {
13
40
  if (!this.service.k8sServiceName || !this.service.k8sServicePort) {
14
41
  throw new Error(`${this.service.name} service configuration missing k8sServiceName or k8sServicePort`);
15
42
  }
43
+ if (this.reusePortForwards) {
44
+ const isReused = await this.checkExistingPortForward();
45
+ if (isReused) {
46
+ return `http://localhost:${this.localPort}`;
47
+ }
48
+ }
16
49
  return new Promise((resolve, reject) => {
17
50
  const args = [
18
51
  'port-forward',
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,100 @@
1
+ import { jest } from '@jest/globals';
2
+ import { Buffer } from 'buffer';
3
+ const mockFind = jest.fn();
4
+ jest.unstable_mockModule('find-process', () => ({
5
+ default: mockFind,
6
+ }));
7
+ const mockSpawn = jest.fn();
8
+ const mockChildProcess = {
9
+ spawn: mockSpawn,
10
+ };
11
+ jest.unstable_mockModule('child_process', () => ({
12
+ ...mockChildProcess,
13
+ spawn: mockSpawn,
14
+ }));
15
+ const { ArkServiceProxy } = await import('./arkServiceProxy.js');
16
+ describe('ArkServiceProxy', () => {
17
+ const mockService = {
18
+ name: 'test-service',
19
+ helmReleaseName: 'test-service',
20
+ description: 'Test service',
21
+ k8sServiceName: 'test-service-k8s',
22
+ k8sServicePort: 8080,
23
+ namespace: 'default',
24
+ enabled: true,
25
+ category: 'test',
26
+ };
27
+ beforeEach(() => {
28
+ jest.clearAllMocks();
29
+ });
30
+ describe('port-forward reuse', () => {
31
+ it('creates new port-forward when reuse is disabled', async () => {
32
+ const proxy = new ArkServiceProxy(mockService, 3000, false);
33
+ const mockProcess = {
34
+ stdout: { on: jest.fn() },
35
+ stderr: { on: jest.fn() },
36
+ on: jest.fn(),
37
+ kill: jest.fn(),
38
+ };
39
+ mockSpawn.mockReturnValue(mockProcess);
40
+ setTimeout(() => {
41
+ const stdoutCallback = mockProcess.stdout.on.mock.calls.find((call) => call[0] === 'data')?.[1];
42
+ if (stdoutCallback) {
43
+ stdoutCallback(Buffer.from('Forwarding from 127.0.0.1:3000'));
44
+ }
45
+ }, 10);
46
+ const url = await proxy.start();
47
+ expect(url).toBe('http://localhost:3000');
48
+ expect(mockFind).not.toHaveBeenCalled();
49
+ expect(mockSpawn).toHaveBeenCalledWith('kubectl', ['port-forward', 'service/test-service-k8s', '3000:8080', '--namespace', 'default'], expect.any(Object));
50
+ });
51
+ it('reuses existing kubectl port-forward when reuse is enabled', async () => {
52
+ mockFind.mockResolvedValue([
53
+ {
54
+ pid: 12345,
55
+ name: 'kubectl',
56
+ cmd: 'kubectl port-forward service/test-service-k8s 3000:8080',
57
+ },
58
+ ]);
59
+ const proxy = new ArkServiceProxy(mockService, 3000, true);
60
+ const url = await proxy.start();
61
+ expect(url).toBe('http://localhost:3000');
62
+ expect(mockFind).toHaveBeenCalledWith('port', 3000);
63
+ expect(mockSpawn).not.toHaveBeenCalled();
64
+ });
65
+ it('creates new port-forward when port is not in use', async () => {
66
+ mockFind.mockResolvedValue([]);
67
+ const proxy = new ArkServiceProxy(mockService, 3000, true);
68
+ const mockProcess = {
69
+ stdout: { on: jest.fn() },
70
+ stderr: { on: jest.fn() },
71
+ on: jest.fn(),
72
+ kill: jest.fn(),
73
+ };
74
+ mockSpawn.mockReturnValue(mockProcess);
75
+ setTimeout(() => {
76
+ const stdoutCallback = mockProcess.stdout.on.mock.calls.find((call) => call[0] === 'data')?.[1];
77
+ if (stdoutCallback) {
78
+ stdoutCallback(Buffer.from('Forwarding from 127.0.0.1:3000'));
79
+ }
80
+ }, 10);
81
+ const url = await proxy.start();
82
+ expect(url).toBe('http://localhost:3000');
83
+ expect(mockFind).toHaveBeenCalledWith('port', 3000);
84
+ expect(mockSpawn).toHaveBeenCalled();
85
+ });
86
+ it('throws error when port is in use by non-kubectl process', async () => {
87
+ mockFind.mockResolvedValue([
88
+ {
89
+ pid: 54321,
90
+ name: 'node',
91
+ cmd: 'node server.js',
92
+ },
93
+ ]);
94
+ const proxy = new ArkServiceProxy(mockService, 3000, true);
95
+ await expect(proxy.start()).rejects.toThrow('test-service port forward failed: port 3000 is already in use by node (PID: 54321)');
96
+ expect(mockFind).toHaveBeenCalledWith('port', 3000);
97
+ expect(mockSpawn).not.toHaveBeenCalled();
98
+ });
99
+ });
100
+ });
@@ -6,6 +6,7 @@ export interface ChatConfig {
6
6
  currentTarget?: QueryTarget;
7
7
  a2aContextId?: string;
8
8
  sessionId?: string;
9
+ conversationId?: string;
9
10
  queryTimeout?: string;
10
11
  }
11
12
  export interface ToolCall {
@@ -17,11 +17,17 @@ export class ChatClient {
17
17
  signal: signal,
18
18
  };
19
19
  // Build metadata object - only add if we have something to include
20
- if (config.sessionId || config.a2aContextId || config.queryTimeout) {
20
+ if (config.sessionId ||
21
+ config.conversationId ||
22
+ config.a2aContextId ||
23
+ config.queryTimeout) {
21
24
  params.metadata = {};
22
25
  if (config.sessionId) {
23
26
  params.metadata.sessionId = config.sessionId;
24
27
  }
28
+ if (config.conversationId) {
29
+ params.metadata.conversationId = config.conversationId;
30
+ }
25
31
  if (config.queryTimeout) {
26
32
  params.metadata.timeout = config.queryTimeout;
27
33
  }
@@ -12,9 +12,11 @@ export interface ArkConfig {
12
12
  chat?: ChatConfig;
13
13
  marketplace?: MarketplaceConfig;
14
14
  services?: {
15
- [serviceName: string]: Partial<ArkService>;
15
+ reusePortForwards?: boolean;
16
+ [serviceName: string]: Partial<ArkService> | boolean | undefined;
16
17
  };
17
18
  queryTimeout?: string;
19
+ defaultExportTypes?: string[];
18
20
  clusterInfo?: ClusterInfo;
19
21
  }
20
22
  /**
@@ -20,6 +20,9 @@ export function loadConfig() {
20
20
  repoUrl: 'https://github.com/mckinsey/agents-at-scale-marketplace',
21
21
  registry: 'oci://ghcr.io/mckinsey/agents-at-scale-marketplace/charts',
22
22
  },
23
+ services: {
24
+ reusePortForwards: false,
25
+ },
23
26
  };
24
27
  // Load user config from home directory
25
28
  const userConfigPath = path.join(os.homedir(), '.arkrc.yaml');
@@ -48,9 +51,7 @@ export function loadConfig() {
48
51
  // Apply environment variable overrides
49
52
  if (process.env.ARK_CHAT_STREAMING !== undefined) {
50
53
  config.chat = config.chat || {};
51
- config.chat.streaming =
52
- process.env.ARK_CHAT_STREAMING === '1' ||
53
- process.env.ARK_CHAT_STREAMING === 'true';
54
+ config.chat.streaming = process.env.ARK_CHAT_STREAMING === '1';
54
55
  }
55
56
  if (process.env.ARK_CHAT_OUTPUT_FORMAT !== undefined) {
56
57
  config.chat = config.chat || {};
@@ -70,6 +71,11 @@ export function loadConfig() {
70
71
  config.marketplace = config.marketplace || {};
71
72
  config.marketplace.registry = process.env.ARK_MARKETPLACE_REGISTRY;
72
73
  }
74
+ if (process.env.ARK_SERVICES_REUSE_PORT_FORWARDS !== undefined) {
75
+ config.services = config.services || {};
76
+ config.services.reusePortForwards =
77
+ process.env.ARK_SERVICES_REUSE_PORT_FORWARDS === '1';
78
+ }
73
79
  return config;
74
80
  }
75
81
  /**
@@ -96,16 +102,24 @@ function mergeConfig(target, source) {
96
102
  }
97
103
  if (source.services) {
98
104
  target.services = target.services || {};
105
+ if (source.services.reusePortForwards !== undefined) {
106
+ target.services.reusePortForwards = source.services.reusePortForwards;
107
+ }
99
108
  for (const [serviceName, overrides] of Object.entries(source.services)) {
100
- target.services[serviceName] = {
101
- ...target.services[serviceName],
102
- ...overrides,
103
- };
109
+ if (serviceName !== 'reusePortForwards' && typeof overrides === 'object') {
110
+ target.services[serviceName] = {
111
+ ...target.services[serviceName],
112
+ ...overrides,
113
+ };
114
+ }
104
115
  }
105
116
  }
106
117
  if (source.queryTimeout !== undefined) {
107
118
  target.queryTimeout = source.queryTimeout;
108
119
  }
120
+ if (source.defaultExportTypes) {
121
+ target.defaultExportTypes = source.defaultExportTypes;
122
+ }
109
123
  }
110
124
  /**
111
125
  * Get the paths checked for config files
@@ -39,6 +39,9 @@ describe('config', () => {
39
39
  repoUrl: 'https://github.com/mckinsey/agents-at-scale-marketplace',
40
40
  registry: 'oci://ghcr.io/mckinsey/agents-at-scale-marketplace/charts',
41
41
  },
42
+ services: {
43
+ reusePortForwards: false,
44
+ },
42
45
  });
43
46
  });
44
47
  it('loads and merges configs in order: defaults, user, project', () => {
@@ -77,6 +80,13 @@ describe('config', () => {
77
80
  const config = loadConfig();
78
81
  expect(config.queryTimeout).toBe('30m');
79
82
  });
83
+ it('loads defaultExportTypes from config file', () => {
84
+ mockFs.existsSync.mockReturnValue(true);
85
+ mockFs.readFileSync.mockReturnValue('yaml');
86
+ mockYaml.parse.mockReturnValue({ defaultExportTypes: ['agents', 'teams'] });
87
+ const config = loadConfig();
88
+ expect(config.defaultExportTypes).toEqual(['agents', 'teams']);
89
+ });
80
90
  it('ARK_QUERY_TIMEOUT environment variable overrides config', () => {
81
91
  mockFs.existsSync.mockReturnValue(true);
82
92
  mockFs.readFileSync.mockReturnValue('yaml');
@@ -11,6 +11,7 @@ export interface QueryOptions {
11
11
  verbose?: boolean;
12
12
  outputFormat?: string;
13
13
  sessionId?: string;
14
+ conversationId?: string;
14
15
  }
15
16
  export declare function executeQuery(options: QueryOptions): Promise<void>;
16
17
  /**
@@ -8,6 +8,7 @@ import { ExitCodes } from './errors.js';
8
8
  import { ArkApiProxy } from './arkApiProxy.js';
9
9
  import { ChatClient } from './chatClient.js';
10
10
  import { watchEventsLive } from './kubectl.js';
11
+ import { loadConfig } from './config.js';
11
12
  export async function executeQuery(options) {
12
13
  if (options.outputFormat) {
13
14
  return executeQueryWithFormat(options);
@@ -15,7 +16,8 @@ export async function executeQuery(options) {
15
16
  let arkApiProxy;
16
17
  const spinner = ora('Connecting to Ark API...').start();
17
18
  try {
18
- arkApiProxy = new ArkApiProxy();
19
+ const config = loadConfig();
20
+ arkApiProxy = new ArkApiProxy(undefined, config.services?.reusePortForwards ?? false);
19
21
  const arkApiClient = await arkApiProxy.start();
20
22
  const chatClient = new ChatClient(arkApiClient);
21
23
  spinner.text = 'Executing query...';
@@ -29,7 +31,13 @@ export async function executeQuery(options) {
29
31
  let headerShown = false;
30
32
  let firstOutput = true;
31
33
  const sessionId = options.sessionId || process.env.ARK_SESSION_ID;
32
- await chatClient.sendMessage(targetId, messages, { streamingEnabled: true, sessionId, queryTimeout: options.timeout }, (chunk, toolCalls, arkMetadata) => {
34
+ const conversationId = options.conversationId || process.env.ARK_CONVERSATION_ID;
35
+ await chatClient.sendMessage(targetId, messages, {
36
+ streamingEnabled: true,
37
+ sessionId,
38
+ conversationId,
39
+ queryTimeout: options.timeout,
40
+ }, (chunk, toolCalls, arkMetadata) => {
33
41
  if (firstOutput) {
34
42
  spinner.stop();
35
43
  firstOutput = false;
@@ -105,6 +113,9 @@ async function executeQueryWithFormat(options) {
105
113
  ...((options.sessionId || process.env.ARK_SESSION_ID) && {
106
114
  sessionId: options.sessionId || process.env.ARK_SESSION_ID,
107
115
  }),
116
+ ...((options.conversationId || process.env.ARK_CONVERSATION_ID) && {
117
+ conversationId: options.conversationId || process.env.ARK_CONVERSATION_ID,
118
+ }),
108
119
  targets: [
109
120
  {
110
121
  type: options.targetType,
@@ -6,6 +6,8 @@ interface K8sResource {
6
6
  }
7
7
  export declare function getResource<T extends K8sResource>(resourceType: string, name: string): Promise<T>;
8
8
  export declare function listResources<T extends K8sResource>(resourceType: string, options?: {
9
+ namespace?: string;
10
+ labels?: string;
9
11
  sortBy?: string;
10
12
  }): Promise<T[]>;
11
13
  export declare function deleteResource(resourceType: string, name?: string, options?: {
@@ -25,6 +25,12 @@ export async function listResources(resourceType, options) {
25
25
  if (options?.sortBy) {
26
26
  args.push(`--sort-by=${options.sortBy}`);
27
27
  }
28
+ if (options?.namespace) {
29
+ args.push('-n', options.namespace);
30
+ }
31
+ if (options?.labels) {
32
+ args.push('-l', options.labels);
33
+ }
28
34
  args.push('-o', 'json');
29
35
  const result = await execa('kubectl', args, { stdio: 'pipe' });
30
36
  const data = JSON.parse(result.stdout);
@@ -152,6 +152,22 @@ describe('kubectl', () => {
152
152
  'json',
153
153
  ], { stdio: 'pipe' });
154
154
  });
155
+ it('should pass namespace and label filters', async () => {
156
+ const result = await listResources('queries', {
157
+ labels: 'app=test',
158
+ namespace: 'foo'
159
+ });
160
+ expect(mockExeca).toHaveBeenCalledWith('kubectl', [
161
+ 'get',
162
+ 'queries',
163
+ '-n',
164
+ 'foo',
165
+ '-l',
166
+ 'app=test',
167
+ '-o',
168
+ 'json',
169
+ ], { stdio: 'pipe' });
170
+ });
155
171
  it('should handle kubectl errors when listing resources', async () => {
156
172
  mockExeca.mockRejectedValue(new Error('kubectl connection error'));
157
173
  await expect(listResources('queries')).rejects.toThrow('kubectl connection error');
@@ -122,6 +122,7 @@ export interface Query {
122
122
  input: string;
123
123
  targets: QueryTarget[];
124
124
  sessionId?: string;
125
+ conversationId?: string;
125
126
  timeout?: string;
126
127
  };
127
128
  status?: QueryStatus;
@@ -155,9 +155,11 @@ const MainMenu = ({ config }) => {
155
155
  // Import and start ChatUI in the same process
156
156
  const { render } = await import('ink');
157
157
  const { ArkApiProxy } = await import('../lib/arkApiProxy.js');
158
+ const { loadConfig } = await import('../lib/config.js');
158
159
  const ChatUI = (await import('../components/ChatUI.js')).default;
159
160
  try {
160
- const proxy = new ArkApiProxy();
161
+ const config = loadConfig();
162
+ const proxy = new ArkApiProxy(undefined, config.services?.reusePortForwards ?? false);
161
163
  const arkApiClient = await proxy.start();
162
164
  // Render ChatUI as a new Ink app
163
165
  render(_jsx(ChatUI, { arkApiClient: arkApiClient, arkApiProxy: proxy }));
@@ -211,7 +213,9 @@ const MainMenu = ({ config }) => {
211
213
  // Unmount fullscreen app and clear screen.
212
214
  await unmountInkApp();
213
215
  const { openDashboard } = await import('../commands/dashboard/index.js');
214
- await openDashboard();
216
+ const { loadConfig } = await import('../lib/config.js');
217
+ const config = loadConfig();
218
+ await openDashboard(config);
215
219
  break;
216
220
  }
217
221
  case 'status': {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-at-scale/ark",
3
- "version": "0.1.47",
3
+ "version": "0.1.49",
4
4
  "description": "Ark CLI - Interactive terminal interface for ARK agents",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -9,7 +9,8 @@
9
9
  },
10
10
  "files": [
11
11
  "dist",
12
- "templates"
12
+ "templates",
13
+ ".arkrc.template.yaml"
13
14
  ],
14
15
  "scripts": {
15
16
  "build": "npm run copy-templates && tsc && chmod +x dist/index.js",
@@ -48,6 +49,7 @@
48
49
  "commander": "^12.1.0",
49
50
  "debug": "^4.4.1",
50
51
  "execa": "^9.6.0",
52
+ "find-process": "^1.4.7",
51
53
  "ink": "^6.0.1",
52
54
  "ink-select-input": "^6.2.0",
53
55
  "ink-spinner": "^5.0.0",
@@ -1,8 +0,0 @@
1
- import { Agent, ArkApiClient } from '../lib/arkApiClient.js';
2
- interface AgentSelectorProps {
3
- arkApiClient: ArkApiClient;
4
- onSelect: (agent: Agent) => void;
5
- onExit: () => void;
6
- }
7
- export declare function AgentSelector({ arkApiClient, onSelect, onExit, }: AgentSelectorProps): import("react/jsx-runtime").JSX.Element;
8
- export {};
@@ -1,53 +0,0 @@
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 AgentSelector({ arkApiClient, onSelect, onExit, }) {
5
- const [selectedIndex, setSelectedIndex] = useState(0);
6
- const [agents, setAgents] = useState([]);
7
- const [loading, setLoading] = useState(true);
8
- const [error, setError] = useState(null);
9
- useEffect(() => {
10
- arkApiClient
11
- .getAgents()
12
- .then((fetchedAgents) => {
13
- setAgents(fetchedAgents);
14
- setLoading(false);
15
- })
16
- .catch((err) => {
17
- setError(err.message || 'Failed to fetch agents');
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 ? agents.length - 1 : prev - 1));
27
- }
28
- else if (key.downArrow || input === 'j') {
29
- setSelectedIndex((prev) => (prev === agents.length - 1 ? 0 : prev + 1));
30
- }
31
- else if (key.return) {
32
- onSelect(agents[selectedIndex]);
33
- }
34
- else {
35
- // Handle number keys for quick selection
36
- const num = parseInt(input, 10);
37
- if (!isNaN(num) && num >= 1 && num <= agents.length) {
38
- onSelect(agents[num - 1]);
39
- }
40
- }
41
- });
42
- if (loading) {
43
- return (_jsx(Box, { children: _jsx(Text, { children: "Loading agents..." }) }));
44
- }
45
- if (error) {
46
- return (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["Error: ", error] }) }));
47
- }
48
- if (agents.length === 0) {
49
- return (_jsx(Box, { children: _jsx(Text, { children: "No agents available" }) }));
50
- }
51
- const selectedAgent = agents[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 Agent" }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { dimColor: true, children: "Choose an agent to start a conversation with" }) }), _jsx(Box, { flexDirection: "column", children: agents.map((agent, index) => (_jsx(Box, { marginBottom: 0, children: _jsxs(Text, { color: index === selectedIndex ? 'green' : undefined, children: [index === selectedIndex ? '❯ ' : ' ', index + 1, ". ", agent.name] }) }, agent.name))) }), selectedAgent.description && (_jsx(Box, { marginTop: 1, paddingLeft: 2, children: _jsx(Text, { dimColor: true, wrap: "wrap", children: selectedAgent.description }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter to confirm \u00B7 Number to select \u00B7 Esc to exit" }) })] }));
53
- }
@@ -1,8 +0,0 @@
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 {};
@@ -1,53 +0,0 @@
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
- }
@@ -1,8 +0,0 @@
1
- import { Team, ArkApiClient } from '../lib/arkApiClient.js';
2
- interface TeamSelectorProps {
3
- arkApiClient: ArkApiClient;
4
- onSelect: (team: Team) => void;
5
- onExit: () => void;
6
- }
7
- export declare function TeamSelector({ arkApiClient, onSelect, onExit, }: TeamSelectorProps): import("react/jsx-runtime").JSX.Element;
8
- export {};
@@ -1,55 +0,0 @@
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 TeamSelector({ arkApiClient, onSelect, onExit, }) {
5
- const [selectedIndex, setSelectedIndex] = useState(0);
6
- const [teams, setTeams] = useState([]);
7
- const [loading, setLoading] = useState(true);
8
- const [error, setError] = useState(null);
9
- useEffect(() => {
10
- arkApiClient
11
- .getTeams()
12
- .then((fetchedTeams) => {
13
- setTeams(fetchedTeams);
14
- setLoading(false);
15
- })
16
- .catch((err) => {
17
- setError(err.message || 'Failed to fetch teams');
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 ? teams.length - 1 : prev - 1));
27
- }
28
- else if (key.downArrow || input === 'j') {
29
- setSelectedIndex((prev) => (prev === teams.length - 1 ? 0 : prev + 1));
30
- }
31
- else if (key.return) {
32
- onSelect(teams[selectedIndex]);
33
- }
34
- else {
35
- // Handle number keys for quick selection
36
- const num = parseInt(input, 10);
37
- if (!isNaN(num) && num >= 1 && num <= teams.length) {
38
- onSelect(teams[num - 1]);
39
- }
40
- }
41
- });
42
- if (loading) {
43
- return (_jsx(Box, { children: _jsx(Text, { children: "Loading teams..." }) }));
44
- }
45
- if (error) {
46
- return (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["Error: ", error] }) }));
47
- }
48
- if (teams.length === 0) {
49
- return (_jsx(Box, { children: _jsx(Text, { children: "No teams available" }) }));
50
- }
51
- const selectedTeam = teams[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 Team" }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { dimColor: true, children: "Choose a team to start a conversation with" }) }), _jsx(Box, { flexDirection: "column", children: teams.map((team, index) => (_jsx(Box, { marginBottom: 0, children: _jsxs(Text, { color: index === selectedIndex ? 'green' : undefined, children: [index === selectedIndex ? '❯ ' : ' ', index + 1, ". ", team.name, team.strategy ? ` (${team.strategy})` : ''] }) }, team.name))) }), selectedTeam &&
53
- (selectedTeam.description || selectedTeam.members_count) && (_jsx(Box, { marginTop: 1, paddingLeft: 2, children: _jsx(Text, { dimColor: true, wrap: "wrap", children: selectedTeam.description ||
54
- `Members: ${selectedTeam.members_count}` }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter to confirm \u00B7 Number to select \u00B7 Esc to exit" }) })] }));
55
- }
@@ -1,8 +0,0 @@
1
- import { Tool, ArkApiClient } from '../lib/arkApiClient.js';
2
- interface ToolSelectorProps {
3
- arkApiClient: ArkApiClient;
4
- onSelect: (tool: Tool) => void;
5
- onExit: () => void;
6
- }
7
- export declare function ToolSelector({ arkApiClient, onSelect, onExit, }: ToolSelectorProps): import("react/jsx-runtime").JSX.Element;
8
- export {};