@agents-at-scale/ark 0.1.70 → 0.1.72

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.
@@ -19,6 +19,7 @@ function enrichServiceDetails(service) {
19
19
  warning: { icon: '⚠', text: 'warning', color: 'yellow' },
20
20
  'not ready': { icon: '○', text: 'not ready', color: 'yellow' },
21
21
  'not installed': { icon: '?', text: 'not installed', color: 'yellow' },
22
+ 'no access': { icon: '⊘', text: 'no access', color: 'yellow' },
22
23
  };
23
24
  const statusInfo = statusMap[service.status] || {
24
25
  icon: '?',
@@ -52,8 +52,22 @@ export const getKindVersion = () => ({
52
52
  return match ? match[1] : output.trim();
53
53
  },
54
54
  });
55
- function createErrorServiceStatus(name, url, error, defaultStatus = 'unhealthy', defaultDetails) {
55
+ function isForbiddenError(error) {
56
+ return error instanceof Error && /forbidden/i.test(error.message);
57
+ }
58
+ function createErrorServiceStatus(name, url, error, defaultStatus = 'unhealthy', defaultDetails, namespace) {
56
59
  const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
60
+ if (isForbiddenError(error)) {
61
+ return {
62
+ name,
63
+ status: 'no access',
64
+ url,
65
+ details: namespace
66
+ ? `No permission to read namespace '${namespace}'`
67
+ : 'No permission to read this resource',
68
+ namespace,
69
+ };
70
+ }
57
71
  return {
58
72
  name,
59
73
  status: defaultStatus,
@@ -246,7 +260,7 @@ export class StatusChecker {
246
260
  namespace,
247
261
  };
248
262
  }
249
- return createErrorServiceStatus(serviceName, '', error, 'unhealthy', `Failed to check deployment: ${errorMessage}`);
263
+ return createErrorServiceStatus(serviceName, '', error, 'unhealthy', `Failed to check deployment: ${errorMessage}`, namespace);
250
264
  }
251
265
  }
252
266
  /**
@@ -288,7 +302,7 @@ export class StatusChecker {
288
302
  }
289
303
  catch (error) {
290
304
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
291
- return createErrorServiceStatus(serviceName, '', error, 'unhealthy', `Failed to check helm status: ${errorMessage}`);
305
+ return createErrorServiceStatus(serviceName, '', error, 'unhealthy', `Failed to check helm status: ${errorMessage}`, namespace);
292
306
  }
293
307
  }
294
308
  /**
@@ -1,4 +1,5 @@
1
1
  import { execa } from 'execa';
2
+ import { classifyFailure } from './readinessChecks.js';
2
3
  export async function detectClusterType() {
3
4
  try {
4
5
  const { stdout } = await execa('kubectl', ['config', 'current-context']);
@@ -31,7 +32,59 @@ export async function detectClusterType() {
31
32
  };
32
33
  }
33
34
  }
35
+ const NODE_INTERNAL_IP_JSONPATH = 'jsonpath={.items[0].status.addresses[?(@.type=="InternalIP")].address}';
36
+ const NODE_EXTERNAL_IP_JSONPATH = 'jsonpath={.items[0].status.addresses[?(@.type=="ExternalIP")].address}';
37
+ async function getNodeIp(jsonpath) {
38
+ const { stdout } = await execa('kubectl', ['get', 'nodes', '-o', jsonpath]);
39
+ return stdout.trim();
40
+ }
41
+ async function getIstioGatewayAddress(field) {
42
+ const { stdout } = await execa('kubectl', [
43
+ 'get',
44
+ 'svc',
45
+ '-n',
46
+ 'istio-system',
47
+ 'istio-ingressgateway',
48
+ '-o',
49
+ `jsonpath={.status.loadBalancer.ingress[0].${field}}`,
50
+ ]);
51
+ return stdout.trim();
52
+ }
53
+ async function resolveClusterIp(type) {
54
+ switch (type) {
55
+ case 'minikube':
56
+ try {
57
+ const { stdout } = await execa('minikube', ['ip']);
58
+ return stdout.trim();
59
+ }
60
+ catch {
61
+ return getNodeIp(NODE_INTERNAL_IP_JSONPATH);
62
+ }
63
+ case 'docker-desktop':
64
+ return 'localhost';
65
+ case 'cloud':
66
+ try {
67
+ const ip = await getIstioGatewayAddress('ip');
68
+ return ip || (await getIstioGatewayAddress('hostname'));
69
+ }
70
+ catch {
71
+ return getNodeIp(NODE_EXTERNAL_IP_JSONPATH);
72
+ }
73
+ default:
74
+ return getNodeIp(NODE_INTERNAL_IP_JSONPATH);
75
+ }
76
+ }
77
+ function isForbiddenError(error) {
78
+ if (!(error instanceof Error)) {
79
+ return false;
80
+ }
81
+ const stderr = error.stderr;
82
+ const text = typeof stderr === 'string' && stderr ? stderr : error.message;
83
+ return classifyFailure(text) === 'forbidden';
84
+ }
34
85
  export async function getClusterInfo(context) {
86
+ let resolvedContext;
87
+ let resolvedNamespace;
35
88
  try {
36
89
  // If context is provided, use it
37
90
  const contextArgs = context ? ['--context', context] : [];
@@ -48,6 +101,8 @@ export async function getClusterInfo(context) {
48
101
  const currentContext = config['current-context'] || '';
49
102
  const contextData = config.contexts?.find((c) => c.name === currentContext);
50
103
  const namespace = contextData?.context?.namespace || 'default';
104
+ resolvedContext = currentContext;
105
+ resolvedNamespace = namespace;
51
106
  // Detect cluster type from context name
52
107
  const clusterInfo = await detectClusterType();
53
108
  clusterInfo.context = currentContext;
@@ -56,99 +111,22 @@ export async function getClusterInfo(context) {
56
111
  return clusterInfo;
57
112
  }
58
113
  let ip;
59
- switch (clusterInfo.type) {
60
- case 'minikube':
61
- try {
62
- const { stdout } = await execa('minikube', ['ip']);
63
- ip = stdout.trim();
64
- }
65
- catch {
66
- // Fallback to kubectl if minikube command fails
67
- const { stdout } = await execa('kubectl', [
68
- 'get',
69
- 'nodes',
70
- '-o',
71
- 'jsonpath={.items[0].status.addresses[?(@.type=="InternalIP")].address}',
72
- ]);
73
- ip = stdout.trim();
74
- }
75
- break;
76
- case 'kind': {
77
- const { stdout: kindOutput } = await execa('kubectl', [
78
- 'get',
79
- 'nodes',
80
- '-o',
81
- 'jsonpath={.items[0].status.addresses[?(@.type=="InternalIP")].address}',
82
- ]);
83
- ip = kindOutput.trim();
84
- break;
85
- }
86
- case 'docker-desktop':
87
- ip = 'localhost';
88
- break;
89
- case 'k3s': {
90
- const { stdout: k3sOutput } = await execa('kubectl', [
91
- 'get',
92
- 'nodes',
93
- '-o',
94
- 'jsonpath={.items[0].status.addresses[?(@.type=="InternalIP")].address}',
95
- ]);
96
- ip = k3sOutput.trim();
97
- break;
98
- }
99
- case 'cloud':
100
- // For cloud clusters, try to get the external IP or load balancer IP
101
- try {
102
- const { stdout: lbOutput } = await execa('kubectl', [
103
- 'get',
104
- 'svc',
105
- '-n',
106
- 'istio-system',
107
- 'istio-ingressgateway',
108
- '-o',
109
- 'jsonpath={.status.loadBalancer.ingress[0].ip}',
110
- ]);
111
- ip = lbOutput.trim();
112
- if (!ip) {
113
- const { stdout: hostnameOutput } = await execa('kubectl', [
114
- 'get',
115
- 'svc',
116
- '-n',
117
- 'istio-system',
118
- 'istio-ingressgateway',
119
- '-o',
120
- 'jsonpath={.status.loadBalancer.ingress[0].hostname}',
121
- ]);
122
- ip = hostnameOutput.trim();
123
- }
124
- }
125
- catch {
126
- // Fallback to node IP
127
- const { stdout: nodeOutput } = await execa('kubectl', [
128
- 'get',
129
- 'nodes',
130
- '-o',
131
- 'jsonpath={.items[0].status.addresses[?(@.type=="ExternalIP")].address}',
132
- ]);
133
- ip = nodeOutput.trim();
134
- }
135
- break;
136
- default: {
137
- const { stdout: defaultOutput } = await execa('kubectl', [
138
- 'get',
139
- 'nodes',
140
- '-o',
141
- 'jsonpath={.items[0].status.addresses[?(@.type=="InternalIP")].address}',
142
- ]);
143
- ip = defaultOutput.trim();
144
- break;
114
+ try {
115
+ ip = await resolveClusterIp(clusterInfo.type);
116
+ }
117
+ catch (error) {
118
+ if (!isForbiddenError(error)) {
119
+ throw error;
145
120
  }
121
+ ip = undefined;
146
122
  }
147
123
  return { ...clusterInfo, ip };
148
124
  }
149
125
  catch (error) {
150
126
  return {
151
127
  type: 'unknown',
128
+ ...(resolvedContext !== undefined && { context: resolvedContext }),
129
+ ...(resolvedNamespace !== undefined && { namespace: resolvedNamespace }),
152
130
  error: error instanceof Error ? error.message : 'Unknown error',
153
131
  };
154
132
  }
@@ -20,6 +20,8 @@ export interface NamespaceMemoryCheck {
20
20
  message: string;
21
21
  }
22
22
  export type ReadinessProgress = (result: ReadinessCheckResult) => void;
23
+ type FailureReason = 'not-found' | 'forbidden' | 'unreachable' | 'undetermined';
24
+ export declare function classifyFailure(stderr: string): FailureReason;
23
25
  export declare function describeStorageBackend(): Promise<BackendDetection>;
24
26
  /**
25
27
  * Report whether the namespace ends up with a Memory the executor can use.
@@ -39,3 +41,4 @@ export declare function describeStorageBackend(): Promise<BackendDetection>;
39
41
  export declare function describeNamespaceMemory(namespace: string): Promise<NamespaceMemoryCheck>;
40
42
  export declare function detectStorageBackend(): Promise<DetectedBackend>;
41
43
  export declare function runReadinessChecks(timeoutSeconds: number, backend: DetectedBackend, onProgress?: ReadinessProgress): Promise<ReadinessCheckResult[]>;
44
+ export {};
@@ -19,7 +19,7 @@ async function runKubectl(args, timeoutMs) {
19
19
  // aggregated API server serves no arkconfigs, so this is the normal reply
20
20
  // there rather than something to retry.
21
21
  const UNSERVED_RESOURCE_PATTERN = /doesn't have a resource type|no matches for kind|the server could not find the requested resource/i;
22
- function classifyFailure(stderr) {
22
+ export function classifyFailure(stderr) {
23
23
  if (/not\s*found/i.test(stderr) || UNSERVED_RESOURCE_PATTERN.test(stderr)) {
24
24
  return 'not-found';
25
25
  }
@@ -16,7 +16,7 @@ export interface KubernetesConfig {
16
16
  export type DeploymentStatus = 'available' | 'progressing' | 'replicafailure' | 'failed' | 'not found' | 'unknown';
17
17
  export type ServiceStatus = {
18
18
  name: string;
19
- status: 'healthy' | 'warning' | 'unhealthy' | 'not installed' | 'not ready';
19
+ status: 'healthy' | 'warning' | 'unhealthy' | 'not installed' | 'not ready' | 'no access';
20
20
  deploymentStatus?: DeploymentStatus;
21
21
  url?: string;
22
22
  version?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-at-scale/ark",
3
- "version": "0.1.70",
3
+ "version": "0.1.72",
4
4
  "description": "Ark CLI - Interactive terminal interface for ARK agents",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -85,6 +85,7 @@
85
85
  "node": ">=18.0.0"
86
86
  },
87
87
  "overrides": {
88
+ "vite": "^8.3.3",
88
89
  "brace-expansion": "^5.0.12",
89
90
  "ip-address": "^10.3.1",
90
91
  "js-yaml": "^4.3.1",
@@ -101,6 +102,7 @@
101
102
  "form-data": "^4.0.6",
102
103
  "postcss": "^8.5.18",
103
104
  "proxy-addr": "^2.0.8",
104
- "source-map-js": "^1.2.2"
105
+ "source-map-js": "^1.2.2",
106
+ "follow-redirects": "^1.16.1"
105
107
  }
106
108
  }
@@ -63,7 +63,7 @@ ENTRYPOINT [ "/bin/bash", "-c", "mcp-proxy --debug --port 8080 --host 0.0.0.0 --
63
63
  {{- end }}
64
64
 
65
65
  {{- else if eq .Values.technology "go" }}
66
- FROM golang:1.26.6-alpine AS go-builder
66
+ FROM golang:1.26.9-alpine AS go-builder
67
67
  {{- if .Values.packageSource }}
68
68
  {{- if eq .Values.packageSource "go-install" }}
69
69
  RUN go install {{ .Values.packageName }}@latest
@@ -96,9 +96,9 @@ metadata:
96
96
  name: sample-query
97
97
  spec:
98
98
  input: 'Hello! Can you help me understand what you can do?'
99
- targets:
100
- - type: agent
101
- name: sample-agent
99
+ target:
100
+ type: agent
101
+ name: sample-agent
102
102
  ```
103
103
 
104
104
  ## Prerequisites
@@ -13,7 +13,7 @@ kind: Query
13
13
  metadata:
14
14
  name: sample-query
15
15
  spec:
16
- targets:
17
- - name: sample-agent
18
- type: agent
16
+ target:
17
+ name: sample-agent
18
+ type: agent
19
19
  input: Calculate 3+2.
@@ -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"]
@@ -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"
@@ -53,14 +56,15 @@ wheels = [
53
56
 
54
57
  [[package]]
55
58
  name = "authlib"
56
- version = "1.6.12"
59
+ version = "1.8.0"
57
60
  source = { registry = "https://pypi.org/simple" }
58
61
  dependencies = [
59
62
  { name = "cryptography" },
63
+ { name = "joserfc" },
60
64
  ]
61
- sdist = { url = "https://files.pythonhosted.org/packages/d3/30/6691fdc63b35f54a5a65e04fa1e59d827f4d4e8f4a39678ba7d3088ce0c8/authlib-1.6.12.tar.gz", hash = "sha256:0656d8482f28fc8221929d5f35b2bde5d13e10555ebc06b4561b0d622e83b1bd", size = 165368, upload-time = "2026-05-04T08:11:31.826Z" }
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" }
62
66
  wheels = [
63
- { url = "https://files.pythonhosted.org/packages/cd/51/9b0b5cd4cf683a02db937a6f9bbebcdc9c56558a7bb3763ce7d3512103c3/authlib-1.6.12-py2.py3-none-any.whl", hash = "sha256:e9229ad7fde610b139dd12f5edbe97eab9ee78bfb85691247e767727850b99ab", size = 244473, upload-time = "2026-05-04T08:11:30.354Z" },
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" },
64
68
  ]
65
69
 
66
70
  [[package]]
@@ -448,6 +452,18 @@ wheels = [
448
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" },
449
453
  ]
450
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
+
451
467
  [[package]]
452
468
  name = "jsonref"
453
469
  version = "1.1.0"
@@ -668,10 +684,10 @@ resolution-markers = [
668
684
  "python_full_version < '3.14' and sys_platform != 'win32'",
669
685
  ]
670
686
  dependencies = [
671
- { name = "annotated-types" },
672
- { name = "pydantic-core", version = "2.33.2", source = { registry = "https://pypi.org/simple" } },
673
- { name = "typing-extensions" },
674
- { 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'" },
675
691
  ]
676
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" }
677
693
  wheels = [
@@ -680,7 +696,7 @@ wheels = [
680
696
 
681
697
  [package.optional-dependencies]
682
698
  email = [
683
- { name = "email-validator" },
699
+ { name = "email-validator", marker = "python_full_version < '3.14'" },
684
700
  ]
685
701
 
686
702
  [[package]]
@@ -692,10 +708,10 @@ resolution-markers = [
692
708
  "python_full_version >= '3.14' and sys_platform != 'win32'",
693
709
  ]
694
710
  dependencies = [
695
- { name = "annotated-types" },
696
- { name = "pydantic-core", version = "2.46.4", source = { registry = "https://pypi.org/simple" } },
697
- { name = "typing-extensions" },
698
- { 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'" },
699
715
  ]
700
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" }
701
717
  wheels = [
@@ -704,7 +720,7 @@ wheels = [
704
720
 
705
721
  [package.optional-dependencies]
706
722
  email = [
707
- { name = "email-validator" },
723
+ { name = "email-validator", marker = "python_full_version >= '3.14'" },
708
724
  ]
709
725
 
710
726
  [[package]]
@@ -716,7 +732,7 @@ resolution-markers = [
716
732
  "python_full_version < '3.14' and sys_platform != 'win32'",
717
733
  ]
718
734
  dependencies = [
719
- { name = "typing-extensions" },
735
+ { name = "typing-extensions", marker = "python_full_version < '3.14'" },
720
736
  ]
721
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" }
722
738
  wheels = [
@@ -762,7 +778,7 @@ resolution-markers = [
762
778
  "python_full_version >= '3.14' and sys_platform != 'win32'",
763
779
  ]
764
780
  dependencies = [
765
- { name = "typing-extensions" },
781
+ { name = "typing-extensions", marker = "python_full_version >= '3.14'" },
766
782
  ]
767
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" }
768
784
  wheels = [
@@ -1095,8 +1111,8 @@ name = "secretstorage"
1095
1111
  version = "3.5.0"
1096
1112
  source = { registry = "https://pypi.org/simple" }
1097
1113
  dependencies = [
1098
- { name = "cryptography" },
1099
- { name = "jeepney" },
1114
+ { name = "cryptography", marker = "sys_platform != 'win32'" },
1115
+ { name = "jeepney", marker = "sys_platform != 'win32'" },
1100
1116
  ]
1101
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" }
1102
1118
  wheels = [
@@ -1157,7 +1173,7 @@ resolution-markers = [
1157
1173
  "python_full_version < '3.14' and sys_platform != 'win32'",
1158
1174
  ]
1159
1175
  dependencies = [
1160
- { name = "typing-extensions" },
1176
+ { name = "typing-extensions", marker = "python_full_version < '3.14'" },
1161
1177
  ]
1162
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" }
1163
1179
  wheels = [
@@ -1173,7 +1189,7 @@ resolution-markers = [
1173
1189
  "python_full_version >= '3.14' and sys_platform != 'win32'",
1174
1190
  ]
1175
1191
  dependencies = [
1176
- { name = "typing-extensions" },
1192
+ { name = "typing-extensions", marker = "python_full_version >= '3.14'" },
1177
1193
  ]
1178
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" }
1179
1195
  wheels = [