@prism-d1/cli 1.0.26 → 1.0.28

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 (56) hide show
  1. package/dist/assets/eval-harness/README.md +114 -0
  2. package/dist/assets/eval-harness/eval-config.json +10 -0
  3. package/dist/assets/eval-harness/rubrics/agent-quality.json +79 -0
  4. package/dist/assets/eval-harness/rubrics/api-response-quality.json +45 -0
  5. package/dist/assets/eval-harness/rubrics/code-quality.json +98 -0
  6. package/dist/assets/eval-harness/rubrics/security-compliance.json +145 -0
  7. package/dist/assets/eval-harness/rubrics/spec-compliance.json +67 -0
  8. package/dist/assets/eval-harness/run-eval.sh +122 -0
  9. package/dist/assets/github-workflows/README.md +110 -0
  10. package/dist/assets/github-workflows/prism-agent-eval.yml +313 -0
  11. package/dist/assets/github-workflows/prism-ai-metrics.yml +261 -0
  12. package/dist/assets/github-workflows/prism-dora-weekly.yml +334 -0
  13. package/dist/assets/github-workflows/prism-eval-gate.yml +310 -0
  14. package/dist/assets/infra/bin/app.ts +56 -0
  15. package/dist/assets/infra/cdk.json +12 -0
  16. package/dist/assets/infra/lib/api-stack.ts +347 -0
  17. package/dist/assets/infra/lib/constructs/bedrock-guardrail-construct.ts +201 -0
  18. package/dist/assets/infra/lib/constructs/guardrail-enforcer-construct.ts +59 -0
  19. package/dist/assets/infra/lib/constructs/prism-vpc-construct.ts +75 -0
  20. package/dist/assets/infra/lib/constructs/security-agent-construct.ts +266 -0
  21. package/dist/assets/infra/lib/dashboard-stack.ts +1392 -0
  22. package/dist/assets/infra/lib/lambda/api-handler.ts +477 -0
  23. package/dist/assets/infra/lib/lambda/defect-correlator.ts +142 -0
  24. package/dist/assets/infra/lib/lambda/exfiltration-detector.ts +100 -0
  25. package/dist/assets/infra/lib/lambda/layers/guardrail-enforcer/nodejs/guardrail-enforcer.js +53 -0
  26. package/dist/assets/infra/lib/lambda/metrics-processor.ts +748 -0
  27. package/dist/assets/infra/lib/lambda/security-agent-processor.ts +231 -0
  28. package/dist/assets/infra/lib/lambda/security-remediation-tracker.ts +120 -0
  29. package/dist/assets/infra/lib/lambda/security-response-automator.ts +130 -0
  30. package/dist/assets/infra/lib/lambda/spec-to-code-calculator.ts +123 -0
  31. package/dist/assets/infra/lib/metrics-pipeline-stack.ts +701 -0
  32. package/dist/assets/infra/package.json +23 -0
  33. package/dist/assets/infra/tsconfig.json +24 -0
  34. package/dist/src/commands/bootstrapper/install-eval-harness.d.ts.map +1 -1
  35. package/dist/src/commands/bootstrapper/install-eval-harness.js +3 -4
  36. package/dist/src/commands/bootstrapper/install-eval-harness.js.map +1 -1
  37. package/dist/src/commands/bootstrapper/install-git-hooks.d.ts.map +1 -1
  38. package/dist/src/commands/bootstrapper/install-git-hooks.js +2 -5
  39. package/dist/src/commands/bootstrapper/install-git-hooks.js.map +1 -1
  40. package/dist/src/commands/securityagent/setup.d.ts.map +1 -1
  41. package/dist/src/commands/securityagent/setup.js +2 -3
  42. package/dist/src/commands/securityagent/setup.js.map +1 -1
  43. package/dist/src/commands/workshop/deploy-infra.d.ts.map +1 -1
  44. package/dist/src/commands/workshop/deploy-infra.js +2 -3
  45. package/dist/src/commands/workshop/deploy-infra.js.map +1 -1
  46. package/dist/src/commands/workshop/generate-demo-data.d.ts.map +1 -1
  47. package/dist/src/commands/workshop/generate-demo-data.js +3 -8
  48. package/dist/src/commands/workshop/generate-demo-data.js.map +1 -1
  49. package/dist/src/commands/workshop/perform-pen-test.d.ts.map +1 -1
  50. package/dist/src/commands/workshop/perform-pen-test.js +5 -14
  51. package/dist/src/commands/workshop/perform-pen-test.js.map +1 -1
  52. package/dist/src/utils/root.d.ts +6 -0
  53. package/dist/src/utils/root.d.ts.map +1 -1
  54. package/dist/src/utils/root.js +29 -0
  55. package/dist/src/utils/root.js.map +1 -1
  56. package/package.json +2 -2
@@ -0,0 +1,310 @@
1
+ # prism-eval-gate.yml — Evaluate AI-generated code quality on PRs.
2
+ #
3
+ # Copy this file to .github/workflows/prism-eval-gate.yml in your repo.
4
+ #
5
+ # Required setup:
6
+ # - OIDC provider configured for GitHub Actions in your AWS account
7
+ # - IAM role with bedrock:InvokeModel and events:PutEvents permissions
8
+ # - Repository secret PRISM_METRICS_ROLE_ARN set to the OIDC role ARN
9
+ # - .prism/eval-harness/ directory in your repo (run: bash prism-cli bootstrapper install-eval-harness)
10
+ # - At least one rubric JSON in .prism/eval-harness/rubrics/
11
+
12
+ name: PRISM Eval Gate
13
+
14
+ on:
15
+ pull_request:
16
+ types: [opened, synchronize, reopened]
17
+ branches: [main, master]
18
+
19
+ permissions:
20
+ id-token: write
21
+ contents: read
22
+ pull-requests: write
23
+
24
+ jobs:
25
+ eval-gate:
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - name: Checkout
29
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
30
+ with:
31
+ fetch-depth: 0
32
+
33
+ - name: Configure AWS credentials (OIDC)
34
+ uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1
35
+ with:
36
+ role-to-assume: ${{ secrets.PRISM_METRICS_ROLE_ARN }}
37
+ aws-region: us-west-2
38
+
39
+ - name: Read team ID
40
+ id: prism-config
41
+ run: |
42
+ TEAM_ID=$(jq -r '.team_id // "no_team"' .prism/config.json 2>/dev/null || echo "no_team")
43
+ echo "team_id=${TEAM_ID}" >> "$GITHUB_OUTPUT"
44
+
45
+ - name: Install dependencies
46
+ run: sudo apt-get update -qq && sudo apt-get install -y -qq jq bc
47
+
48
+ - name: Identify AI-generated changes
49
+ id: detect-ai
50
+ run: |
51
+ BASE_SHA="${{ github.event.pull_request.base.sha }}"
52
+ HEAD_SHA="${{ github.sha }}"
53
+
54
+ AI_SHAS=""
55
+ for SHA in $(git rev-list "${BASE_SHA}..${HEAD_SHA}"); do
56
+ if git log -1 --format='%B' "${SHA}" | grep -q 'AI-Origin: ai-generated\|AI-Origin: ai-assisted'; then
57
+ AI_SHAS="${AI_SHAS} ${SHA}"
58
+ fi
59
+ done
60
+ AI_SHAS=$(echo "${AI_SHAS}" | xargs)
61
+
62
+ if [[ -z "${AI_SHAS}" ]]; then
63
+ echo "has_ai_changes=false" >> "$GITHUB_OUTPUT"
64
+ exit 0
65
+ fi
66
+
67
+ echo "has_ai_changes=true" >> "$GITHUB_OUTPUT"
68
+ echo "ai_shas=${AI_SHAS}" >> "$GITHUB_OUTPUT"
69
+
70
+ # Collect changed source files from AI commits
71
+ CHANGED_FILES=""
72
+ for SHA in ${AI_SHAS}; do
73
+ FILES=$(git diff-tree --no-commit-id --name-only -r "${SHA}" \
74
+ | grep -E '\.(ts|js|py|go|java|rs|tsx|jsx)$' | grep -v '\.test\.\|\.spec\.' || true)
75
+ CHANGED_FILES="${CHANGED_FILES}\n${FILES}"
76
+ done
77
+
78
+ echo -e "${CHANGED_FILES}" | sort -u | grep -v '^$' > /tmp/ai-changed-files.txt || true
79
+ FILE_COUNT=$(wc -l < /tmp/ai-changed-files.txt | tr -d ' ')
80
+ echo "file_count=${FILE_COUNT}" >> "$GITHUB_OUTPUT"
81
+
82
+ - name: Run evaluations
83
+ id: eval
84
+ if: steps.detect-ai.outputs.has_ai_changes == 'true'
85
+ run: |
86
+ set -o pipefail
87
+ chmod +x .prism/eval-harness/run-eval.sh
88
+
89
+ # Find spec from commit trailers
90
+ BASE_SHA="${{ github.event.pull_request.base.sha }}"
91
+ HEAD_SHA="${{ github.sha }}"
92
+ SPEC_REF=$(git log "${BASE_SHA}..${HEAD_SHA}" --format='%(trailers:key=Spec,valueonly)' | head -1 | tr -d '[:space:]' || true)
93
+ SPEC_FLAG=""
94
+ [[ -n "${SPEC_REF}" && -f "${SPEC_REF}" ]] && SPEC_FLAG="--spec ${SPEC_REF}"
95
+
96
+ PASS_COUNT=0
97
+ FAIL_COUNT=0
98
+ TOTAL_SCORE=0
99
+ TOTAL_HALLUCINATIONS=0
100
+ RESULTS=""
101
+
102
+ while IFS= read -r FILE; do
103
+ [[ -z "${FILE}" || ! -f "${FILE}" ]] && continue
104
+
105
+ # Auto-select rubric based on file path
106
+ if echo "${FILE}" | grep -qiE '(agent|assistant|orchestrat|workflow|chain)'; then
107
+ RUBRIC=".prism/eval-harness/rubrics/agent-quality.json"
108
+ elif echo "${FILE}" | grep -qiE '(auth|security|guard|policy|iam|permission|crypto)'; then
109
+ RUBRIC=".prism/eval-harness/rubrics/security-compliance.json"
110
+ elif echo "${FILE}" | grep -qiE '(api|handler|route|controller)'; then
111
+ RUBRIC=".prism/eval-harness/rubrics/api-response-quality.json"
112
+ else
113
+ RUBRIC=".prism/eval-harness/rubrics/code-quality.json"
114
+ fi
115
+
116
+ # Fall back to first available rubric if selected doesn't exist
117
+ [[ -f "${RUBRIC}" ]] || RUBRIC=$(ls .prism/eval-harness/rubrics/*.json 2>/dev/null | head -1)
118
+ [[ -f "${RUBRIC}" ]] || { echo "No rubrics found"; exit 1; }
119
+
120
+ echo "Evaluating: ${FILE} (rubric: $(basename "${RUBRIC}" .json))"
121
+
122
+ set +e
123
+ OUTPUT=$(./.prism/eval-harness/run-eval.sh "${RUBRIC}" "${FILE}" ${SPEC_FLAG} 2>&1)
124
+ EXIT_CODE=$?
125
+ set -e
126
+
127
+ SCORE=$(echo "${OUTPUT}" | grep '^Score:' | awk '{print $2}' || echo "0")
128
+ RESULT=$(echo "${OUTPUT}" | grep '^Result:' | awk '{print $2}' || echo "UNKNOWN")
129
+ HALLUC=$(echo "${OUTPUT}" | grep '^Hallucinations:' | awk '{print $2}' || echo "0")
130
+
131
+ TOTAL_SCORE=$(echo "${TOTAL_SCORE} + ${SCORE}" | bc -l)
132
+ TOTAL_HALLUCINATIONS=$((TOTAL_HALLUCINATIONS + HALLUC))
133
+
134
+ if [[ "${RESULT}" == "SKIP" ]]; then
135
+ continue
136
+ elif [[ "${RESULT}" == "PASS" ]]; then
137
+ PASS_COUNT=$((PASS_COUNT + 1))
138
+ else
139
+ FAIL_COUNT=$((FAIL_COUNT + 1))
140
+ fi
141
+
142
+ RESULTS="${RESULTS}\n| \`${FILE}\` | $(basename "${RUBRIC}" .json) | ${SCORE} | ${RESULT} |"
143
+ done < /tmp/ai-changed-files.txt
144
+
145
+ TOTAL_FILES=$((PASS_COUNT + FAIL_COUNT))
146
+ AVG_SCORE="0"
147
+ [[ "${TOTAL_FILES}" -gt 0 ]] && AVG_SCORE=$(echo "scale=4; ${TOTAL_SCORE} / ${TOTAL_FILES}" | bc -l)
148
+
149
+ echo "pass_count=${PASS_COUNT}" >> "$GITHUB_OUTPUT"
150
+ echo "fail_count=${FAIL_COUNT}" >> "$GITHUB_OUTPUT"
151
+ echo "avg_score=${AVG_SCORE}" >> "$GITHUB_OUTPUT"
152
+ echo "total_files=${TOTAL_FILES}" >> "$GITHUB_OUTPUT"
153
+ echo "hallucinations=${TOTAL_HALLUCINATIONS}" >> "$GITHUB_OUTPUT"
154
+ echo -e "${RESULTS}" > /tmp/eval-results-table.txt
155
+
156
+ [[ "${FAIL_COUNT}" -gt 0 ]] && echo "overall_result=FAIL" >> "$GITHUB_OUTPUT" || echo "overall_result=PASS" >> "$GITHUB_OUTPUT"
157
+
158
+ - name: Post PR comment
159
+ if: always()
160
+ uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
161
+ with:
162
+ script: |
163
+ const fs = require('fs');
164
+ const hasAI = '${{ steps.detect-ai.outputs.has_ai_changes }}' === 'true';
165
+
166
+ let body;
167
+ if (!hasAI) {
168
+ body = ':white_check_mark: **PRISM Eval Gate** — No AI-generated code detected. Skipped.';
169
+ } else {
170
+ const avg = '${{ steps.eval.outputs.avg_score }}';
171
+ const pass = '${{ steps.eval.outputs.pass_count }}';
172
+ const fail = '${{ steps.eval.outputs.fail_count }}';
173
+ const total = '${{ steps.eval.outputs.total_files }}';
174
+ const result = '${{ steps.eval.outputs.overall_result }}';
175
+ const halluc = '${{ steps.eval.outputs.hallucinations }}';
176
+ const emoji = result === 'PASS' ? ':white_check_mark:' : ':x:';
177
+ const table = fs.existsSync('/tmp/eval-results-table.txt')
178
+ ? fs.readFileSync('/tmp/eval-results-table.txt', 'utf8').trim() : '';
179
+
180
+ body = `## ${emoji} PRISM Eval Gate — ${result}\n\n` +
181
+ `**Score**: ${avg} | **Files**: ${total} | **Pass**: ${pass} | **Fail**: ${fail} | **Hallucinations**: ${halluc}\n\n` +
182
+ `| File | Rubric | Score | Result |\n|---|---|---|---|\n${table}\n\n` +
183
+ `> Threshold: 0.82 | Model: claude-sonnet-4-20250514`;
184
+ }
185
+
186
+ const { data: comments } = await github.rest.issues.listComments({
187
+ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number
188
+ });
189
+ const existing = comments.find(c => c.body.includes('PRISM Eval Gate'));
190
+ const params = { owner: context.repo.owner, repo: context.repo.repo, body };
191
+
192
+ if (existing) {
193
+ await github.rest.issues.updateComment({ ...params, comment_id: existing.id });
194
+ } else {
195
+ await github.rest.issues.createComment({ ...params, issue_number: context.issue.number });
196
+ }
197
+
198
+ - name: Emit prism.d1.eval event
199
+ if: steps.detect-ai.outputs.has_ai_changes == 'true'
200
+ run: |
201
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
202
+ EVAL_EVENT=$(jq -n \
203
+ --arg team_id "${{ steps.prism-config.outputs.team_id }}" \
204
+ --arg repo "${{ github.repository }}" \
205
+ --arg timestamp "${TIMESTAMP}" \
206
+ --argjson score "${{ steps.eval.outputs.avg_score }}" \
207
+ --arg result "${{ steps.eval.outputs.overall_result }}" \
208
+ --argjson total_files "${{ steps.eval.outputs.total_files }}" \
209
+ --argjson pass_count "${{ steps.eval.outputs.pass_count }}" \
210
+ --argjson fail_count "${{ steps.eval.outputs.fail_count }}" \
211
+ --argjson hallucinations "${{ steps.eval.outputs.hallucinations }}" \
212
+ --arg pr_number "${{ github.event.pull_request.number }}" \
213
+ '{
214
+ team_id: $team_id, repo: $repo, timestamp: $timestamp, prism_level: 2,
215
+ metric: {name: "eval_score", value: $score, unit: "score"},
216
+ ai_context: {tool: "bedrock-eval", model: "anthropic.claude-sonnet-4-20250514", origin: "ai-generated"},
217
+ eval: {total_files: $total_files, pass_count: $pass_count, fail_count: $fail_count,
218
+ result: $result, pr_number: ($pr_number | tonumber), hallucinations_caught: $hallucinations}
219
+ }')
220
+ aws events put-events --region us-west-2 --entries "[{
221
+ \"Source\":\"prism.d1.velocity\",\"DetailType\":\"prism.d1.eval\",
222
+ \"EventBusName\":\"prism-d1-metrics\",
223
+ \"Detail\":$(echo "${EVAL_EVENT}" | jq -c '.' | jq -Rs '.')}]"
224
+
225
+ - name: Wait for Security Agent
226
+ uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
227
+ id: security-gate
228
+ with:
229
+ script: |
230
+ const pr = context.issue.number;
231
+ const opts = { owner: context.repo.owner, repo: context.repo.repo };
232
+
233
+ // Phase 1: Wait for Security Agent to start (2 min)
234
+ let reviewingComment = null;
235
+ for (let i = 0; i < 12; i++) {
236
+ const { data: comments } = await github.rest.issues.listComments({ ...opts, issue_number: pr });
237
+ reviewingComment = comments.find(c => c.user.login === 'aws-security-agent[bot]' && c.body.includes('reviewing your pull request'));
238
+ if (reviewingComment) break;
239
+ await new Promise(r => setTimeout(r, 10000));
240
+ }
241
+ if (!reviewingComment) { core.setOutput('findings', '0'); return; }
242
+
243
+ // Phase 2: Wait for completion (10 min) — look after the "reviewing" comment
244
+ const since = reviewingComment.created_at;
245
+ for (let i = 0; i < 40; i++) {
246
+ // Check for completed review submission (summary appears as review body, not issue comment)
247
+ const { data: reviews } = await github.rest.pulls.listReviews({ ...opts, pull_number: pr });
248
+ const botReview = reviews.find(r => r.user.login === 'aws-security-agent[bot]' && new Date(r.submitted_at) >= new Date(since));
249
+ // Check for inline review comments
250
+ const { data: reviewComments } = await github.rest.pulls.listReviewComments({ ...opts, pull_number: pr });
251
+ const findings = reviewComments.filter(c => c.user.login === 'aws-security-agent[bot]' && new Date(c.created_at) >= new Date(since));
252
+ if (botReview || findings.length > 0) {
253
+ core.setOutput('findings', String(findings.length));
254
+ return;
255
+ }
256
+ await new Promise(r => setTimeout(r, 15000));
257
+ }
258
+ core.setOutput('findings', '0');
259
+
260
+ - name: Forward Security Agent findings
261
+ if: steps.security-gate.outputs.findings != '0'
262
+ env:
263
+ GH_TOKEN: ${{ github.token }}
264
+ run: |
265
+ PR_NUMBER="${{ github.event.pull_request.number }}"
266
+ REPO="${{ github.repository }}"
267
+ TEAM_ID="${{ steps.prism-config.outputs.team_id }}"
268
+ BASE_SHA="${{ github.event.pull_request.base.sha }}"
269
+ HEAD_SHA="${{ github.sha }}"
270
+
271
+ AI_ORIGIN="human"
272
+ for SHA in $(git rev-list "${BASE_SHA}..${HEAD_SHA}"); do
273
+ if git log -1 --format='%B' "${SHA}" | grep -q 'AI-Origin: ai-generated\|AI-Origin: ai-assisted'; then
274
+ AI_ORIGIN="ai-assisted"; break
275
+ fi
276
+ done
277
+
278
+ REVIEW_COMMENTS=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/comments" --paginate 2>/dev/null || echo "[]")
279
+
280
+ ENTRIES=$(echo "${REVIEW_COMMENTS}" | jq --arg team_id "${TEAM_ID}" --arg repo "${REPO}" \
281
+ --arg ai_origin "${AI_ORIGIN}" --argjson pr_number "${PR_NUMBER}" '
282
+ def cwe_severity:
283
+ {"89":"CRITICAL","78":"CRITICAL","94":"CRITICAL","502":"CRITICAL","798":"CRITICAL",
284
+ "79":"HIGH","22":"HIGH","918":"HIGH","352":"HIGH","287":"HIGH","862":"HIGH",
285
+ "200":"MEDIUM","532":"MEDIUM","601":"MEDIUM"};
286
+ [.[] | select(.user.login == "aws-security-agent[bot]")] |
287
+ to_entries | map(.value as $c |
288
+ ($c.body | capture("CWE-(?<id>[0-9]+)") // {id: null}).id as $cwe_id |
289
+ (if $cwe_id then (cwe_severity[$cwe_id] // "MEDIUM") else "MEDIUM" end) as $severity |
290
+ {Source:"prism.d1.velocity", DetailType:"prism.d1.security.code_review",
291
+ EventBusName:"prism-d1-metrics",
292
+ Detail:({team_id:$team_id, repo:$repo, timestamp:$c.created_at, prism_level:3,
293
+ metric:{name:"security_finding",value:1,unit:"count"},
294
+ ai_context:{tool:"security-agent",model:"aws-security-agent",origin:$ai_origin},
295
+ security_agent_finding:{finding_id:"sa-pr\($pr_number)-\($c.id)", phase:"code_review",
296
+ severity:$severity, cwe_id:(if $cwe_id then "CWE-\($cwe_id)" else null end),
297
+ ai_origin:$ai_origin, found_at:$c.created_at}
298
+ } | tostring)})' 2>/dev/null || echo "[]")
299
+
300
+ TOTAL=$(echo "${ENTRIES}" | jq 'length')
301
+ for i in $(seq 0 10 $((TOTAL - 1))); do
302
+ aws events put-events --region us-west-2 --entries "$(echo "${ENTRIES}" | jq ".[$i:$((i+10))]")"
303
+ done
304
+
305
+ - name: Fail on eval or security issues
306
+ if: steps.eval.outputs.overall_result == 'FAIL' || steps.security-gate.outputs.findings != '0'
307
+ run: |
308
+ [[ "${{ steps.eval.outputs.overall_result }}" == "FAIL" ]] && echo "❌ Eval gate failed"
309
+ [[ "${{ steps.security-gate.outputs.findings }}" != "0" ]] && echo "❌ Security Agent found ${{ steps.security-gate.outputs.findings }} issues"
310
+ exit 1
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ import 'source-map-support/register';
3
+ import * as cdk from 'aws-cdk-lib';
4
+ import { Aspects } from 'aws-cdk-lib';
5
+ import { AwsSolutionsChecks } from 'cdk-nag';
6
+ import { MetricsPipelineStack } from '../lib/metrics-pipeline-stack';
7
+ import { ApiStack } from '../lib/api-stack';
8
+ import { DashboardStack } from '../lib/dashboard-stack';
9
+
10
+ const app = new cdk.App();
11
+
12
+ const env: cdk.Environment = {
13
+ account: process.env.CDK_DEFAULT_ACCOUNT,
14
+ region: process.env.CDK_DEFAULT_REGION ?? 'us-west-2',
15
+ };
16
+
17
+ const pipelineStack = new MetricsPipelineStack(app, 'PrismD1MetricsPipeline', {
18
+ env,
19
+ description: 'PRISM D1 Velocity - Core metrics event pipeline (EventBridge, DynamoDB)',
20
+ tags: {
21
+ 'prism:project': 'PRISM',
22
+ 'prism:domain': 'D1-Velocity',
23
+ 'prism:component': 'MetricsPipeline',
24
+ },
25
+ });
26
+
27
+ const apiStack = new ApiStack(app, 'PrismD1Api', {
28
+ env,
29
+ description: 'PRISM D1 Velocity - Metric ingestion and query API',
30
+ eventBus: pipelineStack.eventBus,
31
+ eventsTable: pipelineStack.eventsTable,
32
+ metadataTable: pipelineStack.metadataTable,
33
+ kmsKey: pipelineStack.kmsKey,
34
+ tags: {
35
+ 'prism:project': 'PRISM',
36
+ 'prism:domain': 'D1-Velocity',
37
+ 'prism:component': 'Api',
38
+ },
39
+ });
40
+
41
+ const dashboardStack = new DashboardStack(app, 'PrismD1Dashboard', {
42
+ env,
43
+ description: 'PRISM D1 Velocity - CloudWatch dashboards and alarms',
44
+ tags: {
45
+ 'prism:project': 'PRISM',
46
+ 'prism:domain': 'D1-Velocity',
47
+ 'prism:component': 'Dashboard',
48
+ },
49
+ });
50
+
51
+ apiStack.addDependency(pipelineStack);
52
+
53
+ // Enable cdk-nag AWS Solutions checks on all stacks
54
+ Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true }));
55
+
56
+ app.synth();
@@ -0,0 +1,12 @@
1
+ {
2
+ "app": "npx ts-node bin/app.ts",
3
+ "watch": {
4
+ "include": ["**"],
5
+ "exclude": ["README.md", "dist/**", "node_modules/**", "cdk.out/**"]
6
+ },
7
+ "context": {
8
+ "@aws-cdk/aws-lambda:recognizeLayerVersion": true,
9
+ "@aws-cdk/core:checkSecretUsage": true,
10
+ "@aws-cdk/core:target-partitions": ["aws"]
11
+ }
12
+ }