@agents-at-scale/ark 0.1.46 → 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.
- package/.arkrc.template.yaml +51 -0
- package/README.md +4 -0
- package/dist/arkServices.js +22 -8
- package/dist/arkServices.spec.js +6 -0
- package/dist/commands/agents/index.d.ts +1 -1
- package/dist/commands/agents/index.js +4 -2
- package/dist/commands/chat/index.js +1 -1
- package/dist/commands/completion/index.js +19 -5
- package/dist/commands/dashboard/index.d.ts +2 -2
- package/dist/commands/dashboard/index.js +5 -4
- package/dist/commands/export/index.d.ts +3 -0
- package/dist/commands/export/index.js +73 -0
- package/dist/commands/export/index.spec.d.ts +1 -0
- package/dist/commands/export/index.spec.js +145 -0
- package/dist/commands/import/index.d.ts +3 -0
- package/dist/commands/import/index.js +27 -0
- package/dist/commands/import/index.spec.d.ts +1 -0
- package/dist/commands/import/index.spec.js +46 -0
- package/dist/commands/install/index.js +20 -10
- package/dist/commands/marketplace/index.js +51 -23
- package/dist/commands/marketplace/index.spec.d.ts +1 -0
- package/dist/commands/marketplace/index.spec.js +88 -0
- package/dist/commands/memory/index.js +9 -4
- package/dist/commands/models/index.d.ts +1 -1
- package/dist/commands/models/index.js +4 -2
- package/dist/commands/query/index.d.ts +1 -1
- package/dist/commands/query/index.js +6 -2
- package/dist/commands/status/index.js +7 -2
- package/dist/commands/teams/index.d.ts +1 -1
- package/dist/commands/teams/index.js +4 -2
- package/dist/commands/uninstall/index.js +20 -10
- package/dist/index.js +4 -0
- package/dist/lib/arkApiProxy.d.ts +1 -1
- package/dist/lib/arkApiProxy.js +2 -2
- package/dist/lib/arkServiceProxy.d.ts +3 -1
- package/dist/lib/arkServiceProxy.js +34 -1
- package/dist/lib/arkServiceProxy.spec.d.ts +1 -0
- package/dist/lib/arkServiceProxy.spec.js +100 -0
- package/dist/lib/chatClient.d.ts +2 -0
- package/dist/lib/chatClient.js +10 -2
- package/dist/lib/config.d.ts +17 -1
- package/dist/lib/config.js +62 -7
- package/dist/lib/config.spec.js +103 -0
- package/dist/lib/constants.d.ts +3 -0
- package/dist/lib/constants.js +5 -0
- package/dist/lib/executeQuery.d.ts +1 -0
- package/dist/lib/executeQuery.js +21 -4
- package/dist/lib/executeQuery.spec.js +4 -1
- package/dist/lib/kubectl.d.ts +3 -0
- package/dist/lib/kubectl.js +68 -0
- package/dist/lib/kubectl.spec.js +16 -0
- package/dist/lib/marketplaceFetcher.d.ts +6 -0
- package/dist/lib/marketplaceFetcher.js +80 -0
- package/dist/lib/marketplaceFetcher.spec.d.ts +1 -0
- package/dist/lib/marketplaceFetcher.spec.js +225 -0
- package/dist/lib/types.d.ts +1 -0
- package/dist/marketplaceServices.d.ts +15 -6
- package/dist/marketplaceServices.js +38 -40
- package/dist/marketplaceServices.spec.d.ts +1 -0
- package/dist/marketplaceServices.spec.js +74 -0
- package/dist/types/marketplace.d.ts +37 -0
- package/dist/types/marketplace.js +1 -0
- package/dist/ui/MainMenu.js +6 -2
- package/package.json +4 -2
- package/templates/marketplace/marketplace.json.example +59 -0
package/dist/lib/config.d.ts
CHANGED
|
@@ -4,11 +4,19 @@ export interface ChatConfig {
|
|
|
4
4
|
streaming?: boolean;
|
|
5
5
|
outputFormat?: 'text' | 'markdown';
|
|
6
6
|
}
|
|
7
|
+
export interface MarketplaceConfig {
|
|
8
|
+
repoUrl?: string;
|
|
9
|
+
registry?: string;
|
|
10
|
+
}
|
|
7
11
|
export interface ArkConfig {
|
|
8
12
|
chat?: ChatConfig;
|
|
13
|
+
marketplace?: MarketplaceConfig;
|
|
9
14
|
services?: {
|
|
10
|
-
|
|
15
|
+
reusePortForwards?: boolean;
|
|
16
|
+
[serviceName: string]: Partial<ArkService> | boolean | undefined;
|
|
11
17
|
};
|
|
18
|
+
queryTimeout?: string;
|
|
19
|
+
defaultExportTypes?: string[];
|
|
12
20
|
clusterInfo?: ClusterInfo;
|
|
13
21
|
}
|
|
14
22
|
/**
|
|
@@ -30,3 +38,11 @@ export declare function getConfigPaths(): {
|
|
|
30
38
|
* Format config as YAML for display
|
|
31
39
|
*/
|
|
32
40
|
export declare function formatConfig(config: ArkConfig): string;
|
|
41
|
+
/**
|
|
42
|
+
* Get marketplace repository URL from config
|
|
43
|
+
*/
|
|
44
|
+
export declare function getMarketplaceRepoUrl(): string;
|
|
45
|
+
/**
|
|
46
|
+
* Get marketplace registry from config
|
|
47
|
+
*/
|
|
48
|
+
export declare function getMarketplaceRegistry(): string;
|
package/dist/lib/config.js
CHANGED
|
@@ -16,6 +16,13 @@ export function loadConfig() {
|
|
|
16
16
|
streaming: true,
|
|
17
17
|
outputFormat: 'text',
|
|
18
18
|
},
|
|
19
|
+
marketplace: {
|
|
20
|
+
repoUrl: 'https://github.com/mckinsey/agents-at-scale-marketplace',
|
|
21
|
+
registry: 'oci://ghcr.io/mckinsey/agents-at-scale-marketplace/charts',
|
|
22
|
+
},
|
|
23
|
+
services: {
|
|
24
|
+
reusePortForwards: false,
|
|
25
|
+
},
|
|
19
26
|
};
|
|
20
27
|
// Load user config from home directory
|
|
21
28
|
const userConfigPath = path.join(os.homedir(), '.arkrc.yaml');
|
|
@@ -44,9 +51,7 @@ export function loadConfig() {
|
|
|
44
51
|
// Apply environment variable overrides
|
|
45
52
|
if (process.env.ARK_CHAT_STREAMING !== undefined) {
|
|
46
53
|
config.chat = config.chat || {};
|
|
47
|
-
config.chat.streaming =
|
|
48
|
-
process.env.ARK_CHAT_STREAMING === '1' ||
|
|
49
|
-
process.env.ARK_CHAT_STREAMING === 'true';
|
|
54
|
+
config.chat.streaming = process.env.ARK_CHAT_STREAMING === '1';
|
|
50
55
|
}
|
|
51
56
|
if (process.env.ARK_CHAT_OUTPUT_FORMAT !== undefined) {
|
|
52
57
|
config.chat = config.chat || {};
|
|
@@ -55,6 +60,22 @@ export function loadConfig() {
|
|
|
55
60
|
config.chat.outputFormat = format;
|
|
56
61
|
}
|
|
57
62
|
}
|
|
63
|
+
if (process.env.ARK_QUERY_TIMEOUT !== undefined) {
|
|
64
|
+
config.queryTimeout = process.env.ARK_QUERY_TIMEOUT;
|
|
65
|
+
}
|
|
66
|
+
if (process.env.ARK_MARKETPLACE_REPO_URL !== undefined) {
|
|
67
|
+
config.marketplace = config.marketplace || {};
|
|
68
|
+
config.marketplace.repoUrl = process.env.ARK_MARKETPLACE_REPO_URL;
|
|
69
|
+
}
|
|
70
|
+
if (process.env.ARK_MARKETPLACE_REGISTRY !== undefined) {
|
|
71
|
+
config.marketplace = config.marketplace || {};
|
|
72
|
+
config.marketplace.registry = process.env.ARK_MARKETPLACE_REGISTRY;
|
|
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
|
+
}
|
|
58
79
|
return config;
|
|
59
80
|
}
|
|
60
81
|
/**
|
|
@@ -70,15 +91,35 @@ function mergeConfig(target, source) {
|
|
|
70
91
|
target.chat.outputFormat = source.chat.outputFormat;
|
|
71
92
|
}
|
|
72
93
|
}
|
|
94
|
+
if (source.marketplace) {
|
|
95
|
+
target.marketplace = target.marketplace || {};
|
|
96
|
+
if (source.marketplace.repoUrl !== undefined) {
|
|
97
|
+
target.marketplace.repoUrl = source.marketplace.repoUrl;
|
|
98
|
+
}
|
|
99
|
+
if (source.marketplace.registry !== undefined) {
|
|
100
|
+
target.marketplace.registry = source.marketplace.registry;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
73
103
|
if (source.services) {
|
|
74
104
|
target.services = target.services || {};
|
|
105
|
+
if (source.services.reusePortForwards !== undefined) {
|
|
106
|
+
target.services.reusePortForwards = source.services.reusePortForwards;
|
|
107
|
+
}
|
|
75
108
|
for (const [serviceName, overrides] of Object.entries(source.services)) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
109
|
+
if (serviceName !== 'reusePortForwards' && typeof overrides === 'object') {
|
|
110
|
+
target.services[serviceName] = {
|
|
111
|
+
...target.services[serviceName],
|
|
112
|
+
...overrides,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
80
115
|
}
|
|
81
116
|
}
|
|
117
|
+
if (source.queryTimeout !== undefined) {
|
|
118
|
+
target.queryTimeout = source.queryTimeout;
|
|
119
|
+
}
|
|
120
|
+
if (source.defaultExportTypes) {
|
|
121
|
+
target.defaultExportTypes = source.defaultExportTypes;
|
|
122
|
+
}
|
|
82
123
|
}
|
|
83
124
|
/**
|
|
84
125
|
* Get the paths checked for config files
|
|
@@ -95,3 +136,17 @@ export function getConfigPaths() {
|
|
|
95
136
|
export function formatConfig(config) {
|
|
96
137
|
return yaml.stringify(config);
|
|
97
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Get marketplace repository URL from config
|
|
141
|
+
*/
|
|
142
|
+
export function getMarketplaceRepoUrl() {
|
|
143
|
+
const config = loadConfig();
|
|
144
|
+
return config.marketplace.repoUrl;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get marketplace registry from config
|
|
148
|
+
*/
|
|
149
|
+
export function getMarketplaceRegistry() {
|
|
150
|
+
const config = loadConfig();
|
|
151
|
+
return config.marketplace.registry;
|
|
152
|
+
}
|
package/dist/lib/config.spec.js
CHANGED
|
@@ -35,6 +35,13 @@ describe('config', () => {
|
|
|
35
35
|
streaming: true,
|
|
36
36
|
outputFormat: 'text',
|
|
37
37
|
},
|
|
38
|
+
marketplace: {
|
|
39
|
+
repoUrl: 'https://github.com/mckinsey/agents-at-scale-marketplace',
|
|
40
|
+
registry: 'oci://ghcr.io/mckinsey/agents-at-scale-marketplace/charts',
|
|
41
|
+
},
|
|
42
|
+
services: {
|
|
43
|
+
reusePortForwards: false,
|
|
44
|
+
},
|
|
38
45
|
});
|
|
39
46
|
});
|
|
40
47
|
it('loads and merges configs in order: defaults, user, project', () => {
|
|
@@ -66,6 +73,28 @@ describe('config', () => {
|
|
|
66
73
|
expect(config.chat?.streaming).toBe(true);
|
|
67
74
|
expect(config.chat?.outputFormat).toBe('markdown');
|
|
68
75
|
});
|
|
76
|
+
it('loads queryTimeout from config file', () => {
|
|
77
|
+
mockFs.existsSync.mockReturnValue(true);
|
|
78
|
+
mockFs.readFileSync.mockReturnValue('yaml');
|
|
79
|
+
mockYaml.parse.mockReturnValue({ queryTimeout: '30m' });
|
|
80
|
+
const config = loadConfig();
|
|
81
|
+
expect(config.queryTimeout).toBe('30m');
|
|
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
|
+
});
|
|
90
|
+
it('ARK_QUERY_TIMEOUT environment variable overrides config', () => {
|
|
91
|
+
mockFs.existsSync.mockReturnValue(true);
|
|
92
|
+
mockFs.readFileSync.mockReturnValue('yaml');
|
|
93
|
+
mockYaml.parse.mockReturnValue({ queryTimeout: '5m' });
|
|
94
|
+
process.env.ARK_QUERY_TIMEOUT = '1h';
|
|
95
|
+
const config = loadConfig();
|
|
96
|
+
expect(config.queryTimeout).toBe('1h');
|
|
97
|
+
});
|
|
69
98
|
it('throws error for invalid YAML', () => {
|
|
70
99
|
const userConfigPath = path.join(os.homedir(), '.arkrc.yaml');
|
|
71
100
|
mockFs.existsSync.mockImplementation((path) => path === userConfigPath);
|
|
@@ -96,4 +125,78 @@ describe('config', () => {
|
|
|
96
125
|
expect(mockYaml.stringify).toHaveBeenCalledWith(config);
|
|
97
126
|
expect(result).toBe('formatted');
|
|
98
127
|
});
|
|
128
|
+
it('loads marketplace config from config file', () => {
|
|
129
|
+
mockFs.existsSync.mockReturnValue(true);
|
|
130
|
+
mockFs.readFileSync.mockReturnValue('yaml');
|
|
131
|
+
mockYaml.parse.mockReturnValue({
|
|
132
|
+
marketplace: {
|
|
133
|
+
repoUrl: 'https://example.com/my-marketplace',
|
|
134
|
+
registry: 'oci://example.com/charts',
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
const config = loadConfig();
|
|
138
|
+
expect(config.marketplace?.repoUrl).toBe('https://example.com/my-marketplace');
|
|
139
|
+
expect(config.marketplace?.registry).toBe('oci://example.com/charts');
|
|
140
|
+
});
|
|
141
|
+
it('marketplace environment variables override config', () => {
|
|
142
|
+
mockFs.existsSync.mockReturnValue(true);
|
|
143
|
+
mockFs.readFileSync.mockReturnValue('yaml');
|
|
144
|
+
mockYaml.parse.mockReturnValue({
|
|
145
|
+
marketplace: {
|
|
146
|
+
repoUrl: 'https://example.com/my-marketplace',
|
|
147
|
+
registry: 'oci://example.com/charts',
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
process.env.ARK_MARKETPLACE_REPO_URL = 'https://custom.com/marketplace';
|
|
151
|
+
process.env.ARK_MARKETPLACE_REGISTRY = 'oci://custom.com/charts';
|
|
152
|
+
const config = loadConfig();
|
|
153
|
+
expect(config.marketplace?.repoUrl).toBe('https://custom.com/marketplace');
|
|
154
|
+
expect(config.marketplace?.registry).toBe('oci://custom.com/charts');
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
describe('marketplace helpers', () => {
|
|
158
|
+
const originalEnv = process.env;
|
|
159
|
+
beforeEach(() => {
|
|
160
|
+
jest.clearAllMocks();
|
|
161
|
+
process.env = { ...originalEnv };
|
|
162
|
+
});
|
|
163
|
+
afterEach(() => {
|
|
164
|
+
process.env = originalEnv;
|
|
165
|
+
});
|
|
166
|
+
it('getMarketplaceRepoUrl returns custom config value', async () => {
|
|
167
|
+
mockFs.existsSync.mockReturnValue(true);
|
|
168
|
+
mockFs.readFileSync.mockReturnValue('yaml');
|
|
169
|
+
mockYaml.parse.mockReturnValue({
|
|
170
|
+
marketplace: {
|
|
171
|
+
repoUrl: 'https://custom-repo.com/marketplace',
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
jest.resetModules();
|
|
175
|
+
const { getMarketplaceRepoUrl } = await import('./config.js');
|
|
176
|
+
expect(getMarketplaceRepoUrl()).toBe('https://custom-repo.com/marketplace');
|
|
177
|
+
});
|
|
178
|
+
it('getMarketplaceRepoUrl returns default value', async () => {
|
|
179
|
+
mockFs.existsSync.mockReturnValue(false);
|
|
180
|
+
jest.resetModules();
|
|
181
|
+
const { getMarketplaceRepoUrl } = await import('./config.js');
|
|
182
|
+
expect(getMarketplaceRepoUrl()).toBe('https://github.com/mckinsey/agents-at-scale-marketplace');
|
|
183
|
+
});
|
|
184
|
+
it('getMarketplaceRegistry returns custom config value', async () => {
|
|
185
|
+
mockFs.existsSync.mockReturnValue(true);
|
|
186
|
+
mockFs.readFileSync.mockReturnValue('yaml');
|
|
187
|
+
mockYaml.parse.mockReturnValue({
|
|
188
|
+
marketplace: {
|
|
189
|
+
registry: 'oci://custom-registry.com/charts',
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
jest.resetModules();
|
|
193
|
+
const { getMarketplaceRegistry } = await import('./config.js');
|
|
194
|
+
expect(getMarketplaceRegistry()).toBe('oci://custom-registry.com/charts');
|
|
195
|
+
});
|
|
196
|
+
it('getMarketplaceRegistry returns default value', async () => {
|
|
197
|
+
mockFs.existsSync.mockReturnValue(false);
|
|
198
|
+
jest.resetModules();
|
|
199
|
+
const { getMarketplaceRegistry } = await import('./config.js');
|
|
200
|
+
expect(getMarketplaceRegistry()).toBe('oci://ghcr.io/mckinsey/agents-at-scale-marketplace/charts');
|
|
201
|
+
});
|
|
99
202
|
});
|
package/dist/lib/constants.d.ts
CHANGED
package/dist/lib/constants.js
CHANGED
|
@@ -6,3 +6,8 @@ export const QUERY_ANNOTATIONS = {
|
|
|
6
6
|
// A2A context ID annotation (goes to K8s annotations)
|
|
7
7
|
A2A_CONTEXT_ID: `${ARK_PREFIX}a2a-context-id`,
|
|
8
8
|
};
|
|
9
|
+
// Event annotation constants
|
|
10
|
+
export const EVENT_ANNOTATIONS = {
|
|
11
|
+
// Event data annotation for structured event data
|
|
12
|
+
EVENT_DATA: `${ARK_PREFIX}event-data`,
|
|
13
|
+
};
|
package/dist/lib/executeQuery.js
CHANGED
|
@@ -7,6 +7,8 @@ import chalk from 'chalk';
|
|
|
7
7
|
import { ExitCodes } from './errors.js';
|
|
8
8
|
import { ArkApiProxy } from './arkApiProxy.js';
|
|
9
9
|
import { ChatClient } from './chatClient.js';
|
|
10
|
+
import { watchEventsLive } from './kubectl.js';
|
|
11
|
+
import { loadConfig } from './config.js';
|
|
10
12
|
export async function executeQuery(options) {
|
|
11
13
|
if (options.outputFormat) {
|
|
12
14
|
return executeQueryWithFormat(options);
|
|
@@ -14,7 +16,8 @@ export async function executeQuery(options) {
|
|
|
14
16
|
let arkApiProxy;
|
|
15
17
|
const spinner = ora('Connecting to Ark API...').start();
|
|
16
18
|
try {
|
|
17
|
-
|
|
19
|
+
const config = loadConfig();
|
|
20
|
+
arkApiProxy = new ArkApiProxy(undefined, config.services?.reusePortForwards ?? false);
|
|
18
21
|
const arkApiClient = await arkApiProxy.start();
|
|
19
22
|
const chatClient = new ChatClient(arkApiClient);
|
|
20
23
|
spinner.text = 'Executing query...';
|
|
@@ -27,9 +30,14 @@ export async function executeQuery(options) {
|
|
|
27
30
|
let lastAgentName;
|
|
28
31
|
let headerShown = false;
|
|
29
32
|
let firstOutput = true;
|
|
30
|
-
// Get sessionId from option or environment variable
|
|
31
33
|
const sessionId = options.sessionId || process.env.ARK_SESSION_ID;
|
|
32
|
-
|
|
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,
|
|
@@ -118,6 +129,12 @@ async function executeQueryWithFormat(options) {
|
|
|
118
129
|
input: JSON.stringify(queryManifest),
|
|
119
130
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
120
131
|
});
|
|
132
|
+
// Give Kubernetes a moment to process the resource before watching
|
|
133
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
134
|
+
if (options.outputFormat === 'events') {
|
|
135
|
+
await watchEventsLive(queryName);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
121
138
|
const timeoutSeconds = 300;
|
|
122
139
|
await execa('kubectl', [
|
|
123
140
|
'wait',
|
|
@@ -134,7 +151,7 @@ async function executeQueryWithFormat(options) {
|
|
|
134
151
|
console.log(stdout);
|
|
135
152
|
}
|
|
136
153
|
else {
|
|
137
|
-
console.error(chalk.red(`Invalid output format: ${options.outputFormat}. Use: yaml, json, or
|
|
154
|
+
console.error(chalk.red(`Invalid output format: ${options.outputFormat}. Use: yaml, json, name, or events`));
|
|
138
155
|
process.exit(ExitCodes.CliError);
|
|
139
156
|
}
|
|
140
157
|
}
|
|
@@ -206,9 +206,12 @@ describe('executeQuery', () => {
|
|
|
206
206
|
expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringMatching(/cli-query-\d+/));
|
|
207
207
|
});
|
|
208
208
|
it('should include sessionId in query manifest when outputFormat is specified', async () => {
|
|
209
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
209
210
|
let appliedManifest = '';
|
|
210
211
|
mockExeca.mockImplementation(async (command, args) => {
|
|
211
|
-
if (args.includes('apply') &&
|
|
212
|
+
if (args.includes('apply') &&
|
|
213
|
+
args.includes('-f') &&
|
|
214
|
+
args.includes('-')) {
|
|
212
215
|
// Capture the stdin input
|
|
213
216
|
const stdinIndex = args.indexOf('-');
|
|
214
217
|
if (stdinIndex >= 0 && args[stdinIndex + 1]) {
|
package/dist/lib/kubectl.d.ts
CHANGED
|
@@ -6,10 +6,13 @@ 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?: {
|
|
12
14
|
all?: boolean;
|
|
13
15
|
}): Promise<void>;
|
|
14
16
|
export declare function replaceResource<T extends K8sResource>(resource: T): Promise<T>;
|
|
17
|
+
export declare function watchEventsLive(queryName: string): Promise<void>;
|
|
15
18
|
export {};
|
package/dist/lib/kubectl.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { execa } from 'execa';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { EVENT_ANNOTATIONS } from './constants.js';
|
|
2
4
|
export async function getResource(resourceType, name) {
|
|
3
5
|
if (name === '@latest') {
|
|
4
6
|
const result = await execa('kubectl', [
|
|
@@ -23,6 +25,12 @@ export async function listResources(resourceType, options) {
|
|
|
23
25
|
if (options?.sortBy) {
|
|
24
26
|
args.push(`--sort-by=${options.sortBy}`);
|
|
25
27
|
}
|
|
28
|
+
if (options?.namespace) {
|
|
29
|
+
args.push('-n', options.namespace);
|
|
30
|
+
}
|
|
31
|
+
if (options?.labels) {
|
|
32
|
+
args.push('-l', options.labels);
|
|
33
|
+
}
|
|
26
34
|
args.push('-o', 'json');
|
|
27
35
|
const result = await execa('kubectl', args, { stdio: 'pipe' });
|
|
28
36
|
const data = JSON.parse(result.stdout);
|
|
@@ -45,3 +53,63 @@ export async function replaceResource(resource) {
|
|
|
45
53
|
});
|
|
46
54
|
return JSON.parse(result.stdout);
|
|
47
55
|
}
|
|
56
|
+
export async function watchEventsLive(queryName) {
|
|
57
|
+
const seenEvents = new Set();
|
|
58
|
+
const pollEvents = async () => {
|
|
59
|
+
try {
|
|
60
|
+
const { stdout } = await execa('kubectl', [
|
|
61
|
+
'get',
|
|
62
|
+
'events',
|
|
63
|
+
'--field-selector',
|
|
64
|
+
`involvedObject.name=${queryName}`,
|
|
65
|
+
'-o',
|
|
66
|
+
'json',
|
|
67
|
+
]);
|
|
68
|
+
const eventsData = JSON.parse(stdout);
|
|
69
|
+
for (const event of eventsData.items || []) {
|
|
70
|
+
const eventId = event.metadata?.uid;
|
|
71
|
+
if (eventId && !seenEvents.has(eventId)) {
|
|
72
|
+
seenEvents.add(eventId);
|
|
73
|
+
const annotations = event.metadata?.annotations || {};
|
|
74
|
+
const eventData = annotations[EVENT_ANNOTATIONS.EVENT_DATA];
|
|
75
|
+
if (eventData) {
|
|
76
|
+
const now = new Date();
|
|
77
|
+
const hours = now.getHours().toString().padStart(2, '0');
|
|
78
|
+
const minutes = now.getMinutes().toString().padStart(2, '0');
|
|
79
|
+
const seconds = now.getSeconds().toString().padStart(2, '0');
|
|
80
|
+
const millis = now.getMilliseconds().toString().padStart(3, '0');
|
|
81
|
+
const timestamp = `${hours}:${minutes}:${seconds}.${millis}`;
|
|
82
|
+
const reason = event.reason || 'Unknown';
|
|
83
|
+
const eventType = event.type || 'Normal';
|
|
84
|
+
const colorCode = eventType === 'Normal' ? 32 : eventType === 'Warning' ? 33 : 31;
|
|
85
|
+
console.log(`${timestamp} \x1b[${colorCode}m${reason}\x1b[0m ${eventData}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// eslint-disable-next-line no-empty, @typescript-eslint/no-unused-vars
|
|
90
|
+
}
|
|
91
|
+
catch (error) { }
|
|
92
|
+
};
|
|
93
|
+
const pollInterval = setInterval(pollEvents, 200);
|
|
94
|
+
const timeoutSeconds = 300;
|
|
95
|
+
const waitProcess = execa('kubectl', [
|
|
96
|
+
'wait',
|
|
97
|
+
'--for=condition=Completed',
|
|
98
|
+
`query/${queryName}`,
|
|
99
|
+
`--timeout=${timeoutSeconds}s`,
|
|
100
|
+
], {
|
|
101
|
+
timeout: timeoutSeconds * 1000,
|
|
102
|
+
});
|
|
103
|
+
try {
|
|
104
|
+
await waitProcess;
|
|
105
|
+
await pollEvents();
|
|
106
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
107
|
+
await pollEvents();
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
console.error(chalk.red('Query wait failed:', error instanceof Error ? error.message : 'Unknown error'));
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
clearInterval(pollInterval);
|
|
114
|
+
}
|
|
115
|
+
}
|
package/dist/lib/kubectl.spec.js
CHANGED
|
@@ -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');
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ArkService, ServiceCollection } from '../types/arkService.js';
|
|
2
|
+
import type { AnthropicMarketplaceManifest, AnthropicMarketplaceItem } from '../types/marketplace.js';
|
|
3
|
+
export declare function fetchMarketplaceManifest(): Promise<AnthropicMarketplaceManifest | null>;
|
|
4
|
+
export declare function mapMarketplaceItemToArkService(item: AnthropicMarketplaceItem, registry?: string): ArkService;
|
|
5
|
+
export declare function getMarketplaceServicesFromManifest(): Promise<ServiceCollection | null>;
|
|
6
|
+
export declare function getMarketplaceAgentsFromManifest(): Promise<ServiceCollection | null>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { getMarketplaceRepoUrl, getMarketplaceRegistry } from './config.js';
|
|
3
|
+
export async function fetchMarketplaceManifest() {
|
|
4
|
+
const repoUrl = getMarketplaceRepoUrl();
|
|
5
|
+
const manifestUrl = `${repoUrl}/raw/main/marketplace.json`;
|
|
6
|
+
try {
|
|
7
|
+
const response = await axios.get(manifestUrl, {
|
|
8
|
+
timeout: 10000,
|
|
9
|
+
headers: {
|
|
10
|
+
Accept: 'application/json',
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
return response.data;
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (axios.isAxiosError(error)) {
|
|
17
|
+
if (error.code === 'ENOTFOUND' || error.code === 'ECONNREFUSED') {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function mapMarketplaceItemToArkService(item, registry) {
|
|
25
|
+
const defaultRegistry = registry || getMarketplaceRegistry();
|
|
26
|
+
const serviceName = item.name
|
|
27
|
+
.toLowerCase()
|
|
28
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
29
|
+
.replace(/^-+|-+$/g, '');
|
|
30
|
+
const chartPath = item.ark?.chartPath || `${defaultRegistry}/${serviceName}`;
|
|
31
|
+
return {
|
|
32
|
+
name: serviceName,
|
|
33
|
+
helmReleaseName: item.ark?.helmReleaseName || serviceName,
|
|
34
|
+
description: item.description,
|
|
35
|
+
enabled: true,
|
|
36
|
+
category: 'marketplace',
|
|
37
|
+
namespace: item.ark?.namespace || serviceName,
|
|
38
|
+
chartPath,
|
|
39
|
+
installArgs: item.ark?.installArgs || ['--create-namespace'],
|
|
40
|
+
k8sServiceName: item.ark?.k8sServiceName || serviceName,
|
|
41
|
+
k8sServicePort: item.ark?.k8sServicePort,
|
|
42
|
+
k8sPortForwardLocalPort: item.ark?.k8sPortForwardLocalPort,
|
|
43
|
+
k8sDeploymentName: item.ark?.k8sDeploymentName || serviceName,
|
|
44
|
+
k8sDevDeploymentName: item.ark?.k8sDevDeploymentName,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export async function getMarketplaceServicesFromManifest() {
|
|
48
|
+
const manifest = await fetchMarketplaceManifest();
|
|
49
|
+
if (!manifest || !manifest.items) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const services = {};
|
|
53
|
+
for (const item of manifest.items) {
|
|
54
|
+
if (item.ark && item.type === 'service') {
|
|
55
|
+
const serviceName = item.name
|
|
56
|
+
.toLowerCase()
|
|
57
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
58
|
+
.replace(/^-+|-+$/g, '');
|
|
59
|
+
services[serviceName] = mapMarketplaceItemToArkService(item);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return Object.keys(services).length > 0 ? services : null;
|
|
63
|
+
}
|
|
64
|
+
export async function getMarketplaceAgentsFromManifest() {
|
|
65
|
+
const manifest = await fetchMarketplaceManifest();
|
|
66
|
+
if (!manifest || !manifest.items) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const agents = {};
|
|
70
|
+
for (const item of manifest.items) {
|
|
71
|
+
if (item.ark && item.type === 'agent') {
|
|
72
|
+
const agentName = item.name
|
|
73
|
+
.toLowerCase()
|
|
74
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
75
|
+
.replace(/^-+|-+$/g, '');
|
|
76
|
+
agents[agentName] = mapMarketplaceItemToArkService(item);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return Object.keys(agents).length > 0 ? agents : null;
|
|
80
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|