@agents-at-scale/ark 0.1.42 → 0.1.43

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 (36) hide show
  1. package/dist/arkServices.js +0 -9
  2. package/dist/commands/completion/index.js +38 -3
  3. package/dist/commands/evaluation/index.spec.js +1 -6
  4. package/dist/commands/generate/generators/team.js +4 -1
  5. package/dist/commands/install/index.js +27 -0
  6. package/dist/commands/marketplace/index.d.ts +4 -0
  7. package/dist/commands/marketplace/index.js +50 -0
  8. package/dist/commands/models/create.js +1 -1
  9. package/dist/commands/models/create.spec.js +6 -2
  10. package/dist/commands/models/providers/azure.spec.js +3 -1
  11. package/dist/commands/queries/delete.d.ts +7 -0
  12. package/dist/commands/queries/delete.js +24 -0
  13. package/dist/commands/queries/delete.spec.d.ts +1 -0
  14. package/dist/commands/queries/delete.spec.js +74 -0
  15. package/dist/commands/queries/index.js +42 -4
  16. package/dist/commands/queries/list.d.ts +6 -0
  17. package/dist/commands/queries/list.js +66 -0
  18. package/dist/commands/queries/list.spec.d.ts +1 -0
  19. package/dist/commands/queries/list.spec.js +170 -0
  20. package/dist/commands/queries/validation.d.ts +2 -0
  21. package/dist/commands/queries/validation.js +10 -0
  22. package/dist/commands/queries/validation.spec.d.ts +1 -0
  23. package/dist/commands/queries/validation.spec.js +27 -0
  24. package/dist/commands/uninstall/index.js +27 -0
  25. package/dist/components/ChatUI.js +2 -2
  26. package/dist/index.js +2 -0
  27. package/dist/lib/arkApiClient.js +2 -0
  28. package/dist/lib/executeQuery.d.ts +0 -4
  29. package/dist/lib/executeQuery.js +98 -104
  30. package/dist/lib/executeQuery.spec.js +176 -99
  31. package/dist/lib/kubectl.d.ts +7 -0
  32. package/dist/lib/kubectl.js +27 -0
  33. package/dist/lib/kubectl.spec.js +89 -1
  34. package/dist/marketplaceServices.d.ts +15 -0
  35. package/dist/marketplaceServices.js +51 -0
  36. package/package.json +1 -1
@@ -0,0 +1,170 @@
1
+ import { jest } from '@jest/globals';
2
+ import { UNSUPPORTED_OUTPUT_FORMAT_MESSAGE } from './validation.js';
3
+ import output from '../../lib/output.js';
4
+ const mockExeca = jest.fn();
5
+ jest.unstable_mockModule('execa', () => ({
6
+ execa: mockExeca,
7
+ }));
8
+ const { createQueriesCommand } = await import('./index.js');
9
+ describe('queries list command', () => {
10
+ beforeEach(() => {
11
+ jest.clearAllMocks();
12
+ console.log = jest.fn();
13
+ jest.spyOn(output, 'warning').mockImplementation(() => { });
14
+ jest.spyOn(output, 'error').mockImplementation(() => { });
15
+ jest.spyOn(process, 'exit').mockImplementation(() => undefined);
16
+ });
17
+ it('should list all queries in text format by default', async () => {
18
+ const mockQueries = [
19
+ {
20
+ metadata: {
21
+ name: 'query-1',
22
+ creationTimestamp: '2024-01-01T00:00:00Z',
23
+ },
24
+ status: {
25
+ phase: 'done',
26
+ },
27
+ },
28
+ {
29
+ metadata: {
30
+ name: 'query-2',
31
+ creationTimestamp: '2024-01-02T00:00:00Z',
32
+ },
33
+ status: {
34
+ phase: 'running',
35
+ },
36
+ },
37
+ ];
38
+ mockExeca.mockResolvedValue({
39
+ stdout: JSON.stringify({ items: mockQueries }),
40
+ });
41
+ const command = createQueriesCommand({});
42
+ await command.parseAsync(['node', 'test', 'list']);
43
+ expect(console.log).toHaveBeenCalledWith(expect.stringMatching(/NAME.*STATUS/));
44
+ expect(console.log).toHaveBeenCalledWith(expect.stringMatching(/query-1/));
45
+ expect(console.log).toHaveBeenCalledWith(expect.stringMatching(/query-2/));
46
+ expect(mockExeca).toHaveBeenCalledWith('kubectl', ['get', 'queries', '-o', 'json'], { stdio: 'pipe' });
47
+ });
48
+ it('should list all queries in JSON format', async () => {
49
+ const mockQueries = [
50
+ {
51
+ metadata: {
52
+ name: 'query-1',
53
+ creationTimestamp: '2024-01-01T00:00:00Z',
54
+ },
55
+ },
56
+ {
57
+ metadata: {
58
+ name: 'query-2',
59
+ creationTimestamp: '2024-01-02T00:00:00Z',
60
+ },
61
+ },
62
+ ];
63
+ mockExeca.mockResolvedValue({
64
+ stdout: JSON.stringify({ items: mockQueries }),
65
+ });
66
+ const command = createQueriesCommand({});
67
+ await command.parseAsync(['node', 'test', '--output', 'json']);
68
+ expect(console.log).toHaveBeenCalledWith(JSON.stringify(mockQueries, null, 2));
69
+ expect(mockExeca).toHaveBeenCalledWith('kubectl', ['get', 'queries', '-o', 'json'], { stdio: 'pipe' });
70
+ });
71
+ it('should support sorting by creation timestamp', async () => {
72
+ const mockQueries = [
73
+ {
74
+ metadata: {
75
+ name: 'query-1',
76
+ creationTimestamp: '2024-01-01T00:00:00Z',
77
+ },
78
+ status: {
79
+ phase: 'done',
80
+ },
81
+ },
82
+ {
83
+ metadata: {
84
+ name: 'query-2',
85
+ creationTimestamp: '2024-01-02T00:00:00Z',
86
+ },
87
+ status: {
88
+ phase: 'running',
89
+ },
90
+ },
91
+ ];
92
+ mockExeca.mockResolvedValue({
93
+ stdout: JSON.stringify({ items: mockQueries }),
94
+ });
95
+ const command = createQueriesCommand({});
96
+ await command.parseAsync([
97
+ 'node',
98
+ 'test',
99
+ '--sort-by',
100
+ '.metadata.creationTimestamp',
101
+ ]);
102
+ expect(console.log).toHaveBeenCalledWith(expect.stringMatching(/NAME.*STATUS/));
103
+ expect(console.log).toHaveBeenCalledWith(expect.stringMatching(/query-1/));
104
+ expect(console.log).toHaveBeenCalledWith(expect.stringMatching(/query-2/));
105
+ expect(mockExeca).toHaveBeenCalledWith('kubectl', ['get', 'queries', '--sort-by=.metadata.creationTimestamp', '-o', 'json'], { stdio: 'pipe' });
106
+ });
107
+ it('should display warning when no queries exist', async () => {
108
+ mockExeca.mockResolvedValue({
109
+ stdout: JSON.stringify({ items: [] }),
110
+ });
111
+ const command = createQueriesCommand({});
112
+ await command.parseAsync(['node', 'test', 'list']);
113
+ expect(output.warning).toHaveBeenCalledWith('no queries available');
114
+ expect(mockExeca).toHaveBeenCalledWith('kubectl', ['get', 'queries', '-o', 'json'], { stdio: 'pipe' });
115
+ });
116
+ it('should handle errors when listing queries', async () => {
117
+ mockExeca.mockRejectedValue(new Error('kubectl connection failed'));
118
+ const command = createQueriesCommand({});
119
+ await command.parseAsync(['node', 'test', 'list']);
120
+ expect(output.error).toHaveBeenCalled();
121
+ expect(process.exit).toHaveBeenCalled();
122
+ });
123
+ it('should handle invalid output format gracefully', async () => {
124
+ const mockQueries = [
125
+ {
126
+ metadata: {
127
+ name: 'query-1',
128
+ creationTimestamp: '2024-01-01T00:00:00Z',
129
+ },
130
+ status: {
131
+ phase: 'done',
132
+ },
133
+ },
134
+ ];
135
+ mockExeca.mockResolvedValue({
136
+ stdout: JSON.stringify({ items: mockQueries }),
137
+ });
138
+ const command = createQueriesCommand({});
139
+ await command.parseAsync(['node', 'test', '--output', 'xml']);
140
+ expect(output.error).toHaveBeenCalledWith(expect.anything(), expect.stringMatching(UNSUPPORTED_OUTPUT_FORMAT_MESSAGE));
141
+ expect(mockExeca).not.toHaveBeenCalled();
142
+ expect(console.log).not.toHaveBeenCalledWith(expect.stringMatching(/query-1/));
143
+ expect(process.exit).toHaveBeenCalled();
144
+ });
145
+ it('should list many queries without truncation', async () => {
146
+ // Create 100 mock queries
147
+ const mockQueries = Array.from({ length: 100 }, (_, i) => ({
148
+ metadata: {
149
+ name: `query-${i + 1}`,
150
+ creationTimestamp: new Date(2024, 0, i + 1).toISOString(),
151
+ },
152
+ status: {
153
+ phase: i % 3 === 0 ? 'done' : i % 2 === 0 ? 'running' : 'initializing',
154
+ },
155
+ }));
156
+ mockExeca.mockResolvedValue({
157
+ stdout: JSON.stringify({ items: mockQueries }),
158
+ });
159
+ const command = createQueriesCommand({});
160
+ await command.parseAsync(['node', 'test', 'list']);
161
+ // Check for header and separator
162
+ expect(console.log).toHaveBeenCalledWith(expect.stringMatching(/NAME.*STATUS/));
163
+ // Verify all queries are logged
164
+ for (let i = 1; i <= 100; i++) {
165
+ expect(console.log).toHaveBeenCalledWith(expect.stringMatching(new RegExp(`query-${i}`)));
166
+ }
167
+ // Verify console.log was called: header + 100 queries
168
+ expect(console.log).toHaveBeenCalledTimes(101);
169
+ });
170
+ });
@@ -0,0 +1,2 @@
1
+ export declare const UNSUPPORTED_OUTPUT_FORMAT_MESSAGE = "unsupported \"output\" format";
2
+ export declare function assertSupportedOutputFormat(format: string | undefined): void;
@@ -0,0 +1,10 @@
1
+ import { InvalidArgumentError } from 'commander';
2
+ const SUPPORTED_OUTPUT_FORMATS = ['json', 'text'];
3
+ export const UNSUPPORTED_OUTPUT_FORMAT_MESSAGE = `unsupported "output" format`;
4
+ const VALID_OUTPUT_FORMATS_MESSAGE = `valid formats are: ${SUPPORTED_OUTPUT_FORMATS.join(', ')}`;
5
+ export function assertSupportedOutputFormat(format) {
6
+ if (format && !SUPPORTED_OUTPUT_FORMATS.includes(format)) {
7
+ const message = `${UNSUPPORTED_OUTPUT_FORMAT_MESSAGE}: "${format}". ${VALID_OUTPUT_FORMATS_MESSAGE}`;
8
+ throw new InvalidArgumentError(message);
9
+ }
10
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ import { jest } from '@jest/globals';
2
+ import { InvalidArgumentError } from 'commander';
3
+ import { assertSupportedOutputFormat, UNSUPPORTED_OUTPUT_FORMAT_MESSAGE, } from './validation.js';
4
+ jest.spyOn(console, 'error').mockImplementation(() => { });
5
+ describe('queries validation', () => {
6
+ describe('assertSupportedOutputFormat', () => {
7
+ it('should not throw for supported formats', () => {
8
+ expect(() => assertSupportedOutputFormat('json')).not.toThrow();
9
+ expect(() => assertSupportedOutputFormat('text')).not.toThrow();
10
+ });
11
+ it('should not throw when format is undefined', () => {
12
+ expect(() => assertSupportedOutputFormat(undefined)).not.toThrow();
13
+ });
14
+ it('should throw InvalidArgumentError for unsupported format', () => {
15
+ expect(() => assertSupportedOutputFormat('xml')).toThrow(InvalidArgumentError);
16
+ });
17
+ it('should include format and supported formats in error message', () => {
18
+ expect(() => assertSupportedOutputFormat('xml')).toThrow(UNSUPPORTED_OUTPUT_FORMAT_MESSAGE);
19
+ });
20
+ it('should work with various invalid formats', () => {
21
+ const invalidFormats = ['yaml', 'csv', 'html', 'pdf'];
22
+ for (const format of invalidFormats) {
23
+ expect(() => assertSupportedOutputFormat(format)).toThrow(InvalidArgumentError);
24
+ }
25
+ });
26
+ });
27
+ });
@@ -5,6 +5,7 @@ import inquirer from 'inquirer';
5
5
  import { showNoClusterError } from '../../lib/startup.js';
6
6
  import output from '../../lib/output.js';
7
7
  import { getInstallableServices } from '../../arkServices.js';
8
+ import { isMarketplaceService, extractMarketplaceServiceName, getMarketplaceService, getAllMarketplaceServices, } from '../../marketplaceServices.js';
8
9
  async function uninstallService(service, verbose = false) {
9
10
  const helmArgs = ['uninstall', service.helmReleaseName, '--ignore-not-found'];
10
11
  // Only add namespace flag if service has explicit namespace
@@ -25,6 +26,32 @@ async function uninstallArk(config, serviceName, options = {}) {
25
26
  console.log(); // Add blank line after cluster info
26
27
  // If a specific service is requested, uninstall only that service
27
28
  if (serviceName) {
29
+ // Check if it's a marketplace service
30
+ if (isMarketplaceService(serviceName)) {
31
+ const marketplaceServiceName = extractMarketplaceServiceName(serviceName);
32
+ const service = getMarketplaceService(marketplaceServiceName);
33
+ if (!service) {
34
+ output.error(`marketplace service '${marketplaceServiceName}' not found`);
35
+ output.info('available marketplace services:');
36
+ const marketplaceServices = getAllMarketplaceServices();
37
+ for (const serviceName of Object.keys(marketplaceServices)) {
38
+ output.info(` marketplace/services/${serviceName}`);
39
+ }
40
+ process.exit(1);
41
+ }
42
+ output.info(`uninstalling marketplace service ${service.name}...`);
43
+ try {
44
+ await uninstallService(service, options.verbose);
45
+ output.success(`${service.name} uninstalled successfully`);
46
+ }
47
+ catch (error) {
48
+ output.error(`failed to uninstall ${service.name}`);
49
+ console.error(error);
50
+ process.exit(1);
51
+ }
52
+ return;
53
+ }
54
+ // Core ARK service
28
55
  const services = getInstallableServices();
29
56
  const service = Object.values(services).find((s) => s.name === serviceName);
30
57
  if (!service) {
@@ -18,12 +18,12 @@ const generateMessageId = () => {
18
18
  // Configure marked with terminal renderer for markdown output
19
19
  const configureMarkdown = () => {
20
20
  marked.setOptions({
21
- // @ts-expect-error - TerminalRenderer types are incomplete
21
+ // @ts-ignore - TerminalRenderer types are incomplete
22
22
  renderer: new TerminalRenderer({
23
23
  showSectionPrefix: false,
24
24
  width: 80,
25
25
  reflowText: true,
26
- // @ts-expect-error - preserveNewlines exists but not in types
26
+ // @ts-ignore - preserveNewlines exists but not in types
27
27
  preserveNewlines: true,
28
28
  }),
29
29
  });
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ import { createDocsCommand } from './commands/docs/index.js';
16
16
  import { createEvaluationCommand } from './commands/evaluation/index.js';
17
17
  import { createGenerateCommand } from './commands/generate/index.js';
18
18
  import { createInstallCommand } from './commands/install/index.js';
19
+ import { createMarketplaceCommand } from './commands/marketplace/index.js';
19
20
  import { createMemoryCommand } from './commands/memory/index.js';
20
21
  import { createModelsCommand } from './commands/models/index.js';
21
22
  import { createQueryCommand } from './commands/query/index.js';
@@ -49,6 +50,7 @@ async function main() {
49
50
  program.addCommand(createEvaluationCommand(config));
50
51
  program.addCommand(createGenerateCommand(config));
51
52
  program.addCommand(createInstallCommand(config));
53
+ program.addCommand(createMarketplaceCommand(config));
52
54
  program.addCommand(createMemoryCommand(config));
53
55
  program.addCommand(createModelsCommand(config));
54
56
  program.addCommand(createQueryCommand(config));
@@ -146,6 +146,8 @@ export class ArkApiClient {
146
146
  }));
147
147
  }
148
148
  async *createChatCompletionStream(params) {
149
+ // Errors from OpenAI SDK will automatically propagate with proper error messages
150
+ // and kill the CLI, so no try/catch needed here
149
151
  const stream = await this.openai.chat.completions.create({
150
152
  ...params,
151
153
  stream: true,
@@ -11,10 +11,6 @@ export interface QueryOptions {
11
11
  verbose?: boolean;
12
12
  outputFormat?: string;
13
13
  }
14
- /**
15
- * Execute a query against any ARK target (model, agent, team)
16
- * This is the shared implementation used by all query commands
17
- */
18
14
  export declare function executeQuery(options: QueryOptions): Promise<void>;
19
15
  /**
20
16
  * Parse a target string like "model/default" or "agent/weather"
@@ -4,24 +4,91 @@
4
4
  import { execa } from 'execa';
5
5
  import ora from 'ora';
6
6
  import chalk from 'chalk';
7
- import output from './output.js';
8
7
  import { ExitCodes } from './errors.js';
9
- import { parseDuration } from './duration.js';
10
- import { getResource } from './kubectl.js';
11
- /**
12
- * Execute a query against any ARK target (model, agent, team)
13
- * This is the shared implementation used by all query commands
14
- */
8
+ import { ArkApiProxy } from './arkApiProxy.js';
9
+ import { ChatClient } from './chatClient.js';
15
10
  export async function executeQuery(options) {
16
- const spinner = options.outputFormat
17
- ? null
18
- : ora('Creating query...').start();
19
- const queryTimeoutMs = options.timeout
20
- ? parseDuration(options.timeout)
21
- : parseDuration('5m');
22
- const watchTimeoutMs = options.watchTimeout
23
- ? parseDuration(options.watchTimeout)
24
- : queryTimeoutMs + 60000;
11
+ if (options.outputFormat) {
12
+ return executeQueryWithFormat(options);
13
+ }
14
+ let arkApiProxy;
15
+ const spinner = ora('Connecting to Ark API...').start();
16
+ try {
17
+ arkApiProxy = new ArkApiProxy();
18
+ const arkApiClient = await arkApiProxy.start();
19
+ const chatClient = new ChatClient(arkApiClient);
20
+ spinner.text = 'Executing query...';
21
+ const targetId = `${options.targetType}/${options.targetName}`;
22
+ const messages = [{ role: 'user', content: options.message }];
23
+ const state = {
24
+ toolCalls: new Map(),
25
+ content: '',
26
+ };
27
+ let lastAgentName;
28
+ let headerShown = false;
29
+ let firstOutput = true;
30
+ await chatClient.sendMessage(targetId, messages, { streamingEnabled: true }, (chunk, toolCalls, arkMetadata) => {
31
+ if (firstOutput) {
32
+ spinner.stop();
33
+ firstOutput = false;
34
+ }
35
+ const agentName = arkMetadata?.agent || arkMetadata?.team;
36
+ if (agentName && agentName !== lastAgentName) {
37
+ if (lastAgentName) {
38
+ if (state.content) {
39
+ process.stdout.write('\n');
40
+ }
41
+ process.stdout.write('\n');
42
+ }
43
+ const prefix = arkMetadata?.team ? '◆' : '●';
44
+ const color = arkMetadata?.team ? 'green' : 'cyan';
45
+ process.stdout.write(chalk[color](`${prefix} ${agentName}\n`));
46
+ lastAgentName = agentName;
47
+ state.content = '';
48
+ state.toolCalls.clear();
49
+ headerShown = true;
50
+ }
51
+ if (toolCalls && toolCalls.length > 0) {
52
+ for (const toolCall of toolCalls) {
53
+ if (!state.toolCalls.has(toolCall.id)) {
54
+ state.toolCalls.set(toolCall.id, toolCall);
55
+ if (state.content) {
56
+ process.stdout.write('\n');
57
+ }
58
+ process.stdout.write(chalk.magenta(`🔧 ${toolCall.function.name}\n`));
59
+ }
60
+ }
61
+ }
62
+ if (chunk) {
63
+ if (state.toolCalls.size > 0 && !state.content) {
64
+ process.stdout.write('\n');
65
+ }
66
+ process.stdout.write(chunk);
67
+ state.content += chunk;
68
+ }
69
+ });
70
+ if (spinner.isSpinning) {
71
+ spinner.stop();
72
+ }
73
+ if ((state.content || state.toolCalls.size > 0) && headerShown) {
74
+ process.stdout.write('\n');
75
+ }
76
+ if (arkApiProxy) {
77
+ arkApiProxy.stop();
78
+ }
79
+ }
80
+ catch (error) {
81
+ if (spinner.isSpinning) {
82
+ spinner.stop();
83
+ }
84
+ if (arkApiProxy) {
85
+ arkApiProxy.stop();
86
+ }
87
+ console.error(chalk.red(error instanceof Error ? error.message : 'Unknown error'));
88
+ process.exit(ExitCodes.CliError);
89
+ }
90
+ }
91
+ async function executeQueryWithFormat(options) {
25
92
  const timestamp = Date.now();
26
93
  const queryName = `cli-query-${timestamp}`;
27
94
  const queryManifest = {
@@ -42,104 +109,31 @@ export async function executeQuery(options) {
42
109
  },
43
110
  };
44
111
  try {
45
- // Apply the query
46
- if (spinner)
47
- spinner.text = 'Submitting query...';
48
112
  await execa('kubectl', ['apply', '-f', '-'], {
49
113
  input: JSON.stringify(queryManifest),
50
114
  stdio: ['pipe', 'pipe', 'pipe'],
51
115
  });
52
- // Watch for query completion using kubectl wait
53
- if (spinner)
54
- spinner.text = 'Waiting for query completion...';
55
- try {
56
- await execa('kubectl', [
57
- 'wait',
58
- '--for=condition=Completed',
59
- `query/${queryName}`,
60
- `--timeout=${Math.floor(watchTimeoutMs / 1000)}s`,
61
- ], { timeout: watchTimeoutMs });
62
- }
63
- catch (error) {
64
- if (spinner)
65
- spinner.stop();
66
- // Check if it's a timeout or other error
67
- if (error instanceof Error &&
68
- error.message.includes('timed out waiting')) {
69
- console.error(chalk.red(`Query did not complete within ${options.watchTimeout ?? `${Math.floor(watchTimeoutMs / 1000)}s`}`));
70
- process.exit(ExitCodes.Timeout);
71
- }
72
- // For other errors, fetch the query to check status
73
- }
74
- if (spinner)
75
- spinner.stop();
76
- // If output format is specified, output the resource and return
77
- if (options.outputFormat) {
78
- try {
79
- if (options.outputFormat === 'name') {
80
- console.log(queryName);
81
- }
82
- else if (options.outputFormat === 'json' ||
83
- options.outputFormat === 'yaml') {
84
- const { stdout } = await execa('kubectl', ['get', 'query', queryName, '-o', options.outputFormat], { stdio: 'pipe' });
85
- console.log(stdout);
86
- }
87
- else {
88
- console.error(chalk.red(`Invalid output format: ${options.outputFormat}. Use: yaml, json, or name`));
89
- process.exit(ExitCodes.CliError);
90
- }
91
- return;
92
- }
93
- catch (error) {
94
- console.error(chalk.red(error instanceof Error
95
- ? error.message
96
- : 'Failed to fetch query resource'));
97
- process.exit(ExitCodes.CliError);
98
- }
116
+ const timeoutSeconds = 300;
117
+ await execa('kubectl', [
118
+ 'wait',
119
+ '--for=condition=Completed',
120
+ `query/${queryName}`,
121
+ `--timeout=${timeoutSeconds}s`,
122
+ ], { timeout: timeoutSeconds * 1000 });
123
+ if (options.outputFormat === 'name') {
124
+ console.log(queryName);
99
125
  }
100
- // Fetch final query state
101
- try {
102
- const query = await getResource('queries', queryName);
103
- const phase = query.status?.phase;
104
- // Check if query completed successfully or with error
105
- if (phase === 'done') {
106
- // Extract and display the response from responses array
107
- if (query.status?.responses && query.status.responses.length > 0) {
108
- const response = query.status.responses[0];
109
- console.log(response.content || response);
110
- }
111
- else {
112
- output.warning('No response received');
113
- }
114
- }
115
- else if (phase === 'error') {
116
- const response = query.status?.responses?.[0];
117
- console.error(chalk.red(response?.content || 'Query failed with unknown error'));
118
- process.exit(ExitCodes.OperationError);
119
- }
120
- else if (phase === 'canceled') {
121
- if (spinner) {
122
- spinner.warn('Query canceled');
123
- }
124
- else {
125
- output.warning('Query canceled');
126
- }
127
- if (query.status?.message) {
128
- output.warning(query.status.message);
129
- }
130
- process.exit(ExitCodes.OperationError);
131
- }
126
+ else if (options.outputFormat === 'json' ||
127
+ options.outputFormat === 'yaml') {
128
+ const { stdout } = await execa('kubectl', ['get', 'query', queryName, '-o', options.outputFormat], { stdio: 'pipe' });
129
+ console.log(stdout);
132
130
  }
133
- catch (error) {
134
- console.error(chalk.red(error instanceof Error
135
- ? error.message
136
- : 'Failed to fetch query result'));
131
+ else {
132
+ console.error(chalk.red(`Invalid output format: ${options.outputFormat}. Use: yaml, json, or name`));
137
133
  process.exit(ExitCodes.CliError);
138
134
  }
139
135
  }
140
136
  catch (error) {
141
- if (spinner)
142
- spinner.stop();
143
137
  console.error(chalk.red(error instanceof Error ? error.message : 'Unknown error'));
144
138
  process.exit(ExitCodes.CliError);
145
139
  }