@agents-at-scale/ark 0.1.69 → 0.1.71
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 +3 -0
- package/dist/commands/install/index.d.ts +1 -0
- package/dist/commands/install/index.js +93 -11
- package/dist/commands/status/index.js +41 -11
- package/dist/lib/chatClient.js +0 -3
- package/dist/lib/readinessChecks.d.ts +23 -0
- package/dist/lib/readinessChecks.js +238 -2
- package/dist/types/arkService.d.ts +2 -0
- package/package.json +8 -5
- package/templates/project/README.md +3 -3
- package/templates/tool/agent.yaml +3 -3
- package/templates/tool/pyproject.toml +5 -0
- package/templates/tool/uv.lock +38 -32
package/dist/arkServices.js
CHANGED
|
@@ -108,6 +108,9 @@ const defaultArkServices = {
|
|
|
108
108
|
category: 'core',
|
|
109
109
|
chartPath: `${REGISTRY_BASE}/ark-tenant:${CHART_VERSION}`,
|
|
110
110
|
installArgs: [],
|
|
111
|
+
// Without a broker, a tenant loses conversation history (issue #2642).
|
|
112
|
+
requires: ['ark-broker'],
|
|
113
|
+
dependencyOverrideArgs: ['--set', 'memory.requireBroker=false'],
|
|
111
114
|
},
|
|
112
115
|
'ark-api': {
|
|
113
116
|
name: 'ark-api',
|
|
@@ -7,5 +7,6 @@ export declare function installArk(config: ArkConfig, serviceNames?: string[], o
|
|
|
7
7
|
arkVersion?: string;
|
|
8
8
|
marketplaceVersion?: string;
|
|
9
9
|
backend?: string;
|
|
10
|
+
deps?: boolean;
|
|
10
11
|
}): Promise<void>;
|
|
11
12
|
export declare function createInstallCommand(config: ArkConfig): Command;
|
|
@@ -85,6 +85,43 @@ function exitIfServicesSkipped(skipped) {
|
|
|
85
85
|
output.error(`installation incomplete: ${skipped.length} service(s) skipped because the requested version was not found: ${detail}`);
|
|
86
86
|
process.exit(1);
|
|
87
87
|
}
|
|
88
|
+
function findService(services, name) {
|
|
89
|
+
return Object.values(services).find((s) => s.name === name || s.helmReleaseName === name);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Expand requested service names to include their transitive `requires`,
|
|
93
|
+
* dependencies first. A visited set both de-duplicates and guards against
|
|
94
|
+
* a cycle in `requires` (a node already being visited is never re-entered).
|
|
95
|
+
* A dependency that does not resolve to an installable service (disabled,
|
|
96
|
+
* or excluded for the current backend) is skipped with a warning rather
|
|
97
|
+
* than pushed through to a confusing "service not found" error for a name
|
|
98
|
+
* the caller never typed.
|
|
99
|
+
*/
|
|
100
|
+
function resolveServiceOrder(requestedNames, services, skipDeps) {
|
|
101
|
+
const resolved = [];
|
|
102
|
+
const visited = new Set();
|
|
103
|
+
function visit(name) {
|
|
104
|
+
if (visited.has(name))
|
|
105
|
+
return;
|
|
106
|
+
visited.add(name);
|
|
107
|
+
if (!skipDeps) {
|
|
108
|
+
const service = findService(services, name);
|
|
109
|
+
for (const dep of service?.requires || []) {
|
|
110
|
+
if (findService(services, dep)) {
|
|
111
|
+
visit(dep);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
output.warning(`${name} requires ${dep}, but it is not installable in this configuration — skipping it`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
resolved.push(name);
|
|
119
|
+
}
|
|
120
|
+
for (const name of requestedNames) {
|
|
121
|
+
visit(name);
|
|
122
|
+
}
|
|
123
|
+
return resolved;
|
|
124
|
+
}
|
|
88
125
|
async function uninstallPrerequisites(service, verbose = false) {
|
|
89
126
|
if (!service.prerequisiteUninstalls?.length)
|
|
90
127
|
return;
|
|
@@ -204,7 +241,23 @@ export async function installArk(config, serviceNames = [], options = {}) {
|
|
|
204
241
|
}
|
|
205
242
|
// If specific services are requested, install only those services
|
|
206
243
|
if (serviceNames.length > 0) {
|
|
207
|
-
|
|
244
|
+
const installableServices = getInstallableServices(backend);
|
|
245
|
+
const skipDeps = options.deps === false;
|
|
246
|
+
const resolvedServiceNames = resolveServiceOrder(serviceNames, installableServices, skipDeps);
|
|
247
|
+
if (skipDeps) {
|
|
248
|
+
const skippedDeps = resolveServiceOrder(serviceNames, installableServices, false).filter((name) => !serviceNames.includes(name));
|
|
249
|
+
if (skippedDeps.length > 0) {
|
|
250
|
+
output.warning(`--no-deps: not installing ${skippedDeps.join(', ')} automatically; the requested service(s) may not behave as expected without them`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
for (const name of resolvedServiceNames) {
|
|
255
|
+
if (!serviceNames.includes(name)) {
|
|
256
|
+
output.info(`${name} is required by a requested service, installing it first...`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
for (const serviceName of resolvedServiceNames) {
|
|
208
261
|
// Check if it's a marketplace item
|
|
209
262
|
if (isMarketplaceService(serviceName)) {
|
|
210
263
|
const service = await getMarketplaceItem(serviceName);
|
|
@@ -249,19 +302,29 @@ export async function installArk(config, serviceNames = [], options = {}) {
|
|
|
249
302
|
continue;
|
|
250
303
|
}
|
|
251
304
|
// Core ARK service
|
|
252
|
-
const
|
|
253
|
-
const service = Object.values(services).find((s) => s.name === serviceName);
|
|
305
|
+
const service = findService(installableServices, serviceName);
|
|
254
306
|
if (!service) {
|
|
255
307
|
output.error(`service '${serviceName}' not found`);
|
|
256
308
|
output.info('available services:');
|
|
257
|
-
for (const s of Object.values(
|
|
309
|
+
for (const s of Object.values(installableServices)) {
|
|
258
310
|
output.info(` ${s.name}`);
|
|
259
311
|
}
|
|
260
312
|
process.exit(1);
|
|
261
313
|
}
|
|
314
|
+
// Apply the override only for a service actually missing one of its
|
|
315
|
+
// `requires` from this install — whether because --no-deps skipped it
|
|
316
|
+
// globally, or because a specific dependency wasn't installable (e.g.
|
|
317
|
+
// disabled in .arkrc.yaml) — not just because --no-deps was passed at
|
|
318
|
+
// all, which would wrongly override a service whose dependency IS
|
|
319
|
+
// being installed alongside it (e.g. `ark install ark-broker
|
|
320
|
+
// ark-tenant --no-deps`).
|
|
321
|
+
const missingRequires = (service.requires || []).some((dep) => !resolvedServiceNames.includes(dep));
|
|
262
322
|
output.info(`installing ${service.name}...`);
|
|
263
323
|
try {
|
|
264
|
-
await installService(service, options.verbose, options.arkVersion, options.marketplaceVersion,
|
|
324
|
+
await installService(service, options.verbose, options.arkVersion, options.marketplaceVersion, [
|
|
325
|
+
...backendInstallArgs(service, backend, postgresValues),
|
|
326
|
+
...(missingRequires ? service.dependencyOverrideArgs || [] : []),
|
|
327
|
+
]);
|
|
265
328
|
output.success(`${service.name} installed successfully`);
|
|
266
329
|
// Wait for ark-apiserver to be ready before continuing to other services
|
|
267
330
|
if (service.helmReleaseName === 'ark-apiserver' && backend === 'postgresql') {
|
|
@@ -416,9 +479,13 @@ export async function installArk(config, serviceNames = [], options = {}) {
|
|
|
416
479
|
}
|
|
417
480
|
}
|
|
418
481
|
}
|
|
419
|
-
// Install selected services
|
|
420
|
-
|
|
421
|
-
|
|
482
|
+
// Install selected services, dependencies first: `mandatory` only forces
|
|
483
|
+
// a service into `selectedComponents`, it says nothing about install
|
|
484
|
+
// order, so a `requires` dependency (e.g. ark-broker for ark-tenant) must
|
|
485
|
+
// still be resolved here even though every mandatory name is present.
|
|
486
|
+
const orderedComponents = resolveServiceOrder(selectedComponents, arkServices, false);
|
|
487
|
+
for (const serviceName of orderedComponents) {
|
|
488
|
+
const service = findService(arkServices, serviceName);
|
|
422
489
|
if (!service || !service.chartPath) {
|
|
423
490
|
continue;
|
|
424
491
|
}
|
|
@@ -473,7 +540,9 @@ export async function installArk(config, serviceNames = [], options = {}) {
|
|
|
473
540
|
process.exit(1);
|
|
474
541
|
}
|
|
475
542
|
}
|
|
476
|
-
// Install all services
|
|
543
|
+
// Install all services. Every service is selected here, so alphabetical
|
|
544
|
+
// order happens to put ark-broker before ark-tenant today — resolve
|
|
545
|
+
// `requires` explicitly anyway rather than depend on that coincidence.
|
|
477
546
|
const services = getInstallableServices(backend);
|
|
478
547
|
const sortedServices = Object.values(services).sort((a, b) => {
|
|
479
548
|
// Ensure ark-controller is always first
|
|
@@ -483,10 +552,22 @@ export async function installArk(config, serviceNames = [], options = {}) {
|
|
|
483
552
|
return 1;
|
|
484
553
|
return a.name.localeCompare(b.name);
|
|
485
554
|
});
|
|
486
|
-
|
|
555
|
+
const orderedNames = resolveServiceOrder(sortedServices.map((s) => s.name), services, false);
|
|
556
|
+
const orderedServices = orderedNames
|
|
557
|
+
.map((name) => findService(services, name))
|
|
558
|
+
.filter((s) => Boolean(s));
|
|
559
|
+
for (const service of orderedServices) {
|
|
487
560
|
output.info(`installing ${service.name}...`);
|
|
561
|
+
// A `requires` dependency disabled in .arkrc.yaml gets dropped (with a
|
|
562
|
+
// warning) by resolveServiceOrder above rather than installed — apply
|
|
563
|
+
// the same override as the named-service path so the dependent isn't
|
|
564
|
+
// then refused by its own chart-level guard.
|
|
565
|
+
const missingRequires = (service.requires || []).some((dep) => !orderedNames.includes(dep));
|
|
488
566
|
try {
|
|
489
|
-
await installService(service, options.verbose, options.arkVersion, options.marketplaceVersion,
|
|
567
|
+
await installService(service, options.verbose, options.arkVersion, options.marketplaceVersion, [
|
|
568
|
+
...backendInstallArgs(service, backend, postgresValues),
|
|
569
|
+
...(missingRequires ? service.dependencyOverrideArgs || [] : []),
|
|
570
|
+
]);
|
|
490
571
|
console.log(); // Add blank line after command output
|
|
491
572
|
}
|
|
492
573
|
catch (error) {
|
|
@@ -555,6 +636,7 @@ export function createInstallCommand(config) {
|
|
|
555
636
|
.option('--marketplace-version <version>', 'Marketplace item version to install (e.g., 0.1.5)')
|
|
556
637
|
.option('--wait-for-ready <timeout>', 'wait for Ark to be ready after installation (e.g., 30s, 2m)')
|
|
557
638
|
.option('--backend <type>', "storage backend: 'etcd' (default) or 'postgresql' (overrides storage.backend in .arkrc.yaml)")
|
|
639
|
+
.option('--no-deps', "skip a requested service's dependencies (e.g. ark-broker for ark-tenant); the resulting install may lose the guarantees those dependencies provide")
|
|
558
640
|
.option('-v, --verbose', 'show commands being executed')
|
|
559
641
|
.action(async (services, options) => {
|
|
560
642
|
await installArk(config, services, options);
|
|
@@ -5,7 +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, describeStorageBackend, } from '../../lib/readinessChecks.js';
|
|
8
|
+
import { runReadinessChecks, describeStorageBackend, describeNamespaceMemory, } from '../../lib/readinessChecks.js';
|
|
9
9
|
import { arkServices } from '../../arkServices.js';
|
|
10
10
|
import output from '../../lib/output.js';
|
|
11
11
|
import { parseTimeoutToSeconds } from '../../lib/timeout.js';
|
|
@@ -68,7 +68,26 @@ function backendStatusLine(detection) {
|
|
|
68
68
|
details,
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
|
-
function
|
|
71
|
+
function namespaceMemoryStatusLine(check) {
|
|
72
|
+
const display = {
|
|
73
|
+
ready: { icon: '●', color: 'green', details: check.message },
|
|
74
|
+
unresolved: { icon: '✗', color: 'red', details: 'not resolving' },
|
|
75
|
+
missing: { icon: '✗', color: 'red', details: 'missing' },
|
|
76
|
+
'no-broker': { icon: '○', color: 'yellow', details: 'not configured' },
|
|
77
|
+
undetermined: { icon: '?', color: 'yellow', details: 'undetermined' },
|
|
78
|
+
};
|
|
79
|
+
const { icon, color, details } = display[check.status];
|
|
80
|
+
return {
|
|
81
|
+
icon,
|
|
82
|
+
iconColor: color,
|
|
83
|
+
status: 'default memory',
|
|
84
|
+
statusColor: color,
|
|
85
|
+
name: '',
|
|
86
|
+
details,
|
|
87
|
+
subtext: check.status === 'ready' ? undefined : check.message,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function buildStatusSections(data, versionInfo, backend, namespaceMemory) {
|
|
72
91
|
const sections = [];
|
|
73
92
|
// Dependencies section
|
|
74
93
|
sections.push({
|
|
@@ -270,6 +289,9 @@ function buildStatusSections(data, versionInfo, backend) {
|
|
|
270
289
|
}
|
|
271
290
|
}
|
|
272
291
|
}
|
|
292
|
+
if (namespaceMemory) {
|
|
293
|
+
arkStatusLines.push(namespaceMemoryStatusLine(namespaceMemory));
|
|
294
|
+
}
|
|
273
295
|
if (backend) {
|
|
274
296
|
arkStatusLines.push(backendStatusLine(backend));
|
|
275
297
|
}
|
|
@@ -289,16 +311,24 @@ export async function checkStatus(serviceNames, options) {
|
|
|
289
311
|
fetchVersionInfo(),
|
|
290
312
|
]);
|
|
291
313
|
// Only probe for the storage backend if the cluster is reachable; probing an
|
|
292
|
-
// unreachable cluster would just retry to its timeout.
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
314
|
+
// unreachable cluster would just retry to its timeout. The memory check is
|
|
315
|
+
// scoped to the kubeconfig's current namespace: the invariant it reports on
|
|
316
|
+
// is per-namespace, and that is the namespace the user's queries land in.
|
|
317
|
+
// Both probes are independent, so they run together rather than in series.
|
|
318
|
+
const [detection, namespaceMemory] = await Promise.all([
|
|
319
|
+
statusData.clusterAccess
|
|
320
|
+
? describeStorageBackend()
|
|
321
|
+
: Promise.resolve({
|
|
322
|
+
backend: 'unknown',
|
|
323
|
+
status: 'unreachable',
|
|
324
|
+
message: 'Cluster is not reachable — cannot determine the storage backend.',
|
|
325
|
+
}),
|
|
326
|
+
statusData.clusterAccess && statusData.arkReady
|
|
327
|
+
? describeNamespaceMemory(statusData.clusterInfo?.namespace || 'default')
|
|
328
|
+
: Promise.resolve(undefined),
|
|
329
|
+
]);
|
|
300
330
|
spinner.stop();
|
|
301
|
-
const sections = buildStatusSections(statusData, versionInfo, detection);
|
|
331
|
+
const sections = buildStatusSections(statusData, versionInfo, detection, namespaceMemory);
|
|
302
332
|
StatusFormatter.printSections(sections);
|
|
303
333
|
if (options?.waitForReady) {
|
|
304
334
|
const timeoutSeconds = parseTimeoutToSeconds(options.waitForReady);
|
package/dist/lib/chatClient.js
CHANGED
|
@@ -32,9 +32,6 @@ export class ChatClient {
|
|
|
32
32
|
if (config.a2aContextId) {
|
|
33
33
|
annotations[QUERY_ANNOTATIONS.A2A_CONTEXT_ID] = config.a2aContextId;
|
|
34
34
|
}
|
|
35
|
-
if (config.streamingEnabled && onChunk) {
|
|
36
|
-
annotations['ark.mckinsey.com/streaming-enabled'] = 'true';
|
|
37
|
-
}
|
|
38
35
|
const queryResult = await this.arkApiClient.createQuery({
|
|
39
36
|
input,
|
|
40
37
|
target: { type: targetType, name: targetName },
|
|
@@ -12,7 +12,30 @@ export interface ReadinessCheckResult {
|
|
|
12
12
|
durationMs: number;
|
|
13
13
|
message?: string;
|
|
14
14
|
}
|
|
15
|
+
export type NamespaceMemoryStatus = 'ready' | 'unresolved' | 'missing' | 'no-broker' | 'undetermined';
|
|
16
|
+
export interface NamespaceMemoryCheck {
|
|
17
|
+
status: NamespaceMemoryStatus;
|
|
18
|
+
namespace: string;
|
|
19
|
+
memoryName: string;
|
|
20
|
+
message: string;
|
|
21
|
+
}
|
|
15
22
|
export type ReadinessProgress = (result: ReadinessCheckResult) => void;
|
|
16
23
|
export declare function describeStorageBackend(): Promise<BackendDetection>;
|
|
24
|
+
/**
|
|
25
|
+
* Report whether the namespace ends up with a Memory the executor can use.
|
|
26
|
+
*
|
|
27
|
+
* Existence is not the question: NewHTTPMemory errors when
|
|
28
|
+
* status.lastResolvedAddress is empty, so a Memory left unresolved by a
|
|
29
|
+
* missing broker Service fails every query in the namespace rather than
|
|
30
|
+
* degrading. A namespace with no broker is expected to have no Memory, so that
|
|
31
|
+
* is reported as unconfigured rather than broken.
|
|
32
|
+
*
|
|
33
|
+
* ArkConfig.defaultMemory only changes the answer when the Memory it names
|
|
34
|
+
* both exists and resolves: resolveInjectableMemory declines to inject
|
|
35
|
+
* otherwise, and NewMemoryForQuery then falls back to the hardcoded name
|
|
36
|
+
* `default`. So the override is a candidate, and `default` decides the verdict
|
|
37
|
+
* whenever that candidate is not usable.
|
|
38
|
+
*/
|
|
39
|
+
export declare function describeNamespaceMemory(namespace: string): Promise<NamespaceMemoryCheck>;
|
|
17
40
|
export declare function detectStorageBackend(): Promise<DetectedBackend>;
|
|
18
41
|
export declare function runReadinessChecks(timeoutSeconds: number, backend: DetectedBackend, onProgress?: ReadinessProgress): Promise<ReadinessCheckResult[]>;
|
|
@@ -14,8 +14,13 @@ async function runKubectl(args, timeoutMs) {
|
|
|
14
14
|
stderr: result.stderr ?? '',
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
|
+
// A resource type the cluster does not serve at all. kubectl reports this
|
|
18
|
+
// differently from a missing object, and it is just as final an answer — the
|
|
19
|
+
// aggregated API server serves no arkconfigs, so this is the normal reply
|
|
20
|
+
// there rather than something to retry.
|
|
21
|
+
const UNSERVED_RESOURCE_PATTERN = /doesn't have a resource type|no matches for kind|the server could not find the requested resource/i;
|
|
17
22
|
function classifyFailure(stderr) {
|
|
18
|
-
if (/not\s*found/i.test(stderr)) {
|
|
23
|
+
if (/not\s*found/i.test(stderr) || UNSERVED_RESOURCE_PATTERN.test(stderr)) {
|
|
19
24
|
return 'not-found';
|
|
20
25
|
}
|
|
21
26
|
if (/forbidden|unauthorized/i.test(stderr)) {
|
|
@@ -32,7 +37,8 @@ function isAuthoritativeResult(result) {
|
|
|
32
37
|
if (result.exitCode === 0) {
|
|
33
38
|
return true;
|
|
34
39
|
}
|
|
35
|
-
return /not\s*found|forbidden/i.test(result.stderr)
|
|
40
|
+
return (/not\s*found|forbidden/i.test(result.stderr) ||
|
|
41
|
+
UNSERVED_RESOURCE_PATTERN.test(result.stderr));
|
|
36
42
|
}
|
|
37
43
|
async function probeKubectl(args, timeoutMs, maxRetries = DETECT_MAX_RETRIES) {
|
|
38
44
|
let result = await runKubectl(args, timeoutMs);
|
|
@@ -95,6 +101,236 @@ export async function describeStorageBackend() {
|
|
|
95
101
|
}
|
|
96
102
|
return unknownFrom(apiReason);
|
|
97
103
|
}
|
|
104
|
+
// The Memory name the broker chart creates, and the one NewMemoryForQuery
|
|
105
|
+
// hardcodes when a Query carries no spec.memory. ArkConfig can name a
|
|
106
|
+
// different one, but only as a candidate — see describeNamespaceMemory.
|
|
107
|
+
const FALLBACK_MEMORY_NAME = 'default';
|
|
108
|
+
// Either ConfigMap is a broker presence signal, and only when its data carries
|
|
109
|
+
// enabled: "true" — the same test BrokerServiceRefFor applies before the
|
|
110
|
+
// controller's default-Memory backstop will act on it.
|
|
111
|
+
const BROKER_CONFIGMAP_NAMES = ['ark-config-broker', 'ark-config-streaming'];
|
|
112
|
+
// The ConfigMaps are not the whole story: they are rendered only when
|
|
113
|
+
// otelEndpoint.enabled / streaming.enabled are set, both of which default true
|
|
114
|
+
// but are independently disablable. A broker installed purely for message and
|
|
115
|
+
// session storage has neither, and reporting "no broker in this namespace"
|
|
116
|
+
// there is a confident claim that happens to be false. The label is the
|
|
117
|
+
// chart's rather than the release's, so a renamed release still matches. This
|
|
118
|
+
// mirrors DefaultMemoryReconciler's own two-signal lookup.
|
|
119
|
+
const BROKER_SERVICE_SELECTOR = 'app.kubernetes.io/name=ark-broker';
|
|
120
|
+
const BROKER_ENABLED_JSONPATH = '{range .items[*]}{.data.enabled}{"\\n"}{end}';
|
|
121
|
+
// kubectl reports a missing namespace and a missing object with the same
|
|
122
|
+
// NotFound status, so the two are told apart by the message. Reporting on a
|
|
123
|
+
// namespace that does not exist as "no broker here" would be a confident
|
|
124
|
+
// answer drawn from nothing.
|
|
125
|
+
function isMissingNamespace(stderr) {
|
|
126
|
+
return /namespaces?\s+"[^"]*"\s+not\s+found/i.test(stderr);
|
|
127
|
+
}
|
|
128
|
+
async function probeMemory(namespace, name) {
|
|
129
|
+
const result = await probeKubectl(['get', 'memory', name, '--namespace', namespace, '-o', 'json'], 10000);
|
|
130
|
+
if (result.exitCode !== 0) {
|
|
131
|
+
if (isMissingNamespace(result.stderr)) {
|
|
132
|
+
return {
|
|
133
|
+
outcome: 'undetermined',
|
|
134
|
+
detail: `namespace ${namespace} does not exist`,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
const reason = classifyFailure(result.stderr);
|
|
138
|
+
if (reason === 'not-found') {
|
|
139
|
+
return { outcome: 'absent' };
|
|
140
|
+
}
|
|
141
|
+
return { outcome: 'undetermined', detail: reason };
|
|
142
|
+
}
|
|
143
|
+
let address = '';
|
|
144
|
+
let phase = '';
|
|
145
|
+
let statusMessage = '';
|
|
146
|
+
try {
|
|
147
|
+
const parsed = JSON.parse(result.stdout);
|
|
148
|
+
address = parsed?.status?.lastResolvedAddress || '';
|
|
149
|
+
phase = parsed?.status?.phase || '';
|
|
150
|
+
statusMessage = parsed?.status?.message || '';
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return { outcome: 'undetermined', detail: 'unreadable kubectl output' };
|
|
154
|
+
}
|
|
155
|
+
if (address) {
|
|
156
|
+
return { outcome: 'resolved', address };
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
outcome: 'unresolved',
|
|
160
|
+
detail: statusMessage || phase || 'no resolved address',
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
async function probeBrokerConfigMaps(namespace) {
|
|
164
|
+
const result = await probeKubectl([
|
|
165
|
+
'get',
|
|
166
|
+
'configmap',
|
|
167
|
+
...BROKER_CONFIGMAP_NAMES,
|
|
168
|
+
'--namespace',
|
|
169
|
+
namespace,
|
|
170
|
+
'--ignore-not-found',
|
|
171
|
+
'-o',
|
|
172
|
+
`jsonpath=${BROKER_ENABLED_JSONPATH}`,
|
|
173
|
+
], 10000);
|
|
174
|
+
if (result.exitCode !== 0) {
|
|
175
|
+
return 'undetermined';
|
|
176
|
+
}
|
|
177
|
+
const enabled = result.stdout
|
|
178
|
+
.split('\n')
|
|
179
|
+
.some((line) => line.trim() === 'true');
|
|
180
|
+
return enabled ? 'present' : 'absent';
|
|
181
|
+
}
|
|
182
|
+
async function probeBrokerService(namespace) {
|
|
183
|
+
const result = await probeKubectl([
|
|
184
|
+
'get',
|
|
185
|
+
'service',
|
|
186
|
+
'--namespace',
|
|
187
|
+
namespace,
|
|
188
|
+
'--selector',
|
|
189
|
+
BROKER_SERVICE_SELECTOR,
|
|
190
|
+
'-o',
|
|
191
|
+
'name',
|
|
192
|
+
], 10000);
|
|
193
|
+
if (result.exitCode !== 0) {
|
|
194
|
+
return 'undetermined';
|
|
195
|
+
}
|
|
196
|
+
return result.stdout.trim() ? 'present' : 'absent';
|
|
197
|
+
}
|
|
198
|
+
async function probeBroker(namespace) {
|
|
199
|
+
const fromConfigMaps = await probeBrokerConfigMaps(namespace);
|
|
200
|
+
if (fromConfigMaps === 'present') {
|
|
201
|
+
return fromConfigMaps;
|
|
202
|
+
}
|
|
203
|
+
const fromService = await probeBrokerService(namespace);
|
|
204
|
+
if (fromService === 'present') {
|
|
205
|
+
return fromService;
|
|
206
|
+
}
|
|
207
|
+
// Neither signal found it. Say so only if both probes actually answered.
|
|
208
|
+
if (fromConfigMaps === 'undetermined' || fromService === 'undetermined') {
|
|
209
|
+
return 'undetermined';
|
|
210
|
+
}
|
|
211
|
+
return 'absent';
|
|
212
|
+
}
|
|
213
|
+
// ArkConfig is cluster-scoped and optional, and the aggregated API server does
|
|
214
|
+
// not serve it at all — both of which classifyFailure reports as not-found, a
|
|
215
|
+
// legitimate "there is no override". "Could not tell" is different: a
|
|
216
|
+
// namespace-scoped user is forbidden from reading a cluster-scoped resource,
|
|
217
|
+
// and would otherwise be judged against the wrong name.
|
|
218
|
+
async function readDefaultMemoryOverride() {
|
|
219
|
+
const result = await probeKubectl([
|
|
220
|
+
'get',
|
|
221
|
+
'arkconfig',
|
|
222
|
+
'default',
|
|
223
|
+
'-o',
|
|
224
|
+
'jsonpath={.spec.defaultMemory.name}',
|
|
225
|
+
], 10000);
|
|
226
|
+
if (result.exitCode === 0) {
|
|
227
|
+
const name = result.stdout.trim();
|
|
228
|
+
return name ? { kind: 'name', name } : { kind: 'none' };
|
|
229
|
+
}
|
|
230
|
+
if (classifyFailure(result.stderr) === 'not-found') {
|
|
231
|
+
return { kind: 'none' };
|
|
232
|
+
}
|
|
233
|
+
return { kind: 'undetermined' };
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Report whether the namespace ends up with a Memory the executor can use.
|
|
237
|
+
*
|
|
238
|
+
* Existence is not the question: NewHTTPMemory errors when
|
|
239
|
+
* status.lastResolvedAddress is empty, so a Memory left unresolved by a
|
|
240
|
+
* missing broker Service fails every query in the namespace rather than
|
|
241
|
+
* degrading. A namespace with no broker is expected to have no Memory, so that
|
|
242
|
+
* is reported as unconfigured rather than broken.
|
|
243
|
+
*
|
|
244
|
+
* ArkConfig.defaultMemory only changes the answer when the Memory it names
|
|
245
|
+
* both exists and resolves: resolveInjectableMemory declines to inject
|
|
246
|
+
* otherwise, and NewMemoryForQuery then falls back to the hardcoded name
|
|
247
|
+
* `default`. So the override is a candidate, and `default` decides the verdict
|
|
248
|
+
* whenever that candidate is not usable.
|
|
249
|
+
*/
|
|
250
|
+
export async function describeNamespaceMemory(namespace) {
|
|
251
|
+
const override = await readDefaultMemoryOverride();
|
|
252
|
+
if (override.kind === 'name' && override.name !== FALLBACK_MEMORY_NAME) {
|
|
253
|
+
const probe = await probeMemory(namespace, override.name);
|
|
254
|
+
if (probe.outcome === 'resolved') {
|
|
255
|
+
return {
|
|
256
|
+
namespace,
|
|
257
|
+
memoryName: override.name,
|
|
258
|
+
status: 'ready',
|
|
259
|
+
message: probe.address,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
if (probe.outcome === 'undetermined') {
|
|
263
|
+
return {
|
|
264
|
+
namespace,
|
|
265
|
+
memoryName: override.name,
|
|
266
|
+
status: 'undetermined',
|
|
267
|
+
message: `Could not read Memory "${override.name}" in namespace ${namespace} (${probe.detail}).`,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const unusedOverride = override.kind === 'name' && override.name !== FALLBACK_MEMORY_NAME
|
|
272
|
+
? override.name
|
|
273
|
+
: undefined;
|
|
274
|
+
const check = await describeFallbackMemory(namespace, unusedOverride);
|
|
275
|
+
// The override read failed, so this verdict may be judging the wrong name.
|
|
276
|
+
// Only downgrade one that accuses the namespace of being broken.
|
|
277
|
+
if (override.kind === 'undetermined' &&
|
|
278
|
+
(check.status === 'missing' || check.status === 'unresolved')) {
|
|
279
|
+
return {
|
|
280
|
+
...check,
|
|
281
|
+
status: 'undetermined',
|
|
282
|
+
message: `${check.message} ArkConfig could not be read, so a cluster-wide default memory name may apply instead.`,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
return check;
|
|
286
|
+
}
|
|
287
|
+
async function describeFallbackMemory(namespace, unusedOverride) {
|
|
288
|
+
const base = { namespace, memoryName: FALLBACK_MEMORY_NAME };
|
|
289
|
+
// The operator asked for a different name and is not getting it. Without
|
|
290
|
+
// saying so, the remediation below reads as "create Memory/default", which
|
|
291
|
+
// is right for the executor and confusing next to their own ArkConfig.
|
|
292
|
+
const overrideNote = unusedOverride
|
|
293
|
+
? ` ArkConfig names "${unusedOverride}", which is not usable here, so resolution falls back to "${FALLBACK_MEMORY_NAME}".`
|
|
294
|
+
: '';
|
|
295
|
+
const probe = await probeMemory(namespace, FALLBACK_MEMORY_NAME);
|
|
296
|
+
if (probe.outcome === 'resolved') {
|
|
297
|
+
return { ...base, status: 'ready', message: probe.address };
|
|
298
|
+
}
|
|
299
|
+
if (probe.outcome === 'undetermined') {
|
|
300
|
+
return {
|
|
301
|
+
...base,
|
|
302
|
+
status: 'undetermined',
|
|
303
|
+
message: `Could not read Memory "${FALLBACK_MEMORY_NAME}" in namespace ${namespace} (${probe.detail}).`,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
if (probe.outcome === 'unresolved') {
|
|
307
|
+
return {
|
|
308
|
+
...base,
|
|
309
|
+
status: 'unresolved',
|
|
310
|
+
message: `Memory "${FALLBACK_MEMORY_NAME}" in namespace ${namespace} has not resolved an address (${probe.detail}); queries using it will fail.${overrideNote}`,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
const broker = await probeBroker(namespace);
|
|
314
|
+
if (broker === 'undetermined') {
|
|
315
|
+
return {
|
|
316
|
+
...base,
|
|
317
|
+
status: 'undetermined',
|
|
318
|
+
message: `Could not determine whether namespace ${namespace} has a broker, so a missing Memory cannot be judged.`,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
if (broker === 'present') {
|
|
322
|
+
return {
|
|
323
|
+
...base,
|
|
324
|
+
status: 'missing',
|
|
325
|
+
message: `Namespace ${namespace} has a broker but no Memory "${FALLBACK_MEMORY_NAME}"; chats there will silently lose conversation history.${overrideNote}`,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
...base,
|
|
330
|
+
status: 'no-broker',
|
|
331
|
+
message: `No broker in namespace ${namespace}, so no conversation history is kept there.`,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
98
334
|
export async function detectStorageBackend() {
|
|
99
335
|
return (await describeStorageBackend()).backend;
|
|
100
336
|
}
|
|
@@ -13,6 +13,8 @@ export interface ArkService {
|
|
|
13
13
|
chartPath?: string;
|
|
14
14
|
installArgs?: string[];
|
|
15
15
|
prerequisiteUninstalls?: PrerequisiteUninstall[];
|
|
16
|
+
requires?: string[];
|
|
17
|
+
dependencyOverrideArgs?: string[];
|
|
16
18
|
k8sServiceName?: string;
|
|
17
19
|
k8sServicePort?: number;
|
|
18
20
|
k8sPortForwardLocalPort?: number;
|
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.71",
|
|
4
4
|
"description": "Ark CLI - Interactive terminal interface for ARK agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@kubernetes/client-node": "^1.3.0",
|
|
46
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
47
|
-
"axios": "^1.
|
|
46
|
+
"@modelcontextprotocol/sdk": "^1.32.0",
|
|
47
|
+
"axios": "^1.20.0",
|
|
48
48
|
"chalk": "^4.1.2",
|
|
49
49
|
"commander": "^12.1.0",
|
|
50
50
|
"debug": "^4.4.1",
|
|
@@ -85,7 +85,8 @@
|
|
|
85
85
|
"node": ">=18.0.0"
|
|
86
86
|
},
|
|
87
87
|
"overrides": {
|
|
88
|
-
"
|
|
88
|
+
"vite": "^8.3.3",
|
|
89
|
+
"brace-expansion": "^5.0.12",
|
|
89
90
|
"ip-address": "^10.3.1",
|
|
90
91
|
"js-yaml": "^4.3.1",
|
|
91
92
|
"minimatch": "^10.2.3",
|
|
@@ -99,6 +100,8 @@
|
|
|
99
100
|
"ws": "^8.21.0",
|
|
100
101
|
"esbuild": "^0.28.1",
|
|
101
102
|
"form-data": "^4.0.6",
|
|
102
|
-
"postcss": "^8.5.18"
|
|
103
|
+
"postcss": "^8.5.18",
|
|
104
|
+
"proxy-addr": "^2.0.8",
|
|
105
|
+
"source-map-js": "^1.2.2"
|
|
103
106
|
}
|
|
104
107
|
}
|
|
@@ -7,3 +7,8 @@ requires-python = ">=3.12"
|
|
|
7
7
|
dependencies = [
|
|
8
8
|
"fastmcp>=2.9.0",
|
|
9
9
|
]
|
|
10
|
+
|
|
11
|
+
[tool.uv]
|
|
12
|
+
# Security floor: authlib <=1.7.2 is affected by CVE-2026-104056 (XRAY-1104189).
|
|
13
|
+
# authlib is pulled transitively via fastmcp; pin the minimum to the patched release.
|
|
14
|
+
constraint-dependencies = ["authlib>=1.8.0"]
|
package/templates/tool/uv.lock
CHANGED
|
@@ -8,6 +8,9 @@ resolution-markers = [
|
|
|
8
8
|
"python_full_version < '3.14' and sys_platform != 'win32'",
|
|
9
9
|
]
|
|
10
10
|
|
|
11
|
+
[manifest]
|
|
12
|
+
constraints = [{ name = "authlib", specifier = ">=1.8.0" }]
|
|
13
|
+
|
|
11
14
|
[[package]]
|
|
12
15
|
name = "aiofile"
|
|
13
16
|
version = "3.9.0"
|
|
@@ -31,16 +34,15 @@ wheels = [
|
|
|
31
34
|
|
|
32
35
|
[[package]]
|
|
33
36
|
name = "anyio"
|
|
34
|
-
version = "4.
|
|
37
|
+
version = "4.14.2"
|
|
35
38
|
source = { registry = "https://pypi.org/simple" }
|
|
36
39
|
dependencies = [
|
|
37
40
|
{ name = "idna" },
|
|
38
|
-
{ name = "sniffio" },
|
|
39
41
|
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
|
40
42
|
]
|
|
41
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
43
|
+
sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" }
|
|
42
44
|
wheels = [
|
|
43
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
45
|
+
{ url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" },
|
|
44
46
|
]
|
|
45
47
|
|
|
46
48
|
[[package]]
|
|
@@ -54,14 +56,15 @@ wheels = [
|
|
|
54
56
|
|
|
55
57
|
[[package]]
|
|
56
58
|
name = "authlib"
|
|
57
|
-
version = "1.
|
|
59
|
+
version = "1.8.0"
|
|
58
60
|
source = { registry = "https://pypi.org/simple" }
|
|
59
61
|
dependencies = [
|
|
60
62
|
{ name = "cryptography" },
|
|
63
|
+
{ name = "joserfc" },
|
|
61
64
|
]
|
|
62
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
65
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f1/51/bc1729d3cfdc214b4935f4e886e4dd443c3065fd8e1e66423fe84b490f81/authlib-1.8.0.tar.gz", hash = "sha256:f3ecd5f1da737262fb53bf1a4d95c4ea1ad9dd509316587a255c99ab1838a4f0", size = 177759, upload-time = "2026-08-30T12:12:34.833Z" }
|
|
63
66
|
wheels = [
|
|
64
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
67
|
+
{ url = "https://files.pythonhosted.org/packages/b8/c6/6f124bcfbbfb20fba22c939b4e43a06dccfc0e1ca20e5634ca573cb1e271/authlib-1.8.0-py2.py3-none-any.whl", hash = "sha256:88aebbd9af6757e14e912d5dc007ae1dc1f3e27e3b2152ce7c552ee2c3b3c121", size = 260804, upload-time = "2026-08-30T12:12:33.162Z" },
|
|
65
68
|
]
|
|
66
69
|
|
|
67
70
|
[[package]]
|
|
@@ -449,6 +452,18 @@ wheels = [
|
|
|
449
452
|
{ url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" },
|
|
450
453
|
]
|
|
451
454
|
|
|
455
|
+
[[package]]
|
|
456
|
+
name = "joserfc"
|
|
457
|
+
version = "1.7.5"
|
|
458
|
+
source = { registry = "https://pypi.org/simple" }
|
|
459
|
+
dependencies = [
|
|
460
|
+
{ name = "cryptography" },
|
|
461
|
+
]
|
|
462
|
+
sdist = { url = "https://files.pythonhosted.org/packages/19/94/80fea1514b7c6d7d37804d3fe9ca81455f633347fc98731bd71ffe1faa17/joserfc-1.7.5.tar.gz", hash = "sha256:d5ff536e658e17664f8c1b1ab60dc4aa62aa973fcef1edd33cc44bda45d6f5ea", size = 234990, upload-time = "2026-08-29T13:05:42.057Z" }
|
|
463
|
+
wheels = [
|
|
464
|
+
{ url = "https://files.pythonhosted.org/packages/67/c5/82addfd375e5ee6520644e0553e4aadde92d668c4fc99cc716d337fe7bb3/joserfc-1.7.5-py3-none-any.whl", hash = "sha256:add2c2c84e8373b084d526a8b53daba5d7a513a118cd2dcd9fc9f979d0922159", size = 71269, upload-time = "2026-08-29T13:05:40.718Z" },
|
|
465
|
+
]
|
|
466
|
+
|
|
452
467
|
[[package]]
|
|
453
468
|
name = "jsonref"
|
|
454
469
|
version = "1.1.0"
|
|
@@ -669,10 +684,10 @@ resolution-markers = [
|
|
|
669
684
|
"python_full_version < '3.14' and sys_platform != 'win32'",
|
|
670
685
|
]
|
|
671
686
|
dependencies = [
|
|
672
|
-
{ name = "annotated-types" },
|
|
673
|
-
{ name = "pydantic-core", version = "2.33.2", source = { registry = "https://pypi.org/simple" } },
|
|
674
|
-
{ name = "typing-extensions" },
|
|
675
|
-
{ name = "typing-inspection", version = "0.4.1", source = { registry = "https://pypi.org/simple" } },
|
|
687
|
+
{ name = "annotated-types", marker = "python_full_version < '3.14'" },
|
|
688
|
+
{ name = "pydantic-core", version = "2.33.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" },
|
|
689
|
+
{ name = "typing-extensions", marker = "python_full_version < '3.14'" },
|
|
690
|
+
{ name = "typing-inspection", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" },
|
|
676
691
|
]
|
|
677
692
|
sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" }
|
|
678
693
|
wheels = [
|
|
@@ -681,7 +696,7 @@ wheels = [
|
|
|
681
696
|
|
|
682
697
|
[package.optional-dependencies]
|
|
683
698
|
email = [
|
|
684
|
-
{ name = "email-validator" },
|
|
699
|
+
{ name = "email-validator", marker = "python_full_version < '3.14'" },
|
|
685
700
|
]
|
|
686
701
|
|
|
687
702
|
[[package]]
|
|
@@ -693,10 +708,10 @@ resolution-markers = [
|
|
|
693
708
|
"python_full_version >= '3.14' and sys_platform != 'win32'",
|
|
694
709
|
]
|
|
695
710
|
dependencies = [
|
|
696
|
-
{ name = "annotated-types" },
|
|
697
|
-
{ name = "pydantic-core", version = "2.46.4", source = { registry = "https://pypi.org/simple" } },
|
|
698
|
-
{ name = "typing-extensions" },
|
|
699
|
-
{ name = "typing-inspection", version = "0.4.2", source = { registry = "https://pypi.org/simple" } },
|
|
711
|
+
{ name = "annotated-types", marker = "python_full_version >= '3.14'" },
|
|
712
|
+
{ name = "pydantic-core", version = "2.46.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" },
|
|
713
|
+
{ name = "typing-extensions", marker = "python_full_version >= '3.14'" },
|
|
714
|
+
{ name = "typing-inspection", version = "0.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" },
|
|
700
715
|
]
|
|
701
716
|
sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" }
|
|
702
717
|
wheels = [
|
|
@@ -705,7 +720,7 @@ wheels = [
|
|
|
705
720
|
|
|
706
721
|
[package.optional-dependencies]
|
|
707
722
|
email = [
|
|
708
|
-
{ name = "email-validator" },
|
|
723
|
+
{ name = "email-validator", marker = "python_full_version >= '3.14'" },
|
|
709
724
|
]
|
|
710
725
|
|
|
711
726
|
[[package]]
|
|
@@ -717,7 +732,7 @@ resolution-markers = [
|
|
|
717
732
|
"python_full_version < '3.14' and sys_platform != 'win32'",
|
|
718
733
|
]
|
|
719
734
|
dependencies = [
|
|
720
|
-
{ name = "typing-extensions" },
|
|
735
|
+
{ name = "typing-extensions", marker = "python_full_version < '3.14'" },
|
|
721
736
|
]
|
|
722
737
|
sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" }
|
|
723
738
|
wheels = [
|
|
@@ -763,7 +778,7 @@ resolution-markers = [
|
|
|
763
778
|
"python_full_version >= '3.14' and sys_platform != 'win32'",
|
|
764
779
|
]
|
|
765
780
|
dependencies = [
|
|
766
|
-
{ name = "typing-extensions" },
|
|
781
|
+
{ name = "typing-extensions", marker = "python_full_version >= '3.14'" },
|
|
767
782
|
]
|
|
768
783
|
sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" }
|
|
769
784
|
wheels = [
|
|
@@ -1096,23 +1111,14 @@ name = "secretstorage"
|
|
|
1096
1111
|
version = "3.5.0"
|
|
1097
1112
|
source = { registry = "https://pypi.org/simple" }
|
|
1098
1113
|
dependencies = [
|
|
1099
|
-
{ name = "cryptography" },
|
|
1100
|
-
{ name = "jeepney" },
|
|
1114
|
+
{ name = "cryptography", marker = "sys_platform != 'win32'" },
|
|
1115
|
+
{ name = "jeepney", marker = "sys_platform != 'win32'" },
|
|
1101
1116
|
]
|
|
1102
1117
|
sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" }
|
|
1103
1118
|
wheels = [
|
|
1104
1119
|
{ url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" },
|
|
1105
1120
|
]
|
|
1106
1121
|
|
|
1107
|
-
[[package]]
|
|
1108
|
-
name = "sniffio"
|
|
1109
|
-
version = "1.3.1"
|
|
1110
|
-
source = { registry = "https://pypi.org/simple" }
|
|
1111
|
-
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
|
|
1112
|
-
wheels = [
|
|
1113
|
-
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
|
|
1114
|
-
]
|
|
1115
|
-
|
|
1116
1122
|
[[package]]
|
|
1117
1123
|
name = "sse-starlette"
|
|
1118
1124
|
version = "2.3.6"
|
|
@@ -1167,7 +1173,7 @@ resolution-markers = [
|
|
|
1167
1173
|
"python_full_version < '3.14' and sys_platform != 'win32'",
|
|
1168
1174
|
]
|
|
1169
1175
|
dependencies = [
|
|
1170
|
-
{ name = "typing-extensions" },
|
|
1176
|
+
{ name = "typing-extensions", marker = "python_full_version < '3.14'" },
|
|
1171
1177
|
]
|
|
1172
1178
|
sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" }
|
|
1173
1179
|
wheels = [
|
|
@@ -1183,7 +1189,7 @@ resolution-markers = [
|
|
|
1183
1189
|
"python_full_version >= '3.14' and sys_platform != 'win32'",
|
|
1184
1190
|
]
|
|
1185
1191
|
dependencies = [
|
|
1186
|
-
{ name = "typing-extensions" },
|
|
1192
|
+
{ name = "typing-extensions", marker = "python_full_version >= '3.14'" },
|
|
1187
1193
|
]
|
|
1188
1194
|
sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
|
|
1189
1195
|
wheels = [
|