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

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 (223) hide show
  1. package/dist/arkServices.d.ts +42 -0
  2. package/dist/arkServices.js +138 -0
  3. package/dist/arkServices.spec.d.ts +1 -0
  4. package/dist/arkServices.spec.js +24 -0
  5. package/dist/charts/charts.d.ts +5 -0
  6. package/dist/charts/charts.js +6 -0
  7. package/dist/charts/dependencies.d.ts +6 -0
  8. package/dist/charts/dependencies.js +50 -0
  9. package/dist/charts/types.d.ts +40 -0
  10. package/dist/charts/types.js +1 -0
  11. package/dist/commands/agents/index.d.ts +3 -0
  12. package/dist/commands/agents/index.js +65 -0
  13. package/dist/commands/agents/index.spec.d.ts +1 -0
  14. package/dist/commands/agents/index.spec.js +67 -0
  15. package/dist/commands/agents/selector.d.ts +8 -0
  16. package/dist/commands/agents/selector.js +53 -0
  17. package/dist/commands/agents.d.ts +2 -0
  18. package/dist/commands/agents.js +53 -0
  19. package/dist/commands/chat/index.d.ts +3 -0
  20. package/dist/commands/chat/index.js +29 -0
  21. package/dist/commands/chat.d.ts +2 -0
  22. package/dist/commands/chat.js +45 -0
  23. package/dist/commands/cluster/get.d.ts +2 -0
  24. package/dist/commands/cluster/get.js +39 -0
  25. package/dist/commands/cluster/get.spec.d.ts +1 -0
  26. package/dist/commands/cluster/get.spec.js +92 -0
  27. package/dist/commands/cluster/index.d.ts +2 -1
  28. package/dist/commands/cluster/index.js +3 -5
  29. package/dist/commands/cluster/index.spec.d.ts +1 -0
  30. package/dist/commands/cluster/index.spec.js +24 -0
  31. package/dist/commands/completion/index.d.ts +3 -0
  32. package/dist/commands/completion/index.js +230 -0
  33. package/dist/commands/completion/index.spec.d.ts +1 -0
  34. package/dist/commands/completion/index.spec.js +34 -0
  35. package/dist/commands/completion.js +159 -2
  36. package/dist/commands/config/index.d.ts +3 -0
  37. package/dist/commands/config/index.js +42 -0
  38. package/dist/commands/config/index.spec.d.ts +1 -0
  39. package/dist/commands/config/index.spec.js +78 -0
  40. package/dist/commands/config.d.ts +0 -3
  41. package/dist/commands/config.js +38 -321
  42. package/dist/commands/dashboard/index.d.ts +4 -0
  43. package/dist/commands/dashboard/index.js +39 -0
  44. package/dist/commands/dashboard.d.ts +3 -0
  45. package/dist/commands/dashboard.js +39 -0
  46. package/dist/commands/dev/index.d.ts +3 -0
  47. package/dist/commands/dev/index.js +9 -0
  48. package/dist/commands/dev/tool/check.d.ts +2 -0
  49. package/dist/commands/dev/tool/check.js +142 -0
  50. package/dist/commands/dev/tool/clean.d.ts +2 -0
  51. package/dist/commands/dev/tool/clean.js +153 -0
  52. package/dist/commands/dev/tool/generate.d.ts +2 -0
  53. package/dist/commands/dev/tool/generate.js +28 -0
  54. package/dist/commands/dev/tool/index.d.ts +2 -0
  55. package/dist/commands/dev/tool/index.js +14 -0
  56. package/dist/commands/dev/tool/init.d.ts +2 -0
  57. package/dist/commands/dev/tool/init.js +320 -0
  58. package/dist/commands/dev/tool/shared.d.ts +5 -0
  59. package/dist/commands/dev/tool/shared.js +258 -0
  60. package/dist/commands/dev/tool/status.d.ts +2 -0
  61. package/dist/commands/dev/tool/status.js +136 -0
  62. package/dist/commands/dev/tool-generate.spec.d.ts +1 -0
  63. package/dist/commands/dev/tool-generate.spec.js +163 -0
  64. package/dist/commands/dev/tool.d.ts +2 -0
  65. package/dist/commands/dev/tool.js +559 -0
  66. package/dist/commands/dev/tool.spec.d.ts +1 -0
  67. package/dist/commands/dev/tool.spec.js +48 -0
  68. package/dist/commands/docs/index.d.ts +4 -0
  69. package/dist/commands/docs/index.js +18 -0
  70. package/dist/commands/generate/config.js +5 -24
  71. package/dist/commands/generate/generators/mcpserver.d.ts +2 -1
  72. package/dist/commands/generate/generators/mcpserver.js +26 -5
  73. package/dist/commands/generate/generators/project.js +22 -41
  74. package/dist/commands/generate/index.d.ts +2 -1
  75. package/dist/commands/generate/index.js +1 -1
  76. package/dist/commands/install/index.d.ts +8 -0
  77. package/dist/commands/install/index.js +295 -0
  78. package/dist/commands/install/index.spec.d.ts +1 -0
  79. package/dist/commands/install/index.spec.js +143 -0
  80. package/dist/commands/install.d.ts +3 -0
  81. package/dist/commands/install.js +147 -0
  82. package/dist/commands/models/create.d.ts +1 -0
  83. package/dist/commands/models/create.js +213 -0
  84. package/dist/commands/models/create.spec.d.ts +1 -0
  85. package/dist/commands/models/create.spec.js +125 -0
  86. package/dist/commands/models/index.d.ts +3 -0
  87. package/dist/commands/models/index.js +75 -0
  88. package/dist/commands/models/index.spec.d.ts +1 -0
  89. package/dist/commands/models/index.spec.js +96 -0
  90. package/dist/commands/models/selector.d.ts +8 -0
  91. package/dist/commands/models/selector.js +53 -0
  92. package/dist/commands/query/index.d.ts +3 -0
  93. package/dist/commands/query/index.js +24 -0
  94. package/dist/commands/query/index.spec.d.ts +1 -0
  95. package/dist/commands/query/index.spec.js +53 -0
  96. package/dist/commands/routes/index.d.ts +3 -0
  97. package/dist/commands/routes/index.js +93 -0
  98. package/dist/commands/routes.d.ts +2 -0
  99. package/dist/commands/routes.js +101 -0
  100. package/dist/commands/status/index.d.ts +3 -0
  101. package/dist/commands/status/index.js +281 -0
  102. package/dist/commands/status.d.ts +3 -0
  103. package/dist/commands/status.js +33 -0
  104. package/dist/commands/targets/index.d.ts +3 -0
  105. package/dist/commands/targets/index.js +72 -0
  106. package/dist/commands/targets/index.spec.d.ts +1 -0
  107. package/dist/commands/targets/index.spec.js +154 -0
  108. package/dist/commands/targets.d.ts +2 -0
  109. package/dist/commands/targets.js +65 -0
  110. package/dist/commands/teams/index.d.ts +3 -0
  111. package/dist/commands/teams/index.js +64 -0
  112. package/dist/commands/teams/index.spec.d.ts +1 -0
  113. package/dist/commands/teams/index.spec.js +70 -0
  114. package/dist/commands/teams/selector.d.ts +8 -0
  115. package/dist/commands/teams/selector.js +55 -0
  116. package/dist/commands/tools/index.d.ts +3 -0
  117. package/dist/commands/tools/index.js +49 -0
  118. package/dist/commands/tools/index.spec.d.ts +1 -0
  119. package/dist/commands/tools/index.spec.js +70 -0
  120. package/dist/commands/tools/selector.d.ts +8 -0
  121. package/dist/commands/tools/selector.js +53 -0
  122. package/dist/commands/uninstall/index.d.ts +3 -0
  123. package/dist/commands/uninstall/index.js +101 -0
  124. package/dist/commands/uninstall/index.spec.d.ts +1 -0
  125. package/dist/commands/uninstall/index.spec.js +125 -0
  126. package/dist/commands/uninstall.d.ts +2 -0
  127. package/dist/commands/uninstall.js +83 -0
  128. package/dist/components/ChatUI.d.ts +16 -0
  129. package/dist/components/ChatUI.js +801 -0
  130. package/dist/components/StatusView.d.ts +10 -0
  131. package/dist/components/StatusView.js +39 -0
  132. package/dist/components/statusChecker.d.ts +14 -24
  133. package/dist/components/statusChecker.js +295 -129
  134. package/dist/config.d.ts +3 -22
  135. package/dist/config.js +10 -161
  136. package/dist/index.d.ts +1 -1
  137. package/dist/index.js +42 -42
  138. package/dist/lib/arkApiClient.d.ts +53 -0
  139. package/dist/lib/arkApiClient.js +102 -0
  140. package/dist/lib/arkApiProxy.d.ts +9 -0
  141. package/dist/lib/arkApiProxy.js +22 -0
  142. package/dist/lib/arkServiceProxy.d.ts +14 -0
  143. package/dist/lib/arkServiceProxy.js +95 -0
  144. package/dist/lib/arkStatus.d.ts +10 -0
  145. package/dist/lib/arkStatus.js +79 -0
  146. package/dist/lib/arkStatus.spec.d.ts +1 -0
  147. package/dist/lib/arkStatus.spec.js +49 -0
  148. package/dist/lib/chatClient.d.ts +33 -0
  149. package/dist/lib/chatClient.js +93 -0
  150. package/dist/lib/cluster.d.ts +2 -1
  151. package/dist/lib/cluster.js +37 -16
  152. package/dist/lib/cluster.spec.d.ts +1 -0
  153. package/dist/lib/cluster.spec.js +338 -0
  154. package/dist/lib/commandUtils.d.ts +4 -0
  155. package/dist/lib/commandUtils.js +18 -0
  156. package/dist/lib/commandUtils.test.d.ts +1 -0
  157. package/dist/lib/commandUtils.test.js +44 -0
  158. package/dist/lib/commands.d.ts +16 -0
  159. package/dist/lib/commands.js +29 -0
  160. package/dist/lib/commands.spec.d.ts +1 -0
  161. package/dist/lib/commands.spec.js +146 -0
  162. package/dist/lib/config.d.ts +26 -80
  163. package/dist/lib/config.js +70 -205
  164. package/dist/lib/config.spec.d.ts +1 -0
  165. package/dist/lib/config.spec.js +99 -0
  166. package/dist/lib/config.test.d.ts +1 -0
  167. package/dist/lib/config.test.js +93 -0
  168. package/dist/lib/consts.d.ts +0 -1
  169. package/dist/lib/consts.js +0 -2
  170. package/dist/lib/consts.spec.d.ts +1 -0
  171. package/dist/lib/consts.spec.js +15 -0
  172. package/dist/lib/dev/tools/analyzer.d.ts +30 -0
  173. package/dist/lib/dev/tools/analyzer.js +190 -0
  174. package/dist/lib/dev/tools/discover_tools.py +392 -0
  175. package/dist/lib/dev/tools/mcp-types.d.ts +28 -0
  176. package/dist/lib/dev/tools/mcp-types.js +86 -0
  177. package/dist/lib/dev/tools/types.d.ts +50 -0
  178. package/dist/lib/dev/tools/types.js +1 -0
  179. package/dist/lib/errors.js +1 -1
  180. package/dist/lib/errors.spec.d.ts +1 -0
  181. package/dist/lib/errors.spec.js +221 -0
  182. package/dist/lib/exec.d.ts +0 -4
  183. package/dist/lib/exec.js +0 -11
  184. package/dist/lib/executeQuery.d.ts +20 -0
  185. package/dist/lib/executeQuery.js +135 -0
  186. package/dist/lib/executeQuery.spec.d.ts +1 -0
  187. package/dist/lib/executeQuery.spec.js +170 -0
  188. package/dist/lib/nextSteps.d.ts +4 -0
  189. package/dist/lib/nextSteps.js +20 -0
  190. package/dist/lib/nextSteps.spec.d.ts +1 -0
  191. package/dist/lib/nextSteps.spec.js +59 -0
  192. package/dist/lib/output.d.ts +36 -0
  193. package/dist/lib/output.js +89 -0
  194. package/dist/lib/output.spec.d.ts +1 -0
  195. package/dist/lib/output.spec.js +123 -0
  196. package/dist/lib/portUtils.d.ts +8 -0
  197. package/dist/lib/portUtils.js +39 -0
  198. package/dist/lib/queryRunner.d.ts +22 -0
  199. package/dist/lib/queryRunner.js +142 -0
  200. package/dist/lib/startup.d.ts +9 -0
  201. package/dist/lib/startup.js +87 -0
  202. package/dist/lib/startup.spec.d.ts +1 -0
  203. package/dist/lib/startup.spec.js +152 -0
  204. package/dist/lib/types.d.ts +87 -3
  205. package/dist/lib/versions.d.ts +23 -0
  206. package/dist/lib/versions.js +51 -0
  207. package/dist/types/types.d.ts +40 -0
  208. package/dist/types/types.js +1 -0
  209. package/dist/ui/AgentSelector.d.ts +8 -0
  210. package/dist/ui/AgentSelector.js +53 -0
  211. package/dist/ui/MainMenu.d.ts +5 -1
  212. package/dist/ui/MainMenu.js +226 -91
  213. package/dist/ui/ModelSelector.d.ts +8 -0
  214. package/dist/ui/ModelSelector.js +53 -0
  215. package/dist/ui/TeamSelector.d.ts +8 -0
  216. package/dist/ui/TeamSelector.js +55 -0
  217. package/dist/ui/ToolSelector.d.ts +8 -0
  218. package/dist/ui/ToolSelector.js +53 -0
  219. package/dist/ui/statusFormatter.d.ts +22 -7
  220. package/dist/ui/statusFormatter.js +39 -39
  221. package/dist/ui/statusFormatter.spec.d.ts +1 -0
  222. package/dist/ui/statusFormatter.spec.js +58 -0
  223. package/package.json +16 -5
@@ -0,0 +1,53 @@
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
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ import type { ArkConfig } from '../../lib/config.js';
3
+ export declare function createQueryCommand(_: ArkConfig): Command;
@@ -0,0 +1,24 @@
1
+ import { Command } from 'commander';
2
+ import output from '../../lib/output.js';
3
+ import { executeQuery, parseTarget } from '../../lib/executeQuery.js';
4
+ export function createQueryCommand(_) {
5
+ const queryCommand = new Command('query');
6
+ queryCommand
7
+ .description('Execute a single query against a model or agent')
8
+ .argument('<target>', 'Query target (e.g., model/default, agent/my-agent)')
9
+ .argument('<message>', 'Message to send')
10
+ .action(async (target, message) => {
11
+ // Parse and validate target format
12
+ const parsed = parseTarget(target);
13
+ if (!parsed) {
14
+ output.error('Invalid target format. Use: model/name or agent/name etc');
15
+ process.exit(1);
16
+ }
17
+ await executeQuery({
18
+ targetType: parsed.type,
19
+ targetName: parsed.name,
20
+ message,
21
+ });
22
+ });
23
+ return queryCommand;
24
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,53 @@
1
+ import { jest } from '@jest/globals';
2
+ import { Command } from 'commander';
3
+ const mockExecuteQuery = jest.fn();
4
+ const mockParseTarget = jest.fn();
5
+ jest.unstable_mockModule('../../lib/executeQuery.js', () => ({
6
+ executeQuery: mockExecuteQuery,
7
+ parseTarget: mockParseTarget,
8
+ }));
9
+ const mockOutput = {
10
+ error: jest.fn(),
11
+ };
12
+ jest.unstable_mockModule('../../lib/output.js', () => ({
13
+ default: mockOutput,
14
+ }));
15
+ const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {
16
+ throw new Error('process.exit called');
17
+ }));
18
+ const { createQueryCommand } = await import('./index.js');
19
+ describe('createQueryCommand', () => {
20
+ beforeEach(() => {
21
+ jest.clearAllMocks();
22
+ });
23
+ it('should create a query command', () => {
24
+ const command = createQueryCommand({});
25
+ expect(command).toBeInstanceOf(Command);
26
+ expect(command.name()).toBe('query');
27
+ expect(command.description()).toBe('Execute a single query against a model or agent');
28
+ });
29
+ it('should parse and execute query with valid target', async () => {
30
+ mockParseTarget.mockReturnValue({
31
+ type: 'model',
32
+ name: 'default',
33
+ });
34
+ mockExecuteQuery.mockResolvedValue(undefined);
35
+ const command = createQueryCommand({});
36
+ await command.parseAsync(['node', 'test', 'model/default', 'Hello world']);
37
+ expect(mockParseTarget).toHaveBeenCalledWith('model/default');
38
+ expect(mockExecuteQuery).toHaveBeenCalledWith({
39
+ targetType: 'model',
40
+ targetName: 'default',
41
+ message: 'Hello world',
42
+ });
43
+ });
44
+ it('should error on invalid target format', async () => {
45
+ mockParseTarget.mockReturnValue(null);
46
+ const command = createQueryCommand({});
47
+ await expect(command.parseAsync(['node', 'test', 'invalid-target', 'Hello'])).rejects.toThrow('process.exit called');
48
+ expect(mockParseTarget).toHaveBeenCalledWith('invalid-target');
49
+ expect(mockExecuteQuery).not.toHaveBeenCalled();
50
+ expect(mockOutput.error).toHaveBeenCalledWith('Invalid target format. Use: model/name or agent/name etc');
51
+ expect(mockExit).toHaveBeenCalledWith(1);
52
+ });
53
+ });
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ import type { ArkConfig } from '../../lib/config.js';
3
+ export declare function createRoutesCommand(_: ArkConfig): Command;
@@ -0,0 +1,93 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { execa } from 'execa';
4
+ import output from '../../lib/output.js';
5
+ async function listRoutes() {
6
+ const namespace = 'ark-system';
7
+ const port = 8080;
8
+ const portSuffix = `:${port}`;
9
+ try {
10
+ // Check if localhost-gateway is installed
11
+ const { stdout: gatewayCheck } = await execa('kubectl', ['get', 'gateway', 'localhost-gateway', '-n', namespace], { reject: false });
12
+ if (!gatewayCheck) {
13
+ output.error("localhost-gateway not installed in namespace 'ark-system'");
14
+ output.info("run 'ark install' first");
15
+ process.exit(1);
16
+ }
17
+ // Get HTTPRoutes
18
+ const { stdout: routeOutput } = await execa('kubectl', [
19
+ 'get',
20
+ 'httproutes',
21
+ '-A',
22
+ '-o',
23
+ 'custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name,HOSTNAMES:.spec.hostnames',
24
+ '--no-headers',
25
+ ], { reject: false });
26
+ if (!routeOutput || routeOutput.trim() === '') {
27
+ console.log(chalk.white('available localhost gateway routes: 0'));
28
+ output.info('no httproutes found. install services to see routes here.');
29
+ return;
30
+ }
31
+ // Parse routes
32
+ const lines = routeOutput.trim().split('\n');
33
+ const routes = [];
34
+ lines.forEach((line) => {
35
+ const parts = line.split(/\s+/);
36
+ if (parts.length >= 3) {
37
+ const name = parts[1];
38
+ // Remove brackets and split hostnames
39
+ const hostnamesStr = parts.slice(2).join(' ').replace(/\[|\]/g, '');
40
+ const hostnames = hostnamesStr
41
+ .split(',')
42
+ .map((h) => h.trim())
43
+ .filter((h) => h && h !== '<none>');
44
+ if (hostnames.length > 0) {
45
+ routes.push({ name, hostnames });
46
+ }
47
+ }
48
+ });
49
+ // Count total routes (each hostname counts as a route)
50
+ const routeCount = routes.reduce((count, r) => count + r.hostnames.length, 0);
51
+ console.log(chalk.white(`available localhost gateway routes: ${routeCount}`));
52
+ // Check port-forward status
53
+ const { stdout: psOutput } = await execa('pgrep', ['-f', `kubectl.*port-forward.*${port}:80`], { reject: false });
54
+ const portForwardActive = !!psOutput;
55
+ if (portForwardActive) {
56
+ output.info(`port-forward active on localhost${portSuffix}`);
57
+ }
58
+ else {
59
+ output.error(`port-forward not running on localhost${portSuffix} - routes are not exposed`);
60
+ console.log(chalk.blue('run:'), `kubectl port-forward -n ${namespace} service/localhost-gateway-nginx ${port}:80 > /dev/null 2>&1 &`);
61
+ }
62
+ console.log();
63
+ // Display routes
64
+ if (routes.length > 0) {
65
+ const maxLength = Math.max(...routes.map((r) => r.name.length));
66
+ routes.forEach((route) => {
67
+ route.hostnames.forEach((hostname) => {
68
+ const url = `http://${hostname}${portSuffix}/`;
69
+ const padding = ' '.repeat(maxLength - route.name.length);
70
+ if (portForwardActive) {
71
+ console.log(` ${route.name}${padding}: ${chalk.blue(url)}`);
72
+ }
73
+ else {
74
+ console.log(` ${route.name}${padding}: ${chalk.blue(url)} ${chalk.red('(unavailable)')}`);
75
+ }
76
+ });
77
+ });
78
+ }
79
+ }
80
+ catch (error) {
81
+ output.error('failed to fetch routes:', error instanceof Error ? error.message : 'Unknown error');
82
+ process.exit(1);
83
+ }
84
+ }
85
+ export function createRoutesCommand(_) {
86
+ const command = new Command('routes');
87
+ command
88
+ .description('show available gateway routes and their urls')
89
+ .action(async () => {
90
+ await listRoutes();
91
+ });
92
+ return command;
93
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createRoutesCommand(): Command;
@@ -0,0 +1,101 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { execa } from 'execa';
4
+ import output from '../lib/output.js';
5
+ import { isCommandAvailable } from '../lib/commandUtils.js';
6
+ async function listRoutes() {
7
+ // Check if kubectl is installed
8
+ const kubectlInstalled = await isCommandAvailable('kubectl');
9
+ if (!kubectlInstalled) {
10
+ output.error('kubectl is not installed. please install kubectl first:');
11
+ output.info('https://kubernetes.io/docs/tasks/tools/');
12
+ process.exit(1);
13
+ }
14
+ const namespace = 'ark-system';
15
+ const port = 8080;
16
+ const portSuffix = `:${port}`;
17
+ try {
18
+ // Check if localhost-gateway is installed
19
+ const { stdout: gatewayCheck } = await execa('kubectl', ['get', 'gateway', 'localhost-gateway', '-n', namespace], { reject: false });
20
+ if (!gatewayCheck) {
21
+ output.error("localhost-gateway not installed in namespace 'ark-system'");
22
+ output.info("run 'ark install' first");
23
+ process.exit(1);
24
+ }
25
+ // Get HTTPRoutes
26
+ const { stdout: routeOutput } = await execa('kubectl', [
27
+ 'get',
28
+ 'httproutes',
29
+ '-A',
30
+ '-o',
31
+ 'custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name,HOSTNAMES:.spec.hostnames',
32
+ '--no-headers',
33
+ ], { reject: false });
34
+ if (!routeOutput || routeOutput.trim() === '') {
35
+ console.log(chalk.white('available localhost gateway routes: 0'));
36
+ output.info('no httproutes found. install services to see routes here.');
37
+ return;
38
+ }
39
+ // Parse routes
40
+ const lines = routeOutput.trim().split('\n');
41
+ const routes = [];
42
+ lines.forEach((line) => {
43
+ const parts = line.split(/\s+/);
44
+ if (parts.length >= 3) {
45
+ const name = parts[1];
46
+ // Remove brackets and split hostnames
47
+ const hostnamesStr = parts.slice(2).join(' ').replace(/\[|\]/g, '');
48
+ const hostnames = hostnamesStr
49
+ .split(',')
50
+ .map((h) => h.trim())
51
+ .filter((h) => h && h !== '<none>');
52
+ if (hostnames.length > 0) {
53
+ routes.push({ name, hostnames });
54
+ }
55
+ }
56
+ });
57
+ // Count total routes (each hostname counts as a route)
58
+ const routeCount = routes.reduce((count, r) => count + r.hostnames.length, 0);
59
+ console.log(chalk.white(`available localhost gateway routes: ${routeCount}`));
60
+ // Check port-forward status
61
+ const { stdout: psOutput } = await execa('pgrep', ['-f', `kubectl.*port-forward.*${port}:80`], { reject: false });
62
+ const portForwardActive = !!psOutput;
63
+ if (portForwardActive) {
64
+ output.info(`port-forward active on localhost${portSuffix}`);
65
+ }
66
+ else {
67
+ output.error(`port-forward not running on localhost${portSuffix} - routes are not exposed`);
68
+ console.log(chalk.blue('run:'), `kubectl port-forward -n ${namespace} service/localhost-gateway-nginx ${port}:80 > /dev/null 2>&1 &`);
69
+ }
70
+ console.log();
71
+ // Display routes
72
+ if (routes.length > 0) {
73
+ const maxLength = Math.max(...routes.map((r) => r.name.length));
74
+ routes.forEach((route) => {
75
+ route.hostnames.forEach((hostname) => {
76
+ const url = `http://${hostname}${portSuffix}/`;
77
+ const padding = ' '.repeat(maxLength - route.name.length);
78
+ if (portForwardActive) {
79
+ console.log(` ${route.name}${padding}: ${chalk.blue(url)}`);
80
+ }
81
+ else {
82
+ console.log(` ${route.name}${padding}: ${chalk.blue(url)} ${chalk.red('(unavailable)')}`);
83
+ }
84
+ });
85
+ });
86
+ }
87
+ }
88
+ catch (error) {
89
+ output.error('failed to fetch routes:', error instanceof Error ? error.message : 'Unknown error');
90
+ process.exit(1);
91
+ }
92
+ }
93
+ export function createRoutesCommand() {
94
+ const command = new Command('routes');
95
+ command
96
+ .description('show available gateway routes and their urls')
97
+ .action(async () => {
98
+ await listRoutes();
99
+ });
100
+ return command;
101
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function checkStatus(): Promise<void>;
3
+ export declare function createStatusCommand(): Command;
@@ -0,0 +1,281 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { StatusChecker } from '../../components/statusChecker.js';
5
+ import { StatusFormatter, } from '../../ui/statusFormatter.js';
6
+ import { fetchVersionInfo } from '../../lib/versions.js';
7
+ /**
8
+ * Enrich service with formatted details including version/revision
9
+ */
10
+ function enrichServiceDetails(service) {
11
+ const statusMap = {
12
+ healthy: { icon: '✓', text: 'healthy', color: 'green' },
13
+ unhealthy: { icon: '✗', text: 'unhealthy', color: 'red' },
14
+ warning: { icon: '⚠', text: 'warning', color: 'yellow' },
15
+ 'not ready': { icon: '○', text: 'not ready', color: 'yellow' },
16
+ 'not installed': { icon: '?', text: 'not installed', color: 'yellow' },
17
+ };
18
+ const statusInfo = statusMap[service.status] || {
19
+ icon: '?',
20
+ text: service.status,
21
+ color: 'yellow',
22
+ };
23
+ // Build details array
24
+ const details = [];
25
+ if (service.status === 'healthy') {
26
+ if (service.version)
27
+ details.push(service.version);
28
+ if (service.revision)
29
+ details.push(`revision ${service.revision}`);
30
+ }
31
+ if (service.details)
32
+ details.push(service.details);
33
+ // Build display name with formatting
34
+ let displayName = chalk.bold(service.name);
35
+ if (service.namespace) {
36
+ displayName += ` ${chalk.blue(service.namespace)}`;
37
+ }
38
+ if (service.isDev) {
39
+ displayName += ' (dev)';
40
+ }
41
+ return {
42
+ statusInfo,
43
+ displayName,
44
+ details: details.join(', '),
45
+ };
46
+ }
47
+ function buildStatusSections(data, versionInfo) {
48
+ const sections = [];
49
+ // Dependencies section
50
+ sections.push({
51
+ title: 'system dependencies:',
52
+ lines: data.dependencies.map((dep) => ({
53
+ icon: dep.installed ? '✓' : '✗',
54
+ iconColor: (dep.installed ? 'green' : 'red'),
55
+ status: dep.installed ? 'installed' : 'missing',
56
+ statusColor: (dep.installed ? 'green' : 'red'),
57
+ name: chalk.bold(dep.name),
58
+ details: dep.version || '',
59
+ subtext: dep.installed ? undefined : dep.details,
60
+ })),
61
+ });
62
+ // Cluster access section
63
+ const clusterLines = [];
64
+ if (data.clusterAccess) {
65
+ const contextName = data.clusterInfo?.context || 'kubernetes cluster';
66
+ const namespace = data.clusterInfo?.namespace || 'default';
67
+ // Add bold context name with blue namespace
68
+ const name = `${chalk.bold(contextName)} ${chalk.blue(namespace)}`;
69
+ const details = [];
70
+ if (data.clusterInfo?.type && data.clusterInfo.type !== 'unknown') {
71
+ details.push(data.clusterInfo.type);
72
+ }
73
+ if (data.clusterInfo?.ip) {
74
+ details.push(data.clusterInfo.ip);
75
+ }
76
+ clusterLines.push({
77
+ icon: '✓',
78
+ iconColor: 'green',
79
+ status: 'accessible',
80
+ statusColor: 'green',
81
+ name,
82
+ details: details.join(', '),
83
+ });
84
+ }
85
+ else {
86
+ clusterLines.push({
87
+ icon: '✗',
88
+ iconColor: 'red',
89
+ status: 'unreachable',
90
+ statusColor: 'red',
91
+ name: 'kubernetes cluster',
92
+ subtext: 'Install minikube: https://minikube.sigs.k8s.io/docs/start',
93
+ });
94
+ }
95
+ sections.push({ title: 'cluster access:', lines: clusterLines });
96
+ // Ark services section
97
+ if (data.clusterAccess) {
98
+ const serviceLines = data.services
99
+ .filter((s) => s.name !== 'ark-controller')
100
+ .map((service) => {
101
+ const { statusInfo, displayName, details } = enrichServiceDetails(service);
102
+ return {
103
+ icon: statusInfo.icon,
104
+ iconColor: statusInfo.color,
105
+ status: statusInfo.text,
106
+ statusColor: statusInfo.color,
107
+ name: displayName,
108
+ details: details,
109
+ };
110
+ });
111
+ sections.push({ title: 'ark services:', lines: serviceLines });
112
+ }
113
+ else {
114
+ sections.push({
115
+ title: 'ark services:',
116
+ lines: [
117
+ {
118
+ icon: '',
119
+ status: '',
120
+ name: 'Cannot check ARK services - cluster not accessible',
121
+ },
122
+ ],
123
+ });
124
+ }
125
+ // Ark status section
126
+ const arkStatusLines = [];
127
+ if (!data.clusterAccess) {
128
+ arkStatusLines.push({
129
+ icon: '✗',
130
+ iconColor: 'red',
131
+ status: 'no cluster access',
132
+ statusColor: 'red',
133
+ name: '',
134
+ });
135
+ }
136
+ else {
137
+ const controller = data.services?.find((s) => s.name === 'ark-controller');
138
+ if (!controller) {
139
+ arkStatusLines.push({
140
+ icon: '○',
141
+ iconColor: 'yellow',
142
+ status: 'not ready',
143
+ statusColor: 'yellow',
144
+ name: 'ark-controller',
145
+ });
146
+ }
147
+ else {
148
+ const { statusInfo, displayName, details } = enrichServiceDetails(controller);
149
+ // Map service status to ark status display
150
+ const statusText = controller.status === 'healthy'
151
+ ? 'ready'
152
+ : controller.status === 'not installed'
153
+ ? 'not ready'
154
+ : controller.status;
155
+ arkStatusLines.push({
156
+ icon: statusInfo.icon,
157
+ iconColor: statusInfo.color,
158
+ status: statusText,
159
+ statusColor: statusInfo.color,
160
+ name: displayName,
161
+ details: details,
162
+ });
163
+ // Add version update status as separate line
164
+ if (controller.status === 'healthy' && versionInfo) {
165
+ const currentVersion = versionInfo.current || controller.version;
166
+ if (!currentVersion) {
167
+ // Version is unknown
168
+ arkStatusLines.push({
169
+ icon: '?',
170
+ iconColor: 'yellow',
171
+ status: 'version unknown',
172
+ statusColor: 'yellow',
173
+ name: '',
174
+ details: versionInfo.latest
175
+ ? `latest: ${versionInfo.latest}`
176
+ : 'unable to determine version',
177
+ });
178
+ }
179
+ else if (versionInfo.latest === undefined) {
180
+ // Have current version but couldn't check for updates
181
+ arkStatusLines.push({
182
+ icon: '?',
183
+ iconColor: 'yellow',
184
+ status: `version ${currentVersion}`,
185
+ statusColor: 'yellow',
186
+ name: '',
187
+ details: 'unable to check for updates',
188
+ });
189
+ }
190
+ else {
191
+ // Have both current and latest versions
192
+ if (currentVersion === versionInfo.latest) {
193
+ arkStatusLines.push({
194
+ icon: '✓',
195
+ iconColor: 'green',
196
+ status: 'up to date',
197
+ statusColor: 'green',
198
+ name: '',
199
+ details: versionInfo.latest,
200
+ });
201
+ }
202
+ else {
203
+ arkStatusLines.push({
204
+ icon: '↑',
205
+ iconColor: 'yellow',
206
+ status: 'update available',
207
+ statusColor: 'yellow',
208
+ name: '',
209
+ details: `${currentVersion} → ${versionInfo.latest}`,
210
+ });
211
+ }
212
+ }
213
+ }
214
+ // Add default model status
215
+ if (data.defaultModel) {
216
+ if (!data.defaultModel.exists) {
217
+ arkStatusLines.push({
218
+ icon: '○',
219
+ iconColor: 'yellow',
220
+ status: 'default model',
221
+ statusColor: 'yellow',
222
+ name: '',
223
+ details: 'not configured',
224
+ });
225
+ }
226
+ else if (data.defaultModel.available) {
227
+ arkStatusLines.push({
228
+ icon: '●',
229
+ iconColor: 'green',
230
+ status: 'default model',
231
+ statusColor: 'green',
232
+ name: '',
233
+ details: data.defaultModel.provider || 'configured',
234
+ });
235
+ }
236
+ else {
237
+ arkStatusLines.push({
238
+ icon: '●',
239
+ iconColor: 'yellow',
240
+ status: 'default model',
241
+ statusColor: 'yellow',
242
+ name: '',
243
+ details: 'not available',
244
+ });
245
+ }
246
+ }
247
+ }
248
+ }
249
+ sections.push({ title: 'ark status:', lines: arkStatusLines });
250
+ return sections;
251
+ }
252
+ export async function checkStatus() {
253
+ const spinner = ora('Checking system status').start();
254
+ try {
255
+ spinner.text = 'Checking system dependencies';
256
+ const statusChecker = new StatusChecker();
257
+ spinner.text = 'Testing cluster access';
258
+ spinner.text = 'Checking ARK services';
259
+ // Run status check and version fetch in parallel
260
+ const [statusData, versionInfo] = await Promise.all([
261
+ statusChecker.checkAll(),
262
+ fetchVersionInfo(),
263
+ ]);
264
+ spinner.stop();
265
+ const sections = buildStatusSections(statusData, versionInfo);
266
+ StatusFormatter.printSections(sections);
267
+ process.exit(0);
268
+ }
269
+ catch (error) {
270
+ spinner.fail('Failed to check status');
271
+ console.error(chalk.red('Error:'), error);
272
+ process.exit(1);
273
+ }
274
+ }
275
+ export function createStatusCommand() {
276
+ const statusCommand = new Command('status');
277
+ statusCommand
278
+ .description('Check ARK system status')
279
+ .action(() => checkStatus());
280
+ return statusCommand;
281
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function checkStatus(): Promise<void>;
3
+ export declare function createStatusCommand(): Command;
@@ -0,0 +1,33 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { StatusChecker } from '../components/statusChecker.js';
5
+ import { ConfigManager } from '../config.js';
6
+ import { ArkClient } from '../lib/arkClient.js';
7
+ import { StatusFormatter } from '../ui/statusFormatter.js';
8
+ export async function checkStatus() {
9
+ const spinner = ora('Checking system status').start();
10
+ try {
11
+ const configManager = new ConfigManager();
12
+ spinner.text = 'Checking system dependencies';
13
+ const apiBaseUrl = await configManager.getApiBaseUrl();
14
+ const arkClient = new ArkClient(apiBaseUrl);
15
+ const statusChecker = new StatusChecker(arkClient);
16
+ spinner.text = 'Testing cluster access';
17
+ spinner.text = 'Checking ARK services';
18
+ const statusData = await statusChecker.checkAll();
19
+ spinner.stop();
20
+ StatusFormatter.printStatus(statusData);
21
+ process.exit(0);
22
+ }
23
+ catch (error) {
24
+ spinner.fail('Failed to check status');
25
+ console.error(chalk.red('Error:'), error);
26
+ process.exit(1);
27
+ }
28
+ }
29
+ export function createStatusCommand() {
30
+ const statusCommand = new Command('status');
31
+ statusCommand.description('Check ARK system status').action(checkStatus);
32
+ return statusCommand;
33
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ import type { ArkConfig } from '../../lib/config.js';
3
+ export declare function createTargetsCommand(_: ArkConfig): Command;