@pleri/olam-cli 0.1.159 → 0.1.161

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.
Files changed (88) hide show
  1. package/README.md +11 -0
  2. package/dist/agent-stream/agent-sdk-to-chunks.js +3 -0
  3. package/dist/agent-stream/driver-runner.js +9 -4
  4. package/dist/agent-stream/host-driver-launch.js +48 -0
  5. package/dist/commands/bootstrap.d.ts +15 -0
  6. package/dist/commands/bootstrap.d.ts.map +1 -1
  7. package/dist/commands/bootstrap.js +30 -1
  8. package/dist/commands/bootstrap.js.map +1 -1
  9. package/dist/commands/flywheel/check-persona-skeleton.d.ts +30 -2
  10. package/dist/commands/flywheel/check-persona-skeleton.d.ts.map +1 -1
  11. package/dist/commands/flywheel/check-persona-skeleton.js +143 -6
  12. package/dist/commands/flywheel/check-persona-skeleton.js.map +1 -1
  13. package/dist/commands/flywheel/diversity-check.d.ts +12 -2
  14. package/dist/commands/flywheel/diversity-check.d.ts.map +1 -1
  15. package/dist/commands/flywheel/diversity-check.js +56 -6
  16. package/dist/commands/flywheel/diversity-check.js.map +1 -1
  17. package/dist/commands/flywheel/index.d.ts.map +1 -1
  18. package/dist/commands/flywheel/index.js +2 -0
  19. package/dist/commands/flywheel/index.js.map +1 -1
  20. package/dist/commands/flywheel/install-shims.d.ts +36 -3
  21. package/dist/commands/flywheel/install-shims.d.ts.map +1 -1
  22. package/dist/commands/flywheel/install-shims.js +118 -7
  23. package/dist/commands/flywheel/install-shims.js.map +1 -1
  24. package/dist/commands/flywheel/k10-measure.d.ts +12 -2
  25. package/dist/commands/flywheel/k10-measure.d.ts.map +1 -1
  26. package/dist/commands/flywheel/k10-measure.js +55 -6
  27. package/dist/commands/flywheel/k10-measure.js.map +1 -1
  28. package/dist/commands/flywheel/migrate-overlays.d.ts +115 -0
  29. package/dist/commands/flywheel/migrate-overlays.d.ts.map +1 -0
  30. package/dist/commands/flywheel/migrate-overlays.js +766 -0
  31. package/dist/commands/flywheel/migrate-overlays.js.map +1 -0
  32. package/dist/commands/flywheel/sanitize-persona-output.d.ts +33 -2
  33. package/dist/commands/flywheel/sanitize-persona-output.d.ts.map +1 -1
  34. package/dist/commands/flywheel/sanitize-persona-output.js +94 -6
  35. package/dist/commands/flywheel/sanitize-persona-output.js.map +1 -1
  36. package/dist/commands/memory/index.d.ts.map +1 -1
  37. package/dist/commands/memory/index.js +2 -0
  38. package/dist/commands/memory/index.js.map +1 -1
  39. package/dist/commands/memory/install-hooks.d.ts +22 -0
  40. package/dist/commands/memory/install-hooks.d.ts.map +1 -0
  41. package/dist/commands/memory/install-hooks.js +156 -0
  42. package/dist/commands/memory/install-hooks.js.map +1 -0
  43. package/dist/commands/skills-doctor.js +2 -2
  44. package/dist/commands/skills-doctor.js.map +1 -1
  45. package/dist/commands/skills-source.d.ts.map +1 -1
  46. package/dist/commands/skills-source.js +10 -0
  47. package/dist/commands/skills-source.js.map +1 -1
  48. package/dist/commands/skills.d.ts.map +1 -1
  49. package/dist/commands/skills.js +169 -1
  50. package/dist/commands/skills.js.map +1 -1
  51. package/dist/image-digests.json +7 -7
  52. package/dist/index.js +4361 -1768
  53. package/dist/lib/bootstrap-kubernetes.d.ts +42 -0
  54. package/dist/lib/bootstrap-kubernetes.d.ts.map +1 -0
  55. package/dist/lib/bootstrap-kubernetes.js +287 -0
  56. package/dist/lib/bootstrap-kubernetes.js.map +1 -0
  57. package/dist/lib/config.d.ts.map +1 -1
  58. package/dist/lib/config.js +6 -1
  59. package/dist/lib/config.js.map +1 -1
  60. package/dist/lib/flywheel-probes.d.ts +58 -0
  61. package/dist/lib/flywheel-probes.d.ts.map +1 -0
  62. package/dist/lib/flywheel-probes.js +163 -0
  63. package/dist/lib/flywheel-probes.js.map +1 -0
  64. package/dist/lib/shim-generator.d.ts +51 -0
  65. package/dist/lib/shim-generator.d.ts.map +1 -0
  66. package/dist/lib/shim-generator.js +88 -0
  67. package/dist/lib/shim-generator.js.map +1 -0
  68. package/dist/lib/skills-apply-overlays.d.ts +35 -0
  69. package/dist/lib/skills-apply-overlays.d.ts.map +1 -0
  70. package/dist/lib/skills-apply-overlays.js +243 -0
  71. package/dist/lib/skills-apply-overlays.js.map +1 -0
  72. package/dist/mcp-server.js +1106 -453
  73. package/hermes-bundle/version.json +1 -1
  74. package/host-cp/k8s/manifests/50-deployment.yaml +1 -1
  75. package/host-cp/k8s/manifests/auth-service/50-deployment.yaml +1 -1
  76. package/host-cp/k8s/manifests/kg-service/50-deployment.yaml +1 -1
  77. package/host-cp/k8s/manifests/mcp-auth-service/50-deployment.yaml +1 -1
  78. package/host-cp/k8s/manifests/memory-service/30-configmap.yaml +11 -0
  79. package/host-cp/k8s/manifests/memory-service/35-configmap-iii-config.yaml +76 -0
  80. package/host-cp/k8s/manifests/memory-service/50-deployment.yaml +11 -1
  81. package/host-cp/observability/grafana-port-forward.sh +273 -0
  82. package/host-cp/observability/kyverno-cardinality-mutate.sh +452 -0
  83. package/host-cp/observability/loki-ingest.sh +243 -0
  84. package/host-cp/observability/prom-no-double-grafana.sh +301 -0
  85. package/host-cp/src/crystallize-planning.mjs +261 -0
  86. package/host-cp/src/plan-chat-service.mjs +84 -2
  87. package/host-cp/src/planning-sessions.mjs +270 -0
  88. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
1
  {
2
- "bundledAt": "2026-05-21T16:31:13.129Z",
2
+ "bundledAt": "2026-05-22T07:50:42.070Z",
3
3
  "kgFirstSha": "29a9ccce1b115d049e375c4a90eb5cf7c123e610e2d0590270a4db2cdbc64a28"
4
4
  }
@@ -111,7 +111,7 @@ spec:
111
111
  # k3d), started by `olam upgrade` Step 0.7 — not inside this Pod.
112
112
  containers:
113
113
  - name: olam-host-cp
114
- image: ghcr.io/pleri/olam-host-cp@sha256:53c6548f6930231a6f905f4a3ae1f49dbc66e52233b64a09e539b4ffa21180db
114
+ image: ghcr.io/pleri/olam-host-cp@sha256:a71a02ad25f03c1481d8b5a4f3cf50614eb1f9b02376935e4df5c65b9bd4fa8f
115
115
  imagePullPolicy: IfNotPresent
116
116
  securityContext:
117
117
  runAsNonRoot: true
@@ -70,7 +70,7 @@ spec:
70
70
  mountPath: /data
71
71
  containers:
72
72
  - name: olam-auth-service
73
- image: ghcr.io/pleri/olam-auth@sha256:d1b13f12d87d5b119d6495214c26d8c8255deb996d37193f3b4fa47363ab9367
73
+ image: ghcr.io/pleri/olam-auth@sha256:7ad7f92e5feafff3921f3219886a2aec312d83e00c66eaa568e53aac03b19b16
74
74
  imagePullPolicy: IfNotPresent
75
75
  securityContext:
76
76
  runAsNonRoot: true
@@ -61,7 +61,7 @@ spec:
61
61
  mountPath: /data
62
62
  containers:
63
63
  - name: olam-kg-service
64
- image: ghcr.io/pleri/olam-kg-service@sha256:e7276f9ea4d359dcb8a0d623e701e290f51c565fc8b6e3c14bea75b1b780d23d
64
+ image: ghcr.io/pleri/olam-kg-service@sha256:9c01fd288e136116abfd0e34c7230417a30c5036e411838b88a553c44a802f13
65
65
  imagePullPolicy: IfNotPresent
66
66
  securityContext:
67
67
  runAsNonRoot: true
@@ -68,7 +68,7 @@ spec:
68
68
  mountPath: /data
69
69
  containers:
70
70
  - name: olam-mcp-auth-service
71
- image: ghcr.io/pleri/olam-mcp-auth@sha256:d53a7538ca405f4d8c0c4be67d3961617304f07623839eb0de75f9cd2b47b914
71
+ image: ghcr.io/pleri/olam-mcp-auth@sha256:ddd15d5ee0b18ed36a8916c4d8d985182f6e32b57fc2625295f5db19a14b37a0
72
72
  imagePullPolicy: IfNotPresent
73
73
  securityContext:
74
74
  runAsNonRoot: true
@@ -22,3 +22,14 @@ data:
22
22
  # AGENTMEMORY_HOST=0.0.0.0 but ConfigMap override is explicit defense against
23
23
  # a future image regression reverting to 127.0.0.1.
24
24
  AGENTMEMORY_HOST: "0.0.0.0"
25
+ # III_REST_PORT is the env var the agentmemory CLI wrapper reads when it
26
+ # polls its iii subprocess for readiness (cli.mjs:155 — `process.env
27
+ # ["III_REST_PORT"] || "3111"`). The iii engine itself binds the port
28
+ # declared in iii-config.yaml's iii-http worker (overridden via the
29
+ # olam-memory-service-iii-config ConfigMap to 3110, so it does not
30
+ # collide with the metrics-proxy on 3111). Without this env var the
31
+ # wrapper polls 3111 forever, prints "iii-engine did not become ready",
32
+ # and exits — entrypoint propagates the exit, container restarts, and
33
+ # the liveness probe returns 502 from the proxy (its backend was never
34
+ # up). Must equal the iii-http port in 35-configmap-iii-config.yaml.
35
+ III_REST_PORT: "3110"
@@ -0,0 +1,76 @@
1
+ # Overrides the iii-config.yaml shipped inside the agentmemory image so the
2
+ # iii engine binds the INTERNAL port (3110) instead of the EXTERNAL port
3
+ # (3111). The shipped yaml hardcodes `port: 3111` and the agentmemory CLI
4
+ # reads its bind from yaml (NOT from the AGENTMEMORY_PORT env var), so
5
+ # entrypoint.sh's `AGENTMEMORY_PORT=3110` override has no effect.
6
+ #
7
+ # Without this override, the engine and the metrics-proxy both try to bind
8
+ # 0.0.0.0:3111. The proxy starts first and wins the port; the engine fails
9
+ # silently. Probes to /agentmemory/livez hit the proxy and get forwarded to
10
+ # 127.0.0.1:3110, where nothing is listening — proxy returns 502, readiness
11
+ # fails, container restarts.
12
+ #
13
+ # Mounted at /usr/local/lib/node_modules/@agentmemory/agentmemory/dist/iii-config.yaml
14
+ # via subPath in 50-deployment.yaml.
15
+ apiVersion: v1
16
+ kind: ConfigMap
17
+ metadata:
18
+ name: olam-memory-service-iii-config
19
+ namespace: olam
20
+ labels:
21
+ app: olam-memory-service
22
+ olam.io/component: peripheral
23
+ data:
24
+ iii-config.yaml: |
25
+ workers:
26
+ - name: iii-http
27
+ config:
28
+ port: 3110
29
+ host: 0.0.0.0
30
+ default_timeout: 180000
31
+ cors:
32
+ allowed_origins: ["http://localhost:3111", "http://localhost:3113", "http://127.0.0.1:3111", "http://127.0.0.1:3113"]
33
+ allowed_methods: [GET, POST, PUT, DELETE, OPTIONS]
34
+ - name: iii-state
35
+ config:
36
+ adapter:
37
+ name: kv
38
+ config:
39
+ store_method: file_based
40
+ file_path: ./data/state_store.db
41
+ - name: iii-queue
42
+ config:
43
+ adapter:
44
+ name: builtin
45
+ - name: iii-pubsub
46
+ config:
47
+ adapter:
48
+ name: local
49
+ - name: iii-cron
50
+ config:
51
+ adapter:
52
+ name: kv
53
+ - name: iii-stream
54
+ config:
55
+ port: 3112
56
+ host: 0.0.0.0
57
+ adapter:
58
+ name: kv
59
+ config:
60
+ store_method: file_based
61
+ file_path: ./data/stream_store
62
+ - name: iii-observability
63
+ config:
64
+ enabled: true
65
+ service_name: agentmemory
66
+ exporter: memory
67
+ sampling_ratio: 1.0
68
+ metrics_enabled: true
69
+ logs_enabled: true
70
+ logs_console_output: true
71
+ - name: iii-exec
72
+ config:
73
+ watch:
74
+ - src/**/*.ts
75
+ exec:
76
+ - node dist/index.mjs
@@ -70,7 +70,7 @@ spec:
70
70
  # bootstrap-placeholder comment + run `npm run refresh:manifest-digests`
71
71
  # once ghcr.io/pleri/olam-memory-service has a real published digest.
72
72
  # bootstrap-placeholder: pre-publish; refresh after first release
73
- image: ghcr.io/pleri/olam-memory-service@sha256:023bde810b0594829c8aa553b88a64cf53b81b6374085c7a92fb6102450fa3ff
73
+ image: ghcr.io/pleri/olam-memory-service@sha256:8dd1593af37b345a9b9b741803355254f1f719d9bf23e56339d9baed8dea9ac1
74
74
  imagePullPolicy: IfNotPresent
75
75
  securityContext:
76
76
  runAsNonRoot: true
@@ -93,6 +93,13 @@ spec:
93
93
  mountPath: /data
94
94
  - name: tmp
95
95
  mountPath: /tmp
96
+ # Overrides the shipped iii-config.yaml so the engine binds the
97
+ # internal port (3110) instead of colliding with the metrics-proxy
98
+ # on 3111. See 35-configmap-iii-config.yaml for full rationale.
99
+ - name: iii-config-override
100
+ mountPath: /usr/local/lib/node_modules/@agentmemory/agentmemory/dist/iii-config.yaml
101
+ subPath: iii-config.yaml
102
+ readOnly: true
96
103
  readinessProbe:
97
104
  httpGet:
98
105
  # D15 (LOAD-BEARING): memory-service health path is /agentmemory/livez.
@@ -126,3 +133,6 @@ spec:
126
133
  claimName: olam-memory-data
127
134
  - name: tmp
128
135
  emptyDir: {}
136
+ - name: iii-config-override
137
+ configMap:
138
+ name: olam-memory-service-iii-config
@@ -0,0 +1,273 @@
1
+ #!/usr/bin/env bash
2
+ # grafana-port-forward.sh — e2e smoke test: Grafana installs via Helm,
3
+ # port-forward is accessible, Loki datasource
4
+ # is pre-wired and reachable.
5
+ #
6
+ # Usage: scripts/e2e/grafana-port-forward.sh
7
+ #
8
+ # Pre-conditions:
9
+ # - kubectl context is set to a live k8s cluster (does NOT spin up k3d)
10
+ # - helm binary available
11
+ # - jq binary available
12
+ # - grafana Helm repo added (helm repo add grafana https://grafana.github.io/helm-charts)
13
+ # - Loki is already installed (scripts/e2e/loki-ingest.sh ran successfully
14
+ # OR `helm status olam-loki -n monitoring` is healthy)
15
+ #
16
+ # Idempotency: `helm upgrade --install` is idempotent; re-runs succeed on an
17
+ # existing cluster. The Secret is applied via --dry-run | kubectl apply
18
+ # so re-runs update the password (useful for rotation testing).
19
+ # The olam-dashboards ConfigMap is applied before helm install so
20
+ # Grafana's volume mount finds the ConfigMap on first boot.
21
+ #
22
+ # Cleanup: port-forward is killed on exit; Helm release is left in place so
23
+ # downstream tasks can reuse the same cluster.
24
+ #
25
+ # Refs: docs/plans/k3s-ingress-observability/phase-b-tasks.md — Task B2, B3
26
+ # Chart: grafana/grafana 8.5.2 (pinned; latest stable 2026-05-20)
27
+
28
+ set -euo pipefail
29
+
30
+ NAMESPACE="monitoring"
31
+ GRAFANA_RELEASE="olam-grafana"
32
+ GRAFANA_CHART_VERSION="8.5.2"
33
+ LOCAL_PORT="3000"
34
+ GRAFANA_SVC_PORT="80"
35
+ PF_BIND_SECONDS=5
36
+
37
+ log() { printf '[grafana-port-forward] %s\n' "$*" >&2; }
38
+ fail() { printf '[grafana-port-forward] FAIL: %s\n' "$*" >&2; exit 1; }
39
+
40
+ # -------------------------------------------------------------------------
41
+ # Cleanup trap — kill port-forward on exit; leave Helm release in place
42
+ # -------------------------------------------------------------------------
43
+ PF_PID=""
44
+ cleanup() {
45
+ if [[ -n "$PF_PID" ]] && kill -0 "$PF_PID" 2>/dev/null; then
46
+ kill "$PF_PID" 2>/dev/null || true
47
+ fi
48
+ }
49
+ trap cleanup EXIT
50
+
51
+ # -------------------------------------------------------------------------
52
+ # Pre-flight
53
+ # -------------------------------------------------------------------------
54
+ command -v helm >/dev/null 2>&1 || fail "helm not installed"
55
+ command -v kubectl >/dev/null 2>&1 || fail "kubectl not installed"
56
+ command -v curl >/dev/null 2>&1 || fail "curl not installed"
57
+ command -v openssl >/dev/null 2>&1 || fail "openssl not installed"
58
+ command -v jq >/dev/null 2>&1 || fail "jq not installed (required for B3 dashboard assertion)"
59
+ kubectl cluster-info >/dev/null 2>&1 || fail "kubectl: no reachable cluster; set KUBECONFIG"
60
+
61
+ log "pre-flight checks passed"
62
+
63
+ # -------------------------------------------------------------------------
64
+ # Ensure grafana Helm repo is present (idempotent — safe to re-run)
65
+ # -------------------------------------------------------------------------
66
+ helm repo add grafana https://grafana.github.io/helm-charts 2>/dev/null || true
67
+ helm repo update grafana
68
+
69
+ # Verify Loki is already installed (B2 depends on B1)
70
+ if ! helm status "olam-loki" -n "$NAMESPACE" >/dev/null 2>&1; then
71
+ fail "olam-loki Helm release not found in namespace $NAMESPACE — run scripts/e2e/loki-ingest.sh first"
72
+ fi
73
+ log "Loki pre-condition satisfied (olam-loki release found)"
74
+
75
+ # -------------------------------------------------------------------------
76
+ # Step 1: Resolve admin password (preserve existing on idempotent re-run)
77
+ # -------------------------------------------------------------------------
78
+ # Grafana persists the admin password in its internal SQLite on first
79
+ # deploy. Subsequent helm upgrades do NOT re-read GF_SECURITY_ADMIN_PASSWORD
80
+ # from the env (env value is set once at pod-start and not refreshed). So
81
+ # on a re-run, rotating the Secret leaves the in-Grafana password stale
82
+ # and breaks API auth.
83
+ #
84
+ # Idempotency contract: if the Secret already exists, reuse its current
85
+ # password. The Secret's value matches Grafana's stored value (set in
86
+ # concert on first install). Only generate a new password when the
87
+ # Secret doesn't exist yet — i.e. true first deploy.
88
+ if kubectl get secret olam-grafana-admin -n "$NAMESPACE" >/dev/null 2>&1; then
89
+ log "reusing existing admin password from Secret olam-grafana-admin"
90
+ GRAFANA_ADMIN_PW=$(kubectl get secret olam-grafana-admin -n "$NAMESPACE" \
91
+ -o jsonpath='{.data.admin-password}' | base64 -d)
92
+ else
93
+ log "generating fresh admin password (first deploy)"
94
+ GRAFANA_ADMIN_PW=$(openssl rand -base64 24)
95
+ fi
96
+ export GRAFANA_ADMIN_PW
97
+
98
+ # -------------------------------------------------------------------------
99
+ # Step 2: Create / update the admin Secret idempotently
100
+ # -------------------------------------------------------------------------
101
+ log "applying Secret olam-grafana-admin in namespace $NAMESPACE"
102
+ kubectl create secret generic olam-grafana-admin \
103
+ --from-literal=admin-user=admin \
104
+ --from-literal=admin-password="$GRAFANA_ADMIN_PW" \
105
+ -n "$NAMESPACE" \
106
+ --dry-run=client -o yaml \
107
+ | kubectl apply -f -
108
+
109
+ log "Secret applied"
110
+
111
+ # -------------------------------------------------------------------------
112
+ # Step 3a: Apply olam-dashboards ConfigMap BEFORE helm install
113
+ # so Grafana's volume mount finds it on first boot (B3).
114
+ # The ConfigMap is generated from grafana-dashboards/*.json by
115
+ # packages/peripheral-services/scripts/sync-grafana-dashboards.sh.
116
+ # -------------------------------------------------------------------------
117
+ REPO_ROOT="$(git -C "$(dirname "$0")" rev-parse --show-toplevel 2>/dev/null || pwd)"
118
+ CONFIGMAP_MANIFEST="$REPO_ROOT/packages/peripheral-services/manifests/80-grafana-dashboard-configmap.yaml"
119
+
120
+ if [[ -f "$CONFIGMAP_MANIFEST" ]]; then
121
+ log "applying olam-dashboards ConfigMap from $CONFIGMAP_MANIFEST"
122
+ kubectl apply -f "$CONFIGMAP_MANIFEST"
123
+ log "ConfigMap applied"
124
+ else
125
+ log "WARN: $CONFIGMAP_MANIFEST not found — Grafana will warn 'ConfigMap not found' until B3 is deployed"
126
+ fi
127
+
128
+ # -------------------------------------------------------------------------
129
+ # Step 3: Helm upgrade --install
130
+ # -------------------------------------------------------------------------
131
+ log "installing grafana/grafana ($GRAFANA_RELEASE) in namespace $NAMESPACE"
132
+ helm upgrade --install "$GRAFANA_RELEASE" grafana/grafana \
133
+ --version "$GRAFANA_CHART_VERSION" \
134
+ --namespace "$NAMESPACE" \
135
+ --create-namespace \
136
+ -f "$REPO_ROOT/packages/peripheral-services/helm-values/grafana-values.yaml" \
137
+ --wait \
138
+ --timeout 300s
139
+
140
+ log "Grafana Helm install complete"
141
+
142
+ # -------------------------------------------------------------------------
143
+ # Step 4: Wait for Grafana pod Ready
144
+ # -------------------------------------------------------------------------
145
+ log "waiting for Grafana pod Ready (120s)"
146
+ kubectl wait \
147
+ --for=condition=ready pod \
148
+ -l "app.kubernetes.io/name=grafana" \
149
+ -n "$NAMESPACE" \
150
+ --timeout=120s
151
+
152
+ log "Grafana pod Ready"
153
+
154
+ # -------------------------------------------------------------------------
155
+ # Step 5: Start port-forward in background
156
+ # -------------------------------------------------------------------------
157
+ log "port-forwarding svc/$GRAFANA_RELEASE $LOCAL_PORT:$GRAFANA_SVC_PORT in namespace $NAMESPACE"
158
+ kubectl port-forward \
159
+ -n "$NAMESPACE" \
160
+ "svc/$GRAFANA_RELEASE" \
161
+ "${LOCAL_PORT}:${GRAFANA_SVC_PORT}" &
162
+ PF_PID=$!
163
+
164
+ log "port-forward PID $PF_PID; waiting ${PF_BIND_SECONDS}s for bind"
165
+ sleep "$PF_BIND_SECONDS"
166
+
167
+ # Verify the port-forward process is still alive after sleep
168
+ kill -0 "$PF_PID" 2>/dev/null || fail "port-forward process exited prematurely"
169
+
170
+ # -------------------------------------------------------------------------
171
+ # Diagnostic helper — called on assertion failure
172
+ # -------------------------------------------------------------------------
173
+ dump_diagnostics() {
174
+ log "DIAGNOSTIC: last 50 lines of Grafana pod logs:"
175
+ kubectl logs -n "$NAMESPACE" \
176
+ -l "app.kubernetes.io/name=grafana" \
177
+ --tail=50 2>&1 >&2 || true
178
+ }
179
+
180
+ # -------------------------------------------------------------------------
181
+ # Step 6: Assertion 1 — /api/health returns 200 with database: ok
182
+ # -------------------------------------------------------------------------
183
+ log "asserting Grafana health (GET /api/health)"
184
+ HEALTH_RESPONSE=$(
185
+ curl -sf \
186
+ -u "admin:${GRAFANA_ADMIN_PW}" \
187
+ "http://localhost:${LOCAL_PORT}/api/health" \
188
+ || { dump_diagnostics; fail "GET /api/health failed — Grafana not reachable on port $LOCAL_PORT"; }
189
+ )
190
+
191
+ if ! echo "$HEALTH_RESPONSE" | jq -e '.database == "ok"' >/dev/null 2>&1; then
192
+ log "DIAGNOSTIC: /api/health response:"
193
+ echo "$HEALTH_RESPONSE" >&2
194
+ dump_diagnostics
195
+ fail '/api/health returned database != "ok" — Grafana DB layer not healthy'
196
+ fi
197
+
198
+ log "PASS: /api/health → database: ok"
199
+
200
+ # -------------------------------------------------------------------------
201
+ # Step 7: Assertion 2 — /api/datasources includes Loki entry with cluster URL
202
+ # -------------------------------------------------------------------------
203
+ log "asserting Loki datasource pre-wired (GET /api/datasources)"
204
+ DS_RESPONSE=$(
205
+ curl -sf \
206
+ -u "admin:${GRAFANA_ADMIN_PW}" \
207
+ "http://localhost:${LOCAL_PORT}/api/datasources" \
208
+ || { dump_diagnostics; fail "GET /api/datasources failed"; }
209
+ )
210
+
211
+ EXPECTED_URL="olam-loki.monitoring.svc.cluster.local:3100"
212
+
213
+ if ! echo "$DS_RESPONSE" | jq -e 'map(select(.type == "loki")) | length >= 1' >/dev/null 2>&1; then
214
+ log "DIAGNOSTIC: /api/datasources response:"
215
+ echo "$DS_RESPONSE" >&2
216
+ dump_diagnostics
217
+ fail "datasources response contains no 'loki' type entry — datasource not provisioned"
218
+ fi
219
+
220
+ if ! echo "$DS_RESPONSE" | jq -e --arg url "$EXPECTED_URL" 'map(select(.type == "loki" and (.url | contains($url)))) | length >= 1' >/dev/null 2>&1; then
221
+ log "DIAGNOSTIC: /api/datasources response:"
222
+ echo "$DS_RESPONSE" >&2
223
+ dump_diagnostics
224
+ fail "Loki datasource URL does not contain '$EXPECTED_URL' — check grafana-values.yaml datasources block"
225
+ fi
226
+
227
+ log "PASS: Loki datasource found with cluster-local URL $EXPECTED_URL"
228
+
229
+ # -------------------------------------------------------------------------
230
+ # Step 7b: Assertion 2b — dashboard provider loaded olam-home (catches mount-path bugs)
231
+ # -------------------------------------------------------------------------
232
+ log "asserting olam-home dashboard visible in /api/search (catches ConfigMap mount failures)"
233
+ DASHBOARDS=$(
234
+ curl -sf \
235
+ -u "admin:${GRAFANA_ADMIN_PW}" \
236
+ "http://localhost:${LOCAL_PORT}/api/search?type=dash-db&query=olam" \
237
+ || true
238
+ )
239
+
240
+ if ! echo "$DASHBOARDS" | jq -e 'map(select(.uid == "olam-home")) | length == 1' >/dev/null 2>&1; then
241
+ log "DIAGNOSTIC: /api/search response:"
242
+ echo "$DASHBOARDS" >&2
243
+ dump_diagnostics
244
+ fail "olam-home dashboard not found in /api/search — check ConfigMap mount path and dashboard provider config"
245
+ fi
246
+
247
+ log "PASS: olam-home dashboard found via /api/search"
248
+
249
+ # -------------------------------------------------------------------------
250
+ # Step 8: Assertion 3 — olam-home dashboard present (B3)
251
+ # -------------------------------------------------------------------------
252
+ log "asserting olam-home dashboard present (GET /api/dashboards/uid/olam-home)"
253
+ DASHBOARD_RESPONSE=$(
254
+ curl -sf \
255
+ -u "admin:${GRAFANA_ADMIN_PW}" \
256
+ "http://localhost:${LOCAL_PORT}/api/dashboards/uid/olam-home" \
257
+ || { dump_diagnostics; fail "GET /api/dashboards/uid/olam-home failed — dashboard not found or Grafana unreachable"; }
258
+ )
259
+
260
+ if ! echo "$DASHBOARD_RESPONSE" | jq -e '.dashboard.uid == "olam-home"' >/dev/null 2>&1; then
261
+ log "DIAGNOSTIC: /api/dashboards/uid/olam-home response:"
262
+ echo "$DASHBOARD_RESPONSE" >&2
263
+ dump_diagnostics
264
+ fail "olam-home dashboard uid mismatch or missing — check ConfigMap provisioning and Grafana provider config"
265
+ fi
266
+
267
+ log "PASS: olam-home dashboard present with uid=olam-home"
268
+
269
+ # -------------------------------------------------------------------------
270
+ # Final
271
+ # -------------------------------------------------------------------------
272
+ log "PASS: Grafana port-forward accessible; Loki datasource pre-wired; olam-home dashboard provisioned — Tasks B2+B3 verified"
273
+ exit 0