@elinpf/dsh-ops-tool-environment 0.1.0

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/lib/types.d.ts ADDED
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Shared shapes for the environment inventory scanner core.
3
+ *
4
+ * Data flow: scanCluster (raw k8s reads) -> ClusterScan; buildClusterInventory
5
+ * (classification + relations) -> ClusterInventory; inventory.ts persists
6
+ * ClusterInventory per cluster section. Everything here is plain data —
7
+ * no credential paths, no Secret values ever appear in these shapes.
8
+ *
9
+ * @module @elinpf/dsh-ops-tool-environment
10
+ */
11
+ /** Reference to a namespaced k8s object. */
12
+ export interface ResourceRef {
13
+ kind: string;
14
+ namespace: string;
15
+ name: string;
16
+ }
17
+ /** A workload (Deployment / StatefulSet / DaemonSet) reduced to scan-relevant fields. */
18
+ export interface ScannedWorkload {
19
+ kind: 'Deployment' | 'StatefulSet' | 'DaemonSet';
20
+ namespace: string;
21
+ name: string;
22
+ /** All container images (init containers included), as written in the spec. */
23
+ images: string[];
24
+ /** Workload metadata labels. */
25
+ labels: Record<string, string>;
26
+ /** Pod template labels — the set a Service selector matches against. */
27
+ podLabels: Record<string, string>;
28
+ /** Plaintext env values only: { VAR: value }. valueFrom entries never appear here. */
29
+ env: Record<string, string>;
30
+ /** ConfigMap names referenced via envFrom.configMapRef or env.valueFrom.configMapKeyRef. */
31
+ configMapRefs: string[];
32
+ /** Secret names referenced via envFrom.secretRef or env.valueFrom.secretKeyRef — names only, never values. */
33
+ secretRefs: string[];
34
+ }
35
+ export interface ScannedService {
36
+ namespace: string;
37
+ name: string;
38
+ /** ClusterIP / NodePort / LoadBalancer / ExternalName. */
39
+ type: string;
40
+ clusterIP?: string;
41
+ externalName?: string;
42
+ ports: Array<{
43
+ port: number;
44
+ targetPort?: number | string;
45
+ name?: string;
46
+ }>;
47
+ /** Pod selector; null for selector-less Services (external endpoints). */
48
+ selector: Record<string, string> | null;
49
+ labels: Record<string, string>;
50
+ }
51
+ export interface ScannedIngress {
52
+ namespace: string;
53
+ name: string;
54
+ hosts: string[];
55
+ /** Service backends referenced by the rules. */
56
+ serviceBackends: Array<{
57
+ serviceName: string;
58
+ servicePort?: number | string;
59
+ }>;
60
+ }
61
+ export interface ScannedConfigMap {
62
+ namespace: string;
63
+ name: string;
64
+ /** Plain configuration — ConfigMap data is non-secret by design. */
65
+ data: Record<string, string>;
66
+ }
67
+ /** Secret metadata only. The scanner never requests `data`/`stringData`. */
68
+ export interface ScannedSecret {
69
+ namespace: string;
70
+ name: string;
71
+ }
72
+ /** An Endpoints object reduced to what anomaly detection needs. */
73
+ export interface ScannedEndpoints {
74
+ namespace: string;
75
+ name: string;
76
+ /** Ready backend address count (sum of subsets[].addresses). */
77
+ addresses: number;
78
+ }
79
+ /**
80
+ * A rule-detected anomaly in a cluster section. Rules only use generic k8s
81
+ * semantics (namespace comparison, selector/endpoints agreement) — never
82
+ * environment-specific names.
83
+ */
84
+ export interface Anomaly {
85
+ kind: 'cross-namespace-ref' | 'service-no-backend';
86
+ severity: 'info' | 'warning';
87
+ /** The object the anomaly is about (the referencing workload, or the backend-less Service). */
88
+ ref: ResourceRef;
89
+ /** The other side, for cross-namespace references. */
90
+ related?: ResourceRef;
91
+ /** One-line human-readable statement. */
92
+ message: string;
93
+ }
94
+ /** One active Prometheus target, reduced to what matching needs. */
95
+ export interface PromTarget {
96
+ namespace?: string;
97
+ pod?: string;
98
+ service?: string;
99
+ job?: string;
100
+ /** Prometheus health string: 'up' | 'down' | 'unknown'. */
101
+ health: string;
102
+ }
103
+ /** Compact per-workload monitoring status attached to inventory entries. */
104
+ export interface MonitoringStatus {
105
+ up: number;
106
+ down: number;
107
+ }
108
+ /** rook-ceph footprint hints discovered in a cluster (ticket 15). */
109
+ export interface CephHints {
110
+ /** CephBlockPool CRs — the pool names to use with the ceph tool (`rbd ls -p <name>`). */
111
+ pools: {
112
+ namespace: string;
113
+ name: string;
114
+ }[];
115
+ /** CephCluster CRs. */
116
+ clusters: {
117
+ namespace: string;
118
+ name: string;
119
+ }[];
120
+ /**
121
+ * rook-ceph-tools pod location when one is deployed (in-cluster ceph
122
+ * CLI); absent = none deployed, which is itself useful to know.
123
+ */
124
+ toolsPod?: {
125
+ namespace: string;
126
+ name: string;
127
+ };
128
+ }
129
+ /** Raw read of one cluster — the scanner's output before classification. */
130
+ export interface ClusterScan {
131
+ /** Cluster profile id (the ops-access registry key), never a path. */
132
+ cluster: string;
133
+ /** ISO timestamp of the scan. */
134
+ scannedAt: string;
135
+ workloads: ScannedWorkload[];
136
+ services: ScannedService[];
137
+ ingresses: ScannedIngress[];
138
+ configMaps: ScannedConfigMap[];
139
+ secrets: ScannedSecret[];
140
+ /**
141
+ * Endpoints read (ready-address counts per Service). Undefined when the
142
+ * read failed — detectors that need it then skip, rather than guessing.
143
+ */
144
+ endpoints?: ScannedEndpoints[];
145
+ /**
146
+ * Prometheus corroboration, when a Prometheus service was discovered and
147
+ * scraped (undefined otherwise — enhancement, never a hard dependency).
148
+ */
149
+ prometheus?: {
150
+ service: string;
151
+ targets: PromTarget[];
152
+ };
153
+ /**
154
+ * rook-ceph hints, when any were found (pools/clusters/tools pod).
155
+ * Absent both when the cluster has no rook footprint AND when the reads
156
+ * failed — absence of evidence is not annotated.
157
+ */
158
+ ceph?: CephHints;
159
+ }
160
+ /** A workload after classification. `type` is a middleware name, 'infra', or 'unknown'. */
161
+ export interface ClassifiedWorkload extends ScannedWorkload {
162
+ type: string;
163
+ /** Prometheus up/down counts, when the cluster has a scrapable Prometheus. */
164
+ monitoring?: MonitoringStatus;
165
+ }
166
+ /** A recognized middleware instance: workload + the Services fronting it. */
167
+ export interface MiddlewareInstance {
168
+ /** Middleware type from the classification table (e.g. 'redis', 'mysql'). */
169
+ type: string;
170
+ namespace: string;
171
+ /** Workload name. */
172
+ workload: string;
173
+ workloadKind: string;
174
+ images: string[];
175
+ /** Names of Services whose selector matches this instance's pod labels. */
176
+ serviceEntries: string[];
177
+ /** Prometheus up/down counts, when available. */
178
+ monitoring?: MonitoringStatus;
179
+ }
180
+ /**
181
+ * A best-effort relation edge.
182
+ *
183
+ * - `uses-service` workload -> Service (found a svc FQDN in env/ConfigMap values)
184
+ * - `fronts` Service -> workload (Service selector matches pod labels)
185
+ * - `uses-middleware` workload -> middleware instance (uses-service + fronts composed)
186
+ * - `references-secret` workload -> Secret (envFrom/env secretRef; the name only)
187
+ */
188
+ export interface RelationEdge {
189
+ kind: 'uses-service' | 'fronts' | 'uses-middleware' | 'references-secret';
190
+ from: ResourceRef;
191
+ to: ResourceRef;
192
+ /** Where the link was seen, e.g. 'env:PG_HOST' or 'configmap:app-config:PG_HOST'. */
193
+ via: string;
194
+ /** Middleware type, set on uses-middleware edges. */
195
+ targetType?: string;
196
+ }
197
+ /** The per-cluster section that lands in environment.yaml. */
198
+ export interface ClusterInventory {
199
+ /** ISO timestamp of the scan that produced this section. */
200
+ scannedAt: string;
201
+ /** Middleware instances recognized by the classification table. */
202
+ middleware: MiddlewareInstance[];
203
+ /** Rule-detected anomalies (cross-namespace references, backend-less Services). */
204
+ anomalies: Anomaly[];
205
+ /**
206
+ * Every scanned workload with its classification. Workloads with
207
+ * type 'unknown' are the unknown bucket — listed, never dropped.
208
+ */
209
+ workloads: ClassifiedWorkload[];
210
+ services: ScannedService[];
211
+ ingresses: ScannedIngress[];
212
+ edges: RelationEdge[];
213
+ /** The Prometheus service this cluster's monitoring data came from. */
214
+ prometheusService?: string;
215
+ /** rook-ceph footprint hints carried through from the scan. */
216
+ ceph?: CephHints;
217
+ }
package/lib/types.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Shared shapes for the environment inventory scanner core.
3
+ *
4
+ * Data flow: scanCluster (raw k8s reads) -> ClusterScan; buildClusterInventory
5
+ * (classification + relations) -> ClusterInventory; inventory.ts persists
6
+ * ClusterInventory per cluster section. Everything here is plain data —
7
+ * no credential paths, no Secret values ever appear in these shapes.
8
+ *
9
+ * @module @elinpf/dsh-ops-tool-environment
10
+ */
11
+ export {};
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@elinpf/dsh-ops-tool-environment",
3
+ "version": "0.1.0",
4
+ "description": "Environment inventory for ops mode — read-only scanner that maps registered k8s clusters (workloads, services, middleware instances, best-effort relation edges) into ~/.dsh-ops/environment.yaml, plus the model-facing environment tool (overview/show/refresh). Deterministic code only, zero LLM.",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/index.d.ts",
11
+ "default": "./lib/index.js"
12
+ },
13
+ "./prompt": {
14
+ "types": "./lib/prompt.d.ts",
15
+ "default": "./lib/prompt.js"
16
+ },
17
+ "./package.json": "./package.json"
18
+ },
19
+ "files": [
20
+ "lib/**/*.js",
21
+ "lib/**/*.d.ts",
22
+ "cordis.patch.yml"
23
+ ],
24
+ "dsh": {
25
+ "bundle": {
26
+ "patch": "./cordis.patch.yml"
27
+ }
28
+ },
29
+ "dependencies": {
30
+ "@deepseek-ai/schemastery": "^3.18.1",
31
+ "yaml": "^2.7.0"
32
+ },
33
+ "peerDependencies": {
34
+ "@deepseek-ai/cordis": "^4.0.1",
35
+ "@elinpf/dsh-ops-access": "^0.1.0",
36
+ "@elinpf/dsh-ops-prompts": "^0.1.0",
37
+ "@deepseek-ai/dsh-tools": "^0.0.1-rc.1"
38
+ },
39
+ "devDependencies": {
40
+ "@deepseek-ai/cordis": "4.0.1",
41
+ "@deepseek-ai/dsh-tools": "0.0.1-rc.1",
42
+ "@types/node": "^22.0.0",
43
+ "typescript": "^5.4.0",
44
+ "vitest": "^4.1.11",
45
+ "@elinpf/dsh-ops-access": "0.1.0",
46
+ "@elinpf/dsh-ops-prompts": "0.1.0"
47
+ },
48
+ "license": "MIT",
49
+ "publishConfig": {
50
+ "access": "public"
51
+ },
52
+ "scripts": {
53
+ "build": "tsc",
54
+ "typecheck": "tsc --noEmit",
55
+ "test": "vitest run"
56
+ }
57
+ }