@agents-at-scale/ark 0.1.59 → 0.1.61
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/dist/arkServices.js
CHANGED
|
@@ -74,6 +74,19 @@ const defaultArkServices = {
|
|
|
74
74
|
k8sDeploymentName: 'ark-controller',
|
|
75
75
|
k8sDevDeploymentName: 'ark-controller-devspace',
|
|
76
76
|
},
|
|
77
|
+
'ark-apiserver': {
|
|
78
|
+
name: 'ark-apiserver',
|
|
79
|
+
helmReleaseName: 'ark-apiserver',
|
|
80
|
+
description: 'Aggregated API server serving ark.mckinsey.com APIs from PostgreSQL',
|
|
81
|
+
enabled: true,
|
|
82
|
+
mandatory: true,
|
|
83
|
+
category: 'core',
|
|
84
|
+
namespace: 'ark-system',
|
|
85
|
+
chartPath: `${REGISTRY_BASE}/ark-apiserver:${CHART_VERSION}`,
|
|
86
|
+
installArgs: ['--create-namespace'],
|
|
87
|
+
k8sDeploymentName: 'ark-apiserver',
|
|
88
|
+
requiresBackend: 'postgresql',
|
|
89
|
+
},
|
|
77
90
|
'ark-completions': {
|
|
78
91
|
name: 'ark-completions',
|
|
79
92
|
helmReleaseName: 'ark-completions',
|
|
@@ -5,6 +5,7 @@ import { StatusChecker } from '../../components/statusChecker.js';
|
|
|
5
5
|
import { StatusFormatter, } from '../../ui/statusFormatter.js';
|
|
6
6
|
import { fetchVersionInfo } from '../../lib/versions.js';
|
|
7
7
|
import { waitForServicesReady, } from '../../lib/waitForReady.js';
|
|
8
|
+
import { runReadinessChecks, detectStorageBackend, } from '../../lib/readinessChecks.js';
|
|
8
9
|
import { arkServices } from '../../arkServices.js';
|
|
9
10
|
import output from '../../lib/output.js';
|
|
10
11
|
import { parseTimeoutToSeconds } from '../../lib/timeout.js';
|
|
@@ -270,13 +271,15 @@ export async function checkStatus(serviceNames, options) {
|
|
|
270
271
|
StatusFormatter.printSections(sections);
|
|
271
272
|
if (options?.waitForReady) {
|
|
272
273
|
const timeoutSeconds = parseTimeoutToSeconds(options.waitForReady);
|
|
274
|
+
const backend = await detectStorageBackend();
|
|
273
275
|
let servicesToWait = [];
|
|
274
276
|
if (serviceNames && serviceNames.length > 0) {
|
|
275
277
|
servicesToWait = serviceNames
|
|
276
278
|
.map((name) => Object.values(arkServices).find((s) => s.name === name))
|
|
277
279
|
.filter((s) => s !== undefined &&
|
|
278
280
|
s.k8sDeploymentName !== undefined &&
|
|
279
|
-
s.namespace !== undefined
|
|
281
|
+
s.namespace !== undefined &&
|
|
282
|
+
(!s.requiresBackend || s.requiresBackend === backend));
|
|
280
283
|
if (servicesToWait.length === 0) {
|
|
281
284
|
output.error(`No valid services found matching: ${serviceNames.join(', ')}`);
|
|
282
285
|
process.exit(1);
|
|
@@ -286,7 +289,8 @@ export async function checkStatus(serviceNames, options) {
|
|
|
286
289
|
servicesToWait = Object.values(arkServices).filter((s) => s.enabled &&
|
|
287
290
|
s.category === 'core' &&
|
|
288
291
|
s.k8sDeploymentName &&
|
|
289
|
-
s.namespace
|
|
292
|
+
s.namespace &&
|
|
293
|
+
(!s.requiresBackend || s.requiresBackend === backend));
|
|
290
294
|
}
|
|
291
295
|
console.log();
|
|
292
296
|
const waitSpinner = ora(`Waiting for services to be ready (timeout: ${timeoutSeconds}s)...`).start();
|
|
@@ -305,14 +309,23 @@ export async function checkStatus(serviceNames, options) {
|
|
|
305
309
|
});
|
|
306
310
|
waitSpinner.text = `Waiting for services to be ready (${elapsed}/${timeoutSeconds}s)...\n${lines.join('\n')}`;
|
|
307
311
|
});
|
|
308
|
-
if (result) {
|
|
309
|
-
waitSpinner.succeed('All services are ready');
|
|
310
|
-
process.exit(0);
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
312
|
+
if (!result) {
|
|
313
313
|
waitSpinner.fail(`Services did not become ready within ${timeoutSeconds} seconds`);
|
|
314
314
|
process.exit(1);
|
|
315
315
|
}
|
|
316
|
+
waitSpinner.succeed('All services are ready');
|
|
317
|
+
const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000);
|
|
318
|
+
const remainingSeconds = Math.max(1, timeoutSeconds - elapsedSeconds);
|
|
319
|
+
const deepResults = await runReadinessChecks(remainingSeconds, (r) => {
|
|
320
|
+
const icon = r.passed ? chalk.green('✓') : chalk.red('✗');
|
|
321
|
+
const dur = `${(r.durationMs / 1000).toFixed(1)}s`;
|
|
322
|
+
const suffix = r.message ? ` — ${r.message}` : '';
|
|
323
|
+
console.log(` ${icon} ${r.name} (${dur})${suffix}`);
|
|
324
|
+
});
|
|
325
|
+
if (deepResults.some((r) => !r.passed)) {
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
process.exit(0);
|
|
316
329
|
}
|
|
317
330
|
process.exit(0);
|
|
318
331
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type StorageBackend = 'etcd' | 'postgresql';
|
|
2
|
+
export interface ReadinessCheckResult {
|
|
3
|
+
name: string;
|
|
4
|
+
passed: boolean;
|
|
5
|
+
durationMs: number;
|
|
6
|
+
message?: string;
|
|
7
|
+
}
|
|
8
|
+
export type ReadinessProgress = (result: ReadinessCheckResult) => void;
|
|
9
|
+
export declare function detectStorageBackend(): Promise<StorageBackend>;
|
|
10
|
+
export declare function runReadinessChecks(timeoutSeconds: number, onProgress?: ReadinessProgress): Promise<ReadinessCheckResult[]>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
const API_GROUP_POLL_INTERVAL_MS = 10000;
|
|
3
|
+
function sleep(ms) {
|
|
4
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5
|
+
}
|
|
6
|
+
async function runKubectl(args, timeoutMs) {
|
|
7
|
+
const result = await execa('kubectl', args, {
|
|
8
|
+
timeout: timeoutMs,
|
|
9
|
+
reject: false,
|
|
10
|
+
});
|
|
11
|
+
return {
|
|
12
|
+
exitCode: result.exitCode ?? 1,
|
|
13
|
+
stdout: result.stdout ?? '',
|
|
14
|
+
stderr: result.stderr ?? '',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export async function detectStorageBackend() {
|
|
18
|
+
const { exitCode } = await runKubectl(['get', 'crd', 'agents.ark.mckinsey.com'], 10000);
|
|
19
|
+
return exitCode === 0 ? 'etcd' : 'postgresql';
|
|
20
|
+
}
|
|
21
|
+
async function waitForApiServices(timeoutSeconds) {
|
|
22
|
+
const start = Date.now();
|
|
23
|
+
const primary = await runKubectl([
|
|
24
|
+
'wait',
|
|
25
|
+
'--for=condition=Available',
|
|
26
|
+
'apiservice',
|
|
27
|
+
'v1alpha1.ark.mckinsey.com',
|
|
28
|
+
`--timeout=${timeoutSeconds}s`,
|
|
29
|
+
], timeoutSeconds * 1000 + 5000);
|
|
30
|
+
await runKubectl([
|
|
31
|
+
'wait',
|
|
32
|
+
'--for=condition=Available',
|
|
33
|
+
'apiservice',
|
|
34
|
+
'v1prealpha1.ark.mckinsey.com',
|
|
35
|
+
'--timeout=30s',
|
|
36
|
+
], 35000);
|
|
37
|
+
return {
|
|
38
|
+
name: 'APIServices available',
|
|
39
|
+
passed: primary.exitCode === 0,
|
|
40
|
+
durationMs: Date.now() - start,
|
|
41
|
+
message: primary.exitCode === 0
|
|
42
|
+
? undefined
|
|
43
|
+
: (primary.stderr || primary.stdout).trim(),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async function waitForApiGroup(timeoutSeconds) {
|
|
47
|
+
const start = Date.now();
|
|
48
|
+
const deadline = start + timeoutSeconds * 1000;
|
|
49
|
+
while (Date.now() < deadline) {
|
|
50
|
+
const { stdout, exitCode } = await runKubectl(['api-resources', '--api-group=ark.mckinsey.com', '-o', 'name'], 10000);
|
|
51
|
+
if (exitCode === 0 && /agents\./.test(stdout)) {
|
|
52
|
+
return {
|
|
53
|
+
name: 'API group registered',
|
|
54
|
+
passed: true,
|
|
55
|
+
durationMs: Date.now() - start,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
await sleep(API_GROUP_POLL_INTERVAL_MS);
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
name: 'API group registered',
|
|
62
|
+
passed: false,
|
|
63
|
+
durationMs: Date.now() - start,
|
|
64
|
+
message: 'timed out waiting for ark.mckinsey.com API group',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export async function runReadinessChecks(timeoutSeconds, onProgress) {
|
|
68
|
+
const backend = await detectStorageBackend();
|
|
69
|
+
if (backend === 'etcd') {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
const overallStart = Date.now();
|
|
73
|
+
const remaining = () => Math.max(1, timeoutSeconds - Math.floor((Date.now() - overallStart) / 1000));
|
|
74
|
+
const checks = [
|
|
75
|
+
() => waitForApiServices(Math.min(remaining(), 120)),
|
|
76
|
+
() => waitForApiGroup(Math.min(remaining(), 300)),
|
|
77
|
+
];
|
|
78
|
+
const results = [];
|
|
79
|
+
for (const check of checks) {
|
|
80
|
+
const result = await check();
|
|
81
|
+
results.push(result);
|
|
82
|
+
onProgress?.(result);
|
|
83
|
+
if (!result.passed) {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return results;
|
|
88
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agents-at-scale/ark",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.61",
|
|
4
4
|
"description": "Ark CLI - Interactive terminal interface for ARK agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@kubernetes/client-node": "^1.3.0",
|
|
46
46
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
47
|
-
"axios": "^1.
|
|
47
|
+
"axios": "^1.16.0",
|
|
48
48
|
"chalk": "^4.1.2",
|
|
49
49
|
"commander": "^12.1.0",
|
|
50
50
|
"debug": "^4.4.1",
|
|
@@ -87,10 +87,11 @@
|
|
|
87
87
|
"overrides": {
|
|
88
88
|
"minimatch": "^10.2.3",
|
|
89
89
|
"rollup": "4.59.0",
|
|
90
|
-
"hono": "^4.12.
|
|
90
|
+
"hono": "^4.12.14",
|
|
91
91
|
"@hono/node-server": "^1.19.13",
|
|
92
92
|
"flatted": "^3.4.2",
|
|
93
93
|
"tar": "^7.5.11",
|
|
94
|
-
"express-rate-limit": "^8.3.0"
|
|
94
|
+
"express-rate-limit": "^8.3.0",
|
|
95
|
+
"fast-uri": "^3.1.1"
|
|
95
96
|
}
|
|
96
97
|
}
|
package/templates/tool/uv.lock
CHANGED
|
@@ -48,14 +48,14 @@ wheels = [
|
|
|
48
48
|
|
|
49
49
|
[[package]]
|
|
50
50
|
name = "authlib"
|
|
51
|
-
version = "1.6.
|
|
51
|
+
version = "1.6.11"
|
|
52
52
|
source = { registry = "https://pypi.org/simple" }
|
|
53
53
|
dependencies = [
|
|
54
54
|
{ name = "cryptography" },
|
|
55
55
|
]
|
|
56
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
56
|
+
sdist = { url = "https://files.pythonhosted.org/packages/28/10/b325d58ffe86815b399334a101e63bc6fa4e1953921cb23703b48a0a0220/authlib-1.6.11.tar.gz", hash = "sha256:64db35b9b01aeccb4715a6c9a6613a06f2bd7be2ab9d2eb89edd1dfc7580a38f", size = 165359, upload-time = "2026-04-16T07:22:50.279Z" }
|
|
57
57
|
wheels = [
|
|
58
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
58
|
+
{ url = "https://files.pythonhosted.org/packages/57/2f/55fca558f925a51db046e5b929deb317ddb05afed74b22d89f4eca578980/authlib-1.6.11-py2.py3-none-any.whl", hash = "sha256:c8687a9a26451c51a34a06fa17bb97cb15bba46a6a626755e2d7f50da8bff3e3", size = 244469, upload-time = "2026-04-16T07:22:48.413Z" },
|
|
59
59
|
]
|
|
60
60
|
|
|
61
61
|
[[package]]
|
|
@@ -763,20 +763,20 @@ wheels = [
|
|
|
763
763
|
|
|
764
764
|
[[package]]
|
|
765
765
|
name = "python-dotenv"
|
|
766
|
-
version = "1.
|
|
766
|
+
version = "1.2.2"
|
|
767
767
|
source = { registry = "https://pypi.org/simple" }
|
|
768
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
768
|
+
sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" }
|
|
769
769
|
wheels = [
|
|
770
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
770
|
+
{ url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" },
|
|
771
771
|
]
|
|
772
772
|
|
|
773
773
|
[[package]]
|
|
774
774
|
name = "python-multipart"
|
|
775
|
-
version = "0.0.
|
|
775
|
+
version = "0.0.26"
|
|
776
776
|
source = { registry = "https://pypi.org/simple" }
|
|
777
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
777
|
+
sdist = { url = "https://files.pythonhosted.org/packages/88/71/b145a380824a960ebd60e1014256dbb7d2253f2316ff2d73dfd8928ec2c3/python_multipart-0.0.26.tar.gz", hash = "sha256:08fadc45918cd615e26846437f50c5d6d23304da32c341f289a617127b081f17", size = 43501, upload-time = "2026-04-10T14:09:59.473Z" }
|
|
778
778
|
wheels = [
|
|
779
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
779
|
+
{ url = "https://files.pythonhosted.org/packages/9a/22/f1925cdda983ab66fc8ec6ec8014b959262747e58bdca26a4e3d1da29d56/python_multipart-0.0.26-py3-none-any.whl", hash = "sha256:c0b169f8c4484c13b0dcf2ef0ec3a4adb255c4b7d18d8e420477d2b1dd03f185", size = 28847, upload-time = "2026-04-10T14:09:58.131Z" },
|
|
780
780
|
]
|
|
781
781
|
|
|
782
782
|
[[package]]
|
|
@@ -1007,15 +1007,15 @@ wheels = [
|
|
|
1007
1007
|
|
|
1008
1008
|
[[package]]
|
|
1009
1009
|
name = "starlette"
|
|
1010
|
-
version = "0.
|
|
1010
|
+
version = "0.49.1"
|
|
1011
1011
|
source = { registry = "https://pypi.org/simple" }
|
|
1012
1012
|
dependencies = [
|
|
1013
1013
|
{ name = "anyio" },
|
|
1014
1014
|
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
|
1015
1015
|
]
|
|
1016
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
1016
|
+
sdist = { url = "https://files.pythonhosted.org/packages/1b/3f/507c21db33b66fb027a332f2cb3abbbe924cc3a79ced12f01ed8645955c9/starlette-0.49.1.tar.gz", hash = "sha256:481a43b71e24ed8c43b11ea02f5353d77840e01480881b8cb5a26b8cae64a8cb", size = 2654703, upload-time = "2025-10-28T17:34:10.928Z" }
|
|
1017
1017
|
wheels = [
|
|
1018
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1018
|
+
{ url = "https://files.pythonhosted.org/packages/51/da/545b75d420bb23b5d494b0517757b351963e974e79933f01e05c929f20a6/starlette-0.49.1-py3-none-any.whl", hash = "sha256:d92ce9f07e4a3caa3ac13a79523bd18e3bc0042bb8ff2d759a8e7dd0e1859875", size = 74175, upload-time = "2025-10-28T17:34:09.13Z" },
|
|
1019
1019
|
]
|
|
1020
1020
|
|
|
1021
1021
|
[[package]]
|