@agents-at-scale/ark 0.1.52 → 0.1.55

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 (116) hide show
  1. package/dist/arkServices.js +11 -7
  2. package/dist/commands/export/index.js +6 -4
  3. package/dist/commands/generate/generators/agent.js +2 -0
  4. package/dist/commands/generate/generators/marketplace.js +2 -0
  5. package/dist/commands/generate/generators/mcpserver.js +2 -0
  6. package/dist/commands/generate/generators/project.js +9 -2
  7. package/dist/commands/generate/generators/query.js +2 -0
  8. package/dist/commands/generate/generators/team.js +2 -0
  9. package/dist/commands/generate/templateDiscovery.js +1 -0
  10. package/dist/commands/generate/templateEngine.js +1 -3
  11. package/dist/commands/import/index.js +1 -1
  12. package/dist/commands/install/index.js +2 -1
  13. package/dist/commands/models/kubernetes/manifest-builder.js +27 -10
  14. package/dist/commands/models/providers/azure.d.ts +10 -7
  15. package/dist/commands/models/providers/azure.js +83 -21
  16. package/dist/commands/uninstall/index.js +1 -1
  17. package/dist/components/ChatUI.js +17 -16
  18. package/dist/components/statusChecker.js +3 -3
  19. package/dist/lib/arkApiClient.js +11 -9
  20. package/dist/lib/arkApiProxy.js +1 -0
  21. package/dist/lib/arkServiceProxy.js +5 -1
  22. package/dist/lib/chatClient.js +9 -0
  23. package/dist/lib/config.js +8 -3
  24. package/dist/lib/errors.js +3 -0
  25. package/dist/ui/asyncOperations/connectingToArk.js +2 -2
  26. package/package.json +17 -13
  27. package/dist/arkServices.spec.d.ts +0 -1
  28. package/dist/arkServices.spec.js +0 -138
  29. package/dist/commands/agents/index.spec.d.ts +0 -1
  30. package/dist/commands/agents/index.spec.js +0 -67
  31. package/dist/commands/cluster/get.spec.d.ts +0 -1
  32. package/dist/commands/cluster/get.spec.js +0 -92
  33. package/dist/commands/cluster/index.spec.d.ts +0 -1
  34. package/dist/commands/cluster/index.spec.js +0 -24
  35. package/dist/commands/completion/index.spec.d.ts +0 -1
  36. package/dist/commands/completion/index.spec.js +0 -34
  37. package/dist/commands/config/index.spec.d.ts +0 -1
  38. package/dist/commands/config/index.spec.js +0 -78
  39. package/dist/commands/evaluation/index.spec.d.ts +0 -1
  40. package/dist/commands/evaluation/index.spec.js +0 -161
  41. package/dist/commands/export/index.spec.d.ts +0 -1
  42. package/dist/commands/export/index.spec.js +0 -145
  43. package/dist/commands/import/index.spec.d.ts +0 -1
  44. package/dist/commands/import/index.spec.js +0 -46
  45. package/dist/commands/install/index.spec.d.ts +0 -1
  46. package/dist/commands/install/index.spec.js +0 -286
  47. package/dist/commands/marketplace/index.spec.d.ts +0 -1
  48. package/dist/commands/marketplace/index.spec.js +0 -88
  49. package/dist/commands/memory/index.spec.d.ts +0 -1
  50. package/dist/commands/memory/index.spec.js +0 -124
  51. package/dist/commands/models/create.spec.d.ts +0 -1
  52. package/dist/commands/models/create.spec.js +0 -167
  53. package/dist/commands/models/index.spec.d.ts +0 -1
  54. package/dist/commands/models/index.spec.js +0 -96
  55. package/dist/commands/models/providers/azure.spec.d.ts +0 -1
  56. package/dist/commands/models/providers/azure.spec.js +0 -232
  57. package/dist/commands/models/providers/bedrock.spec.d.ts +0 -1
  58. package/dist/commands/models/providers/bedrock.spec.js +0 -241
  59. package/dist/commands/models/providers/openai.spec.d.ts +0 -1
  60. package/dist/commands/models/providers/openai.spec.js +0 -180
  61. package/dist/commands/queries/delete.spec.d.ts +0 -1
  62. package/dist/commands/queries/delete.spec.js +0 -74
  63. package/dist/commands/queries/index.spec.d.ts +0 -1
  64. package/dist/commands/queries/index.spec.js +0 -167
  65. package/dist/commands/queries/list.spec.d.ts +0 -1
  66. package/dist/commands/queries/list.spec.js +0 -170
  67. package/dist/commands/queries/validation.spec.d.ts +0 -1
  68. package/dist/commands/queries/validation.spec.js +0 -27
  69. package/dist/commands/query/index.spec.d.ts +0 -1
  70. package/dist/commands/query/index.spec.js +0 -104
  71. package/dist/commands/targets/index.spec.d.ts +0 -1
  72. package/dist/commands/targets/index.spec.js +0 -154
  73. package/dist/commands/teams/index.spec.d.ts +0 -1
  74. package/dist/commands/teams/index.spec.js +0 -70
  75. package/dist/commands/tools/index.spec.d.ts +0 -1
  76. package/dist/commands/tools/index.spec.js +0 -70
  77. package/dist/commands/uninstall/index.spec.d.ts +0 -1
  78. package/dist/commands/uninstall/index.spec.js +0 -125
  79. package/dist/lib/arkServiceProxy.spec.d.ts +0 -1
  80. package/dist/lib/arkServiceProxy.spec.js +0 -100
  81. package/dist/lib/arkStatus.spec.d.ts +0 -1
  82. package/dist/lib/arkStatus.spec.js +0 -49
  83. package/dist/lib/chatClient.spec.d.ts +0 -1
  84. package/dist/lib/chatClient.spec.js +0 -108
  85. package/dist/lib/cluster.spec.d.ts +0 -1
  86. package/dist/lib/cluster.spec.js +0 -338
  87. package/dist/lib/commands.spec.d.ts +0 -1
  88. package/dist/lib/commands.spec.js +0 -146
  89. package/dist/lib/config.spec.d.ts +0 -1
  90. package/dist/lib/config.spec.js +0 -202
  91. package/dist/lib/duration.spec.d.ts +0 -1
  92. package/dist/lib/duration.spec.js +0 -13
  93. package/dist/lib/errors.spec.d.ts +0 -1
  94. package/dist/lib/errors.spec.js +0 -221
  95. package/dist/lib/executeQuery.spec.d.ts +0 -1
  96. package/dist/lib/executeQuery.spec.js +0 -325
  97. package/dist/lib/kubectl.spec.d.ts +0 -1
  98. package/dist/lib/kubectl.spec.js +0 -192
  99. package/dist/lib/marketplaceFetcher.spec.d.ts +0 -1
  100. package/dist/lib/marketplaceFetcher.spec.js +0 -225
  101. package/dist/lib/nextSteps.spec.d.ts +0 -1
  102. package/dist/lib/nextSteps.spec.js +0 -59
  103. package/dist/lib/output.spec.d.ts +0 -1
  104. package/dist/lib/output.spec.js +0 -123
  105. package/dist/lib/startup.spec.d.ts +0 -1
  106. package/dist/lib/startup.spec.js +0 -152
  107. package/dist/lib/stdin.spec.d.ts +0 -1
  108. package/dist/lib/stdin.spec.js +0 -82
  109. package/dist/lib/timeout.spec.d.ts +0 -1
  110. package/dist/lib/timeout.spec.js +0 -14
  111. package/dist/lib/waitForReady.spec.d.ts +0 -1
  112. package/dist/lib/waitForReady.spec.js +0 -104
  113. package/dist/marketplaceServices.spec.d.ts +0 -1
  114. package/dist/marketplaceServices.spec.js +0 -74
  115. package/dist/ui/statusFormatter.spec.d.ts +0 -1
  116. package/dist/ui/statusFormatter.spec.js +0 -58
@@ -3,9 +3,13 @@ import find from 'find-process';
3
3
  import Debug from 'debug';
4
4
  const debug = Debug('ark:service-proxy');
5
5
  export class ArkServiceProxy {
6
+ reusePortForwards;
7
+ kubectlProcess;
8
+ localPort;
9
+ isReady = false;
10
+ service;
6
11
  constructor(service, localPort, reusePortForwards = false) {
7
12
  this.reusePortForwards = reusePortForwards;
8
- this.isReady = false;
9
13
  this.service = service;
10
14
  this.localPort =
11
15
  localPort || service.k8sPortForwardLocalPort || this.getRandomPort();
@@ -1,5 +1,6 @@
1
1
  import { QUERY_ANNOTATIONS } from './constants.js';
2
2
  export class ChatClient {
3
+ arkApiClient;
3
4
  constructor(arkApiClient) {
4
5
  this.arkApiClient = arkApiClient;
5
6
  }
@@ -57,6 +58,14 @@ export class ChatClient {
57
58
  if (onChunk) {
58
59
  onChunk(content, undefined, arkMetadata);
59
60
  }
61
+ if (!fullResponse &&
62
+ arkMetadata?.completedQuery?.status?.response?.content) {
63
+ const responseContent = arkMetadata.completedQuery.status.response.content;
64
+ fullResponse = responseContent;
65
+ if (onChunk) {
66
+ onChunk(responseContent, undefined, arkMetadata);
67
+ }
68
+ }
60
69
  // Handle tool calls
61
70
  if (delta?.tool_calls) {
62
71
  for (const toolCallDelta of delta.tool_calls) {
@@ -33,7 +33,9 @@ export function loadConfig() {
33
33
  }
34
34
  catch (e) {
35
35
  const message = e instanceof Error ? e.message : 'Unknown error';
36
- throw new Error(`Invalid YAML in ${userConfigPath}: ${message}`);
36
+ throw new Error(`Invalid YAML in ${userConfigPath}: ${message}`, {
37
+ cause: e,
38
+ });
37
39
  }
38
40
  }
39
41
  // Load project config from current directory
@@ -45,7 +47,9 @@ export function loadConfig() {
45
47
  }
46
48
  catch (e) {
47
49
  const message = e instanceof Error ? e.message : 'Unknown error';
48
- throw new Error(`Invalid YAML in ${projectConfigPath}: ${message}`);
50
+ throw new Error(`Invalid YAML in ${projectConfigPath}: ${message}`, {
51
+ cause: e,
52
+ });
49
53
  }
50
54
  }
51
55
  // Apply environment variable overrides
@@ -106,7 +110,8 @@ function mergeConfig(target, source) {
106
110
  target.services.reusePortForwards = source.services.reusePortForwards;
107
111
  }
108
112
  for (const [serviceName, overrides] of Object.entries(source.services)) {
109
- if (serviceName !== 'reusePortForwards' && typeof overrides === 'object') {
113
+ if (serviceName !== 'reusePortForwards' &&
114
+ typeof overrides === 'object') {
110
115
  target.services[serviceName] = {
111
116
  ...target.services[serviceName],
112
117
  ...overrides,
@@ -24,6 +24,9 @@ export var ErrorCode;
24
24
  ErrorCode["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
25
25
  })(ErrorCode || (ErrorCode = {}));
26
26
  export class ArkError extends Error {
27
+ code;
28
+ details;
29
+ suggestions;
27
30
  constructor(message, code = ErrorCode.UNKNOWN_ERROR, details, suggestions) {
28
31
  super(message);
29
32
  this.name = 'ArkError';
@@ -5,8 +5,8 @@ export function createConnectingToArkOperation(params) {
5
5
  operation: async (_signal) => {
6
6
  const client = new ChatClient(params.arkApiClient);
7
7
  const targets = await client.getQueryTargets();
8
- let selectedTarget = null;
9
- let selectedIndex = 0;
8
+ let selectedTarget;
9
+ let selectedIndex;
10
10
  if (params.initialTargetId) {
11
11
  const matchedTarget = targets.find((t) => t.id === params.initialTargetId);
12
12
  const matchedIndex = targets.findIndex((t) => t.id === params.initialTargetId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-at-scale/ark",
3
- "version": "0.1.52",
3
+ "version": "0.1.55",
4
4
  "description": "Ark CLI - Interactive terminal interface for ARK agents",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -20,7 +20,7 @@
20
20
  "clean": "rm -rf dist",
21
21
  "lint": "eslint src/ --fix && prettier --write src/",
22
22
  "lint:check": "eslint src/ && prettier --check src/",
23
- "test": "NODE_OPTIONS=\"--experimental-vm-modules\" jest --coverage --coverageDirectory=./artifacts/coverage --coverageReporters=text --coverageReporters=lcov --coverageReporters=text-summary",
23
+ "test": "vitest run --coverage",
24
24
  "postinstall": "echo \"Ark CLI installed. Run 'ark' to try it out.\""
25
25
  },
26
26
  "keywords": [
@@ -43,8 +43,8 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "@kubernetes/client-node": "^1.3.0",
46
- "@modelcontextprotocol/sdk": "^1.18.0",
47
- "axios": "^1.7.7",
46
+ "@modelcontextprotocol/sdk": "^1.27.1",
47
+ "axios": "^1.13.5",
48
48
  "chalk": "^4.1.2",
49
49
  "commander": "^12.1.0",
50
50
  "debug": "^4.4.1",
@@ -64,25 +64,29 @@
64
64
  "yaml": "^2.6.1"
65
65
  },
66
66
  "devDependencies": {
67
- "@eslint/js": "^9.17.0",
68
- "@jest/globals": "^30.1.2",
67
+ "@eslint/js": "^10.0.1",
69
68
  "@types/debug": "^4.1.12",
70
69
  "@types/inquirer": "^9.0.7",
71
- "@types/jest": "^30.0.0",
72
70
  "@types/marked-terminal": "^6.1.1",
73
71
  "@types/node": "^22.10.2",
74
72
  "@types/react": "^19.1.13",
75
- "@typescript-eslint/eslint-plugin": "^8.20.0",
76
- "@typescript-eslint/parser": "^8.20.0",
77
- "eslint": "^9.17.0",
78
- "jest": "^30.1.3",
73
+ "@typescript-eslint/eslint-plugin": "^8.56.0",
74
+ "@typescript-eslint/parser": "^8.56.0",
75
+ "@vitest/coverage-v8": "^3.2.4",
76
+ "eslint": "^10.0.0",
77
+ "globals": "^16.2.0",
79
78
  "prettier": "^3.6.2",
80
- "ts-jest": "^29.4.1",
81
79
  "ts-node": "^10.9.2",
82
80
  "tsx": "^4.20.5",
83
- "typescript": "^5.7.2"
81
+ "typescript": "^5.7.2",
82
+ "vitest": "^3.2.4"
84
83
  },
85
84
  "engines": {
86
85
  "node": ">=18.0.0"
86
+ },
87
+ "overrides": {
88
+ "minimatch": "^10.2.3",
89
+ "rollup": "4.59.0",
90
+ "hono": "^4.12.2"
87
91
  }
88
92
  }
@@ -1 +0,0 @@
1
- export {};
@@ -1,138 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- const mockLoadConfig = jest.fn();
3
- const mockGetMarketplaceRegistry = jest.fn();
4
- jest.unstable_mockModule('./lib/config.js', () => ({
5
- loadConfig: mockLoadConfig,
6
- getMarketplaceRegistry: mockGetMarketplaceRegistry,
7
- }));
8
- mockLoadConfig.mockReturnValue({});
9
- mockGetMarketplaceRegistry.mockReturnValue('oci://test-registry/charts');
10
- const { arkDependencies, arkServices: originalArkServices, getInstallableServices, } = await import('./arkServices.js');
11
- describe('arkServices', () => {
12
- beforeEach(() => {
13
- jest.clearAllMocks();
14
- mockLoadConfig.mockReturnValue({});
15
- mockGetMarketplaceRegistry.mockReturnValue('oci://test-registry/charts');
16
- });
17
- it('exports arkDependencies with expected structure', () => {
18
- expect(arkDependencies).toBeDefined();
19
- expect(arkDependencies['cert-manager']).toBeDefined();
20
- expect(arkDependencies['cert-manager'].command).toBe('helm');
21
- });
22
- it('exports arkServices with expected structure', () => {
23
- expect(originalArkServices).toBeDefined();
24
- expect(originalArkServices['ark-controller']).toBeDefined();
25
- expect(originalArkServices['ark-controller'].namespace).toBe('ark-system');
26
- expect(originalArkServices['ark-api'].namespace).toBeUndefined();
27
- expect(originalArkServices['ark-dashboard'].namespace).toBeUndefined();
28
- expect(originalArkServices['localhost-gateway'].namespace).toBe('ark-system');
29
- });
30
- it('getInstallableServices returns services with chartPath', () => {
31
- const installable = getInstallableServices();
32
- expect(installable['ark-controller']).toBeDefined();
33
- expect(installable['ark-api']).toBeDefined();
34
- expect(installable['ark-api-a2a']).toBeUndefined();
35
- });
36
- describe('applyConfigOverrides', () => {
37
- it('applies no overrides when config has no services section', async () => {
38
- mockLoadConfig.mockReturnValue({});
39
- jest.resetModules();
40
- const { arkServices } = await import('./arkServices.js');
41
- expect(arkServices['ark-controller'].enabled).toBe(true);
42
- expect(arkServices['ark-api'].enabled).toBe(true);
43
- });
44
- it('applies overrides to enable a disabled service', async () => {
45
- mockLoadConfig.mockReturnValue({
46
- services: {
47
- 'localhost-gateway': { enabled: true },
48
- },
49
- });
50
- jest.resetModules();
51
- const { arkServices } = await import('./arkServices.js');
52
- expect(arkServices['localhost-gateway'].enabled).toBe(true);
53
- });
54
- it('applies overrides to disable an enabled service', async () => {
55
- mockLoadConfig.mockReturnValue({
56
- services: {
57
- 'ark-controller': { enabled: false },
58
- },
59
- });
60
- jest.resetModules();
61
- const { arkServices } = await import('./arkServices.js');
62
- expect(arkServices['ark-controller'].enabled).toBe(false);
63
- });
64
- it('applies overrides to multiple services', async () => {
65
- mockLoadConfig.mockReturnValue({
66
- services: {
67
- 'ark-controller': { enabled: false },
68
- 'ark-api': { enabled: false },
69
- 'localhost-gateway': { enabled: true },
70
- },
71
- });
72
- jest.resetModules();
73
- const { arkServices } = await import('./arkServices.js');
74
- expect(arkServices['ark-controller'].enabled).toBe(false);
75
- expect(arkServices['ark-api'].enabled).toBe(false);
76
- expect(arkServices['localhost-gateway'].enabled).toBe(true);
77
- });
78
- it('applies overrides to namespace', async () => {
79
- mockLoadConfig.mockReturnValue({
80
- services: {
81
- 'ark-api': { namespace: 'custom-namespace' },
82
- },
83
- });
84
- jest.resetModules();
85
- const { arkServices } = await import('./arkServices.js');
86
- expect(arkServices['ark-api'].namespace).toBe('custom-namespace');
87
- });
88
- it('applies overrides to chartPath', async () => {
89
- mockLoadConfig.mockReturnValue({
90
- services: {
91
- 'ark-controller': {
92
- chartPath: 'oci://custom-registry/charts/ark-controller',
93
- },
94
- },
95
- });
96
- jest.resetModules();
97
- const { arkServices } = await import('./arkServices.js');
98
- expect(arkServices['ark-controller'].chartPath).toBe('oci://custom-registry/charts/ark-controller');
99
- });
100
- it('applies partial overrides without affecting other properties', async () => {
101
- mockLoadConfig.mockReturnValue({
102
- services: {
103
- 'ark-controller': { enabled: false },
104
- },
105
- });
106
- jest.resetModules();
107
- const { arkServices } = await import('./arkServices.js');
108
- expect(arkServices['ark-controller'].enabled).toBe(false);
109
- expect(arkServices['ark-controller'].namespace).toBe('ark-system');
110
- expect(arkServices['ark-controller'].category).toBe('core');
111
- expect(arkServices['ark-controller'].description).toBe('Core Ark controller for managing AI resources');
112
- });
113
- it('applies overrides to installArgs', async () => {
114
- mockLoadConfig.mockReturnValue({
115
- services: {
116
- 'ark-controller': { installArgs: ['--set', 'custom.value=true'] },
117
- },
118
- });
119
- jest.resetModules();
120
- const { arkServices } = await import('./arkServices.js');
121
- expect(arkServices['ark-controller'].installArgs).toEqual([
122
- '--set',
123
- 'custom.value=true',
124
- ]);
125
- });
126
- it('does not affect services without overrides', async () => {
127
- mockLoadConfig.mockReturnValue({
128
- services: {
129
- 'ark-controller': { enabled: false },
130
- },
131
- });
132
- jest.resetModules();
133
- const { arkServices } = await import('./arkServices.js');
134
- expect(arkServices['ark-api'].enabled).toBe(true);
135
- expect(arkServices['ark-dashboard'].enabled).toBe(true);
136
- });
137
- });
138
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,67 +0,0 @@
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
- warning: jest.fn(),
9
- error: jest.fn(),
10
- };
11
- jest.unstable_mockModule('../../lib/output.js', () => ({
12
- default: mockOutput,
13
- }));
14
- const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {
15
- throw new Error('process.exit called');
16
- }));
17
- const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => { });
18
- const { createAgentsCommand } = await import('./index.js');
19
- describe('agents command', () => {
20
- beforeEach(() => {
21
- jest.clearAllMocks();
22
- });
23
- it('creates command with correct structure', () => {
24
- const command = createAgentsCommand({});
25
- expect(command).toBeInstanceOf(Command);
26
- expect(command.name()).toBe('agents');
27
- });
28
- it('lists agents in text format', async () => {
29
- const mockAgents = {
30
- items: [{ metadata: { name: 'agent1' } }, { metadata: { name: 'agent2' } }],
31
- };
32
- mockExeca.mockResolvedValue({ stdout: JSON.stringify(mockAgents) });
33
- const command = createAgentsCommand({});
34
- await command.parseAsync(['node', 'test']);
35
- expect(mockExeca).toHaveBeenCalledWith('kubectl', ['get', 'agents', '-o', 'json'], { stdio: 'pipe' });
36
- expect(mockConsoleLog).toHaveBeenCalledWith('agent1');
37
- expect(mockConsoleLog).toHaveBeenCalledWith('agent2');
38
- });
39
- it('lists agents in json format', async () => {
40
- const mockAgents = {
41
- items: [{ metadata: { name: 'agent1' } }],
42
- };
43
- mockExeca.mockResolvedValue({ stdout: JSON.stringify(mockAgents) });
44
- const command = createAgentsCommand({});
45
- await command.parseAsync(['node', 'test', '-o', 'json']);
46
- expect(mockConsoleLog).toHaveBeenCalledWith(JSON.stringify(mockAgents.items, null, 2));
47
- });
48
- it('shows warning when no agents', async () => {
49
- mockExeca.mockResolvedValue({ stdout: JSON.stringify({ items: [] }) });
50
- const command = createAgentsCommand({});
51
- await command.parseAsync(['node', 'test']);
52
- expect(mockOutput.warning).toHaveBeenCalledWith('no agents available');
53
- });
54
- it('handles errors', async () => {
55
- mockExeca.mockRejectedValue(new Error('kubectl failed'));
56
- const command = createAgentsCommand({});
57
- await expect(command.parseAsync(['node', 'test'])).rejects.toThrow('process.exit called');
58
- expect(mockOutput.error).toHaveBeenCalledWith('fetching agents:', 'kubectl failed');
59
- expect(mockExit).toHaveBeenCalledWith(1);
60
- });
61
- it('list subcommand works', async () => {
62
- mockExeca.mockResolvedValue({ stdout: JSON.stringify({ items: [] }) });
63
- const command = createAgentsCommand({});
64
- await command.parseAsync(['node', 'test', 'list']);
65
- expect(mockExeca).toHaveBeenCalled();
66
- });
67
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,92 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { Command } from 'commander';
3
- const mockGetClusterInfo = jest.fn();
4
- jest.unstable_mockModule('../../lib/cluster.js', () => ({
5
- getClusterInfo: mockGetClusterInfo,
6
- }));
7
- const mockOutput = {
8
- error: jest.fn(),
9
- };
10
- jest.unstable_mockModule('../../lib/output.js', () => ({
11
- default: mockOutput,
12
- }));
13
- const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {
14
- throw new Error('process.exit called');
15
- }));
16
- const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => { });
17
- const { createGetCommand } = await import('./get.js');
18
- describe('cluster get command', () => {
19
- beforeEach(() => {
20
- jest.clearAllMocks();
21
- });
22
- it('creates command with correct structure', () => {
23
- const command = createGetCommand();
24
- expect(command).toBeInstanceOf(Command);
25
- expect(command.name()).toBe('get');
26
- });
27
- it('displays cluster info in text format by default', async () => {
28
- mockGetClusterInfo.mockResolvedValue({
29
- context: 'test-cluster',
30
- namespace: 'default',
31
- type: 'minikube',
32
- ip: '192.168.1.1',
33
- });
34
- const command = createGetCommand();
35
- await command.parseAsync(['node', 'test']);
36
- expect(mockGetClusterInfo).toHaveBeenCalledWith(undefined);
37
- expect(mockConsoleLog).toHaveBeenCalledWith('context: test-cluster');
38
- expect(mockConsoleLog).toHaveBeenCalledWith('namespace: default');
39
- expect(mockConsoleLog).toHaveBeenCalledWith('type: minikube');
40
- expect(mockConsoleLog).toHaveBeenCalledWith('ip: 192.168.1.1');
41
- });
42
- it('displays cluster info in json format when requested', async () => {
43
- const clusterInfo = {
44
- context: 'prod-cluster',
45
- namespace: 'production',
46
- type: 'eks',
47
- ip: '10.0.0.1',
48
- };
49
- mockGetClusterInfo.mockResolvedValue(clusterInfo);
50
- const command = createGetCommand();
51
- await command.parseAsync(['node', 'test', '-o', 'json']);
52
- expect(mockConsoleLog).toHaveBeenCalledWith(JSON.stringify(clusterInfo, null, 2));
53
- });
54
- it('uses specified context when provided', async () => {
55
- mockGetClusterInfo.mockResolvedValue({
56
- context: 'custom-context',
57
- namespace: 'custom',
58
- type: 'kind',
59
- ip: '127.0.0.1',
60
- });
61
- const command = createGetCommand();
62
- await command.parseAsync(['node', 'test', '-c', 'custom-context']);
63
- expect(mockGetClusterInfo).toHaveBeenCalledWith('custom-context');
64
- });
65
- it('handles missing ip gracefully', async () => {
66
- mockGetClusterInfo.mockResolvedValue({
67
- context: 'test-cluster',
68
- namespace: 'default',
69
- type: 'unknown',
70
- ip: undefined,
71
- });
72
- const command = createGetCommand();
73
- await command.parseAsync(['node', 'test']);
74
- expect(mockConsoleLog).toHaveBeenCalledWith('ip: unknown');
75
- });
76
- it('exits with error when cluster info has error', async () => {
77
- mockGetClusterInfo.mockResolvedValue({
78
- error: 'No cluster found',
79
- });
80
- const command = createGetCommand();
81
- await expect(command.parseAsync(['node', 'test'])).rejects.toThrow('process.exit called');
82
- expect(mockOutput.error).toHaveBeenCalledWith('getting cluster info:', 'No cluster found');
83
- expect(mockExit).toHaveBeenCalledWith(1);
84
- });
85
- it('handles exceptions gracefully', async () => {
86
- mockGetClusterInfo.mockRejectedValue(new Error('Connection failed'));
87
- const command = createGetCommand();
88
- await expect(command.parseAsync(['node', 'test'])).rejects.toThrow('process.exit called');
89
- expect(mockOutput.error).toHaveBeenCalledWith('failed to get cluster info:', 'Connection failed');
90
- expect(mockExit).toHaveBeenCalledWith(1);
91
- });
92
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,24 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { Command } from 'commander';
3
- const mockCreateGetCommand = jest.fn();
4
- jest.unstable_mockModule('./get.js', () => ({
5
- createGetCommand: mockCreateGetCommand,
6
- }));
7
- const { createClusterCommand } = await import('./index.js');
8
- describe('cluster command', () => {
9
- beforeEach(() => {
10
- jest.clearAllMocks();
11
- mockCreateGetCommand.mockReturnValue(new Command('get'));
12
- });
13
- it('creates command with correct structure', () => {
14
- const command = createClusterCommand({});
15
- expect(command).toBeInstanceOf(Command);
16
- expect(command.name()).toBe('cluster');
17
- });
18
- it('adds get subcommand', () => {
19
- const command = createClusterCommand({});
20
- expect(mockCreateGetCommand).toHaveBeenCalled();
21
- const getCommand = command.commands.find((cmd) => cmd.name() === 'get');
22
- expect(getCommand).toBeDefined();
23
- });
24
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,34 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { Command } from 'commander';
3
- const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => { });
4
- const { createCompletionCommand } = await import('./index.js');
5
- describe('completion command', () => {
6
- beforeEach(() => {
7
- jest.clearAllMocks();
8
- });
9
- it('creates command with correct structure', () => {
10
- const command = createCompletionCommand({});
11
- expect(command).toBeInstanceOf(Command);
12
- expect(command.name()).toBe('completion');
13
- });
14
- it('shows help when called without subcommand', async () => {
15
- const command = createCompletionCommand({});
16
- await command.parseAsync(['node', 'test']);
17
- // Check first call contains the title (strip ANSI color codes)
18
- expect(mockConsoleLog.mock.calls[0][0]).toContain('Shell completion for ARK CLI');
19
- // Check that bash completion instructions are shown
20
- expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('ark completion bash'));
21
- });
22
- it('outputs bash completion script', async () => {
23
- const command = createCompletionCommand({});
24
- await command.parseAsync(['node', 'test', 'bash']);
25
- expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('_ark_completion()'));
26
- expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('COMPREPLY'));
27
- });
28
- it('outputs zsh completion script', async () => {
29
- const command = createCompletionCommand({});
30
- await command.parseAsync(['node', 'test', 'zsh']);
31
- expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('#compdef ark'));
32
- expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('_ark()'));
33
- });
34
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,78 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { Command } from 'commander';
3
- const mockLoadConfig = jest.fn();
4
- const mockGetConfigPaths = jest.fn();
5
- const mockFormatConfig = jest.fn();
6
- jest.unstable_mockModule('../../lib/config.js', () => ({
7
- loadConfig: mockLoadConfig,
8
- getConfigPaths: mockGetConfigPaths,
9
- formatConfig: mockFormatConfig,
10
- }));
11
- const mockExistsSync = jest.fn();
12
- jest.unstable_mockModule('fs', () => ({
13
- default: {
14
- existsSync: mockExistsSync,
15
- },
16
- existsSync: mockExistsSync,
17
- }));
18
- const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => { });
19
- const { createConfigCommand } = await import('./index.js');
20
- describe('config command', () => {
21
- beforeEach(() => {
22
- jest.clearAllMocks();
23
- // Reset environment variables
24
- delete process.env.ARK_CHAT_STREAMING;
25
- delete process.env.ARK_CHAT_OUTPUT_FORMAT;
26
- });
27
- it('creates command with correct structure', () => {
28
- const command = createConfigCommand({});
29
- expect(command).toBeInstanceOf(Command);
30
- expect(command.name()).toBe('config');
31
- });
32
- it('displays config paths and environment variables', async () => {
33
- const mockConfig = { defaultModel: 'test-model' };
34
- const mockPaths = {
35
- user: '/home/user/.arkrc.yaml',
36
- project: '/project/.arkrc.yaml',
37
- };
38
- mockLoadConfig.mockReturnValue(mockConfig);
39
- mockGetConfigPaths.mockReturnValue(mockPaths);
40
- mockFormatConfig.mockReturnValue('formatted config');
41
- mockExistsSync.mockReturnValue(true);
42
- const command = createConfigCommand({});
43
- await command.parseAsync(['node', 'test']);
44
- expect(mockLoadConfig).toHaveBeenCalled();
45
- expect(mockGetConfigPaths).toHaveBeenCalled();
46
- expect(mockFormatConfig).toHaveBeenCalledWith(mockConfig);
47
- expect(mockExistsSync).toHaveBeenCalledWith(mockPaths.user);
48
- expect(mockExistsSync).toHaveBeenCalledWith(mockPaths.project);
49
- });
50
- it('shows when config files do not exist', async () => {
51
- const mockPaths = {
52
- user: '/home/user/.arkrc.yaml',
53
- project: '/project/.arkrc.yaml',
54
- };
55
- mockLoadConfig.mockReturnValue({});
56
- mockGetConfigPaths.mockReturnValue(mockPaths);
57
- mockFormatConfig.mockReturnValue('');
58
- mockExistsSync.mockReturnValue(false);
59
- const command = createConfigCommand({});
60
- await command.parseAsync(['node', 'test']);
61
- expect(mockExistsSync).toHaveBeenCalledWith(mockPaths.user);
62
- expect(mockExistsSync).toHaveBeenCalledWith(mockPaths.project);
63
- // Should show that files don't exist
64
- expect(mockConsoleLog).toHaveBeenCalled();
65
- });
66
- it('displays environment variables when set', async () => {
67
- process.env.ARK_CHAT_STREAMING = 'true';
68
- process.env.ARK_CHAT_OUTPUT_FORMAT = 'json';
69
- mockLoadConfig.mockReturnValue({});
70
- mockGetConfigPaths.mockReturnValue({ user: '', project: '' });
71
- mockFormatConfig.mockReturnValue('');
72
- mockExistsSync.mockReturnValue(false);
73
- const command = createConfigCommand({});
74
- await command.parseAsync(['node', 'test']);
75
- // Should display the environment variables
76
- expect(mockConsoleLog).toHaveBeenCalled();
77
- });
78
- });
@@ -1 +0,0 @@
1
- export {};