@littlebearapps/platform-admin-sdk 1.4.2 → 2.0.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.
Files changed (189) hide show
  1. package/dist/templates.d.ts +1 -1
  2. package/dist/templates.js +232 -2
  3. package/package.json +1 -1
  4. package/templates/full/config/audit-targets.yaml +72 -0
  5. package/templates/full/dashboard/src/components/notifications/NotificationBell.tsx +30 -0
  6. package/templates/full/dashboard/src/components/notifications/NotificationList.tsx +116 -0
  7. package/templates/full/dashboard/src/components/notifications/index.ts +2 -0
  8. package/templates/full/dashboard/src/components/patterns/ActivePatterns.tsx +62 -0
  9. package/templates/full/dashboard/src/components/patterns/PatternStats.tsx +60 -0
  10. package/templates/full/dashboard/src/components/patterns/PatternTabs.tsx +116 -0
  11. package/templates/full/dashboard/src/components/patterns/SuggestionsQueue.tsx +115 -0
  12. package/templates/full/dashboard/src/components/patterns/SystemPatterns.tsx +52 -0
  13. package/templates/full/dashboard/src/components/patterns/index.ts +5 -0
  14. package/templates/full/dashboard/src/components/reports/GapDetectionReport.tsx +69 -0
  15. package/templates/full/dashboard/src/components/reports/SdkAuditReport.tsx +72 -0
  16. package/templates/full/dashboard/src/components/reports/index.ts +2 -0
  17. package/templates/full/dashboard/src/components/search/SearchModal.tsx +108 -0
  18. package/templates/full/dashboard/src/pages/api/notifications/[id]/read.ts +37 -0
  19. package/templates/full/dashboard/src/pages/api/notifications/index.ts +47 -0
  20. package/templates/full/dashboard/src/pages/api/notifications/read-all.ts +28 -0
  21. package/templates/full/dashboard/src/pages/api/notifications/unread-count.ts +31 -0
  22. package/templates/full/dashboard/src/pages/api/patterns/approve.ts +55 -0
  23. package/templates/full/dashboard/src/pages/api/patterns/cache-refresh.ts +38 -0
  24. package/templates/full/dashboard/src/pages/api/patterns/discover.ts +36 -0
  25. package/templates/full/dashboard/src/pages/api/patterns/index.ts +36 -0
  26. package/templates/full/dashboard/src/pages/api/patterns/ready-for-review.ts +39 -0
  27. package/templates/full/dashboard/src/pages/api/patterns/reject.ts +54 -0
  28. package/templates/full/dashboard/src/pages/api/patterns/stats.ts +39 -0
  29. package/templates/full/dashboard/src/pages/api/patterns/suggestions.ts +43 -0
  30. package/templates/full/dashboard/src/pages/api/reports/audit.ts +45 -0
  31. package/templates/full/dashboard/src/pages/api/reports/usage.ts +52 -0
  32. package/templates/full/dashboard/src/pages/api/search/index.ts +74 -0
  33. package/templates/full/dashboard/src/pages/api/search/reindex.ts +28 -0
  34. package/templates/full/dashboard/src/pages/api/search/stats.ts +27 -0
  35. package/templates/full/dashboard/src/pages/api/settings/index.ts +37 -0
  36. package/templates/full/dashboard/src/pages/api/settings/update.ts +41 -0
  37. package/templates/full/dashboard/src/pages/api/topology/index.ts +56 -0
  38. package/templates/full/dashboard/src/pages/notifications.astro +11 -0
  39. package/templates/full/migrations/008_auditor.sql +99 -0
  40. package/templates/full/migrations/010_pricing_versions.sql +110 -0
  41. package/templates/full/migrations/011_multi_account.sql +51 -0
  42. package/templates/full/scripts/ops/set-kv-pricing.ts +182 -0
  43. package/templates/full/scripts/ops/universal-backfill.ts +147 -0
  44. package/templates/full/workers/lib/ai-judge-schema.ts +181 -0
  45. package/templates/full/workers/lib/auditor/comprehensive-report.ts +407 -0
  46. package/templates/full/workers/lib/auditor/feature-coverage.ts +348 -0
  47. package/templates/full/workers/lib/auditor/index.ts +9 -0
  48. package/templates/full/workers/lib/auditor/types.ts +167 -0
  49. package/templates/full/workers/platform-auditor.ts +1071 -0
  50. package/templates/full/wrangler.auditor.jsonc.hbs +75 -0
  51. package/templates/shared/.github/workflows/contract-check.yml.hbs +42 -0
  52. package/templates/shared/.github/workflows/dashboard-deploy.yml.hbs +39 -0
  53. package/templates/shared/.github/workflows/platform-check.yml.hbs +28 -0
  54. package/templates/shared/.github/workflows/security.yml +33 -0
  55. package/templates/shared/config/observability.yaml.hbs +276 -0
  56. package/templates/shared/contracts/schemas/envelope.v1.schema.json +64 -0
  57. package/templates/shared/contracts/schemas/error_report.v1.schema.json +65 -0
  58. package/templates/shared/contracts/types/telemetry-envelope.types.ts +139 -0
  59. package/templates/shared/dashboard/astro.config.mjs +21 -0
  60. package/templates/shared/dashboard/package.json.hbs +29 -0
  61. package/templates/shared/dashboard/src/components/Header.astro +29 -0
  62. package/templates/shared/dashboard/src/components/Nav.astro.hbs +59 -0
  63. package/templates/shared/dashboard/src/components/infrastructure/AlertHistory.tsx +57 -0
  64. package/templates/shared/dashboard/src/components/infrastructure/InfrastructureStats.tsx +73 -0
  65. package/templates/shared/dashboard/src/components/infrastructure/ServiceRegistry.tsx +55 -0
  66. package/templates/shared/dashboard/src/components/infrastructure/UptimeStatus.tsx +56 -0
  67. package/templates/shared/dashboard/src/components/infrastructure/index.ts +4 -0
  68. package/templates/shared/dashboard/src/components/overview/ActivityFeed.tsx +134 -0
  69. package/templates/shared/dashboard/src/components/overview/CostQuadrant.tsx +131 -0
  70. package/templates/shared/dashboard/src/components/overview/ErrorsQuadrant.tsx +113 -0
  71. package/templates/shared/dashboard/src/components/overview/HealthQuadrant.tsx +87 -0
  72. package/templates/shared/dashboard/src/components/overview/MissionControl.tsx +139 -0
  73. package/templates/shared/dashboard/src/components/resources/AllowanceStatus.tsx +44 -0
  74. package/templates/shared/dashboard/src/components/resources/CostCentreOverview.tsx +42 -0
  75. package/templates/shared/dashboard/src/components/resources/ResourceTabs.tsx +69 -0
  76. package/templates/shared/dashboard/src/components/resources/index.ts +3 -0
  77. package/templates/shared/dashboard/src/components/settings/SettingsCard.tsx +21 -0
  78. package/templates/shared/dashboard/src/components/settings/index.ts +1 -0
  79. package/templates/shared/dashboard/src/components/ui/AlertBanner.tsx +39 -0
  80. package/templates/shared/dashboard/src/components/ui/Breadcrumbs.tsx +27 -0
  81. package/templates/shared/dashboard/src/components/ui/EmptyState.tsx +26 -0
  82. package/templates/shared/dashboard/src/components/ui/ErrorBoundary.tsx +42 -0
  83. package/templates/shared/dashboard/src/components/ui/LoadingSkeleton.tsx +18 -0
  84. package/templates/shared/dashboard/src/components/ui/PageShell.tsx +26 -0
  85. package/templates/shared/dashboard/src/components/ui/Sparkline.tsx +127 -0
  86. package/templates/shared/dashboard/src/components/ui/StatusDot.tsx +21 -0
  87. package/templates/shared/dashboard/src/components/ui/Toast.tsx +44 -0
  88. package/templates/shared/dashboard/src/components/ui/index.ts +9 -0
  89. package/templates/shared/dashboard/src/components/usage/AnomaliesWidget.tsx +68 -0
  90. package/templates/shared/dashboard/src/components/usage/HourlyUsageChart.tsx +55 -0
  91. package/templates/shared/dashboard/src/components/usage/PlanAllowanceDashboard.tsx +67 -0
  92. package/templates/shared/dashboard/src/components/usage/ProjectCostBreakdown.tsx +55 -0
  93. package/templates/shared/dashboard/src/components/usage/index.ts +4 -0
  94. package/templates/shared/dashboard/src/env.d.ts.hbs +34 -0
  95. package/templates/shared/dashboard/src/layouts/DashboardLayout.astro +37 -0
  96. package/templates/shared/dashboard/src/lib/cloudflare/costs.ts +21 -0
  97. package/templates/shared/dashboard/src/lib/fetch.ts +29 -0
  98. package/templates/shared/dashboard/src/lib/types.ts +72 -0
  99. package/templates/shared/dashboard/src/middleware/auth.ts +100 -0
  100. package/templates/shared/dashboard/src/middleware/index.ts +1 -0
  101. package/templates/shared/dashboard/src/pages/api/costs/overview.ts +65 -0
  102. package/templates/shared/dashboard/src/pages/api/costs/providers.ts +47 -0
  103. package/templates/shared/dashboard/src/pages/api/infrastructure/services.ts +55 -0
  104. package/templates/shared/dashboard/src/pages/api/infrastructure/stats.ts +99 -0
  105. package/templates/shared/dashboard/src/pages/api/overview/summary.ts +311 -0
  106. package/templates/shared/dashboard/src/pages/api/usage/allowances.ts +56 -0
  107. package/templates/shared/dashboard/src/pages/api/usage/anomalies.ts +45 -0
  108. package/templates/shared/dashboard/src/pages/api/usage/billing.ts +53 -0
  109. package/templates/shared/dashboard/src/pages/api/usage/circuit-breakers.ts +44 -0
  110. package/templates/shared/dashboard/src/pages/api/usage/granular.ts +50 -0
  111. package/templates/shared/dashboard/src/pages/api/usage/hourly.ts +45 -0
  112. package/templates/shared/dashboard/src/pages/api/usage/projects.ts +51 -0
  113. package/templates/shared/dashboard/src/pages/api/usage/status.ts +42 -0
  114. package/templates/shared/dashboard/src/pages/api/user/identity.ts +11 -0
  115. package/templates/shared/dashboard/src/pages/dashboard.astro +11 -0
  116. package/templates/shared/dashboard/src/pages/index.astro +3 -0
  117. package/templates/shared/dashboard/src/pages/resources.astro +11 -0
  118. package/templates/shared/dashboard/src/pages/settings/index.astro +28 -0
  119. package/templates/shared/dashboard/src/pages/settings/notifications.astro +34 -0
  120. package/templates/shared/dashboard/src/pages/settings/thresholds.astro +39 -0
  121. package/templates/shared/dashboard/src/pages/settings/usage.astro +28 -0
  122. package/templates/shared/dashboard/src/styles/global.css +29 -0
  123. package/templates/shared/dashboard/tailwind.config.mjs +9 -0
  124. package/templates/shared/dashboard/tsconfig.json +9 -0
  125. package/templates/shared/dashboard/wrangler.json.hbs +47 -0
  126. package/templates/shared/docs/architecture.md +89 -0
  127. package/templates/shared/docs/post-deploy-runbook.md +126 -0
  128. package/templates/shared/docs/troubleshooting.md +91 -0
  129. package/templates/shared/package.json.hbs +17 -1
  130. package/templates/shared/scripts/ops/backfill-cloudflare-daily.ts +145 -0
  131. package/templates/shared/scripts/ops/backfill-cloudflare-hourly.ts +473 -0
  132. package/templates/shared/scripts/ops/backfill-monthly-rollups.ts +125 -0
  133. package/templates/shared/scripts/ops/discover-graphql-datasets.ts +482 -0
  134. package/templates/shared/scripts/ops/reset-budget-state.ts +279 -0
  135. package/templates/shared/scripts/ops/validate-controls.js +141 -0
  136. package/templates/shared/scripts/ops/validate-pipeline.ts +237 -0
  137. package/templates/shared/scripts/ops/verify-account-completeness.ts +236 -0
  138. package/templates/shared/scripts/validate-schemas.js +61 -0
  139. package/templates/shared/tests/contract/validate-schemas.test.ts +130 -0
  140. package/templates/shared/tests/fixtures/telemetry-envelope-invalid.json +9 -0
  141. package/templates/shared/tests/fixtures/telemetry-envelope-valid.json +27 -0
  142. package/templates/shared/tests/helpers/mock-d1.ts +61 -0
  143. package/templates/shared/tests/helpers/mock-kv.ts +37 -0
  144. package/templates/shared/tests/unit/workers/batch-persistence.test.ts +133 -0
  145. package/templates/shared/tests/unit/workers/budget-enforcement.test.ts +214 -0
  146. package/templates/shared/vitest.config.ts +18 -0
  147. package/templates/shared/workers/lib/usage/collectors/anthropic.ts +114 -0
  148. package/templates/shared/workers/lib/usage/collectors/apify.ts +96 -0
  149. package/templates/shared/workers/lib/usage/collectors/custom-http.ts +151 -0
  150. package/templates/shared/workers/lib/usage/collectors/deepseek.ts +92 -0
  151. package/templates/shared/workers/lib/usage/collectors/gemini.ts +263 -0
  152. package/templates/shared/workers/lib/usage/collectors/github.ts +362 -0
  153. package/templates/shared/workers/lib/usage/collectors/index.ts +31 -15
  154. package/templates/shared/workers/lib/usage/collectors/minimax.ts +106 -0
  155. package/templates/shared/workers/lib/usage/collectors/openai.ts +171 -0
  156. package/templates/shared/workers/lib/usage/collectors/resend.ts +79 -0
  157. package/templates/shared/workers/lib/usage/collectors/stripe.ts +192 -0
  158. package/templates/shared/workers/lib/usage/shared/types.ts +46 -0
  159. package/templates/shared/workers/platform-usage.ts +98 -8
  160. package/templates/standard/dashboard/src/components/errors/ErrorStats.tsx +53 -0
  161. package/templates/standard/dashboard/src/components/errors/ErrorsTable.tsx +133 -0
  162. package/templates/standard/dashboard/src/components/errors/index.ts +2 -0
  163. package/templates/standard/dashboard/src/components/health/CircuitBreakerEvents.tsx +69 -0
  164. package/templates/standard/dashboard/src/components/health/CircuitBreakerPanel.tsx +97 -0
  165. package/templates/standard/dashboard/src/components/health/DlqStatusCard.tsx +52 -0
  166. package/templates/standard/dashboard/src/components/health/HealthTabs.tsx +86 -0
  167. package/templates/standard/dashboard/src/components/health/index.ts +4 -0
  168. package/templates/standard/dashboard/src/lib/errors.ts +28 -0
  169. package/templates/standard/dashboard/src/pages/api/errors/[fingerprint]/mute.ts +49 -0
  170. package/templates/standard/dashboard/src/pages/api/errors/[fingerprint]/resolve.ts +36 -0
  171. package/templates/standard/dashboard/src/pages/api/errors/[fingerprint].ts +55 -0
  172. package/templates/standard/dashboard/src/pages/api/errors/index.ts +58 -0
  173. package/templates/standard/dashboard/src/pages/api/errors/stats.ts +55 -0
  174. package/templates/standard/dashboard/src/pages/api/health/audit-history.ts +37 -0
  175. package/templates/standard/dashboard/src/pages/api/health/dlq.ts +43 -0
  176. package/templates/standard/dashboard/src/pages/circuit-breakers.astro +13 -0
  177. package/templates/standard/dashboard/src/pages/errors.astro +13 -0
  178. package/templates/standard/dashboard/src/pages/health.astro +11 -0
  179. package/templates/standard/migrations/009_topology_mapper.sql +65 -0
  180. package/templates/standard/tests/unit/error-collector/capture.test.ts +106 -0
  181. package/templates/standard/tests/unit/error-collector/fingerprint.test.ts +155 -0
  182. package/templates/standard/workers/lib/error-collector/email-health-alerts.ts +37 -3
  183. package/templates/standard/workers/lib/error-collector/gap-alerts.ts +32 -1
  184. package/templates/standard/workers/lib/mapper/attribution-check.ts +339 -0
  185. package/templates/standard/workers/lib/mapper/index.ts +7 -0
  186. package/templates/standard/workers/platform-mapper.ts +482 -0
  187. package/templates/standard/workers/platform-sdk-test-client.ts +125 -0
  188. package/templates/standard/wrangler.mapper.jsonc.hbs +85 -0
  189. package/templates/standard/wrangler.sdk-test-client.jsonc.hbs +62 -0
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Universal Backfill Script (Full Tier)
4
+ *
5
+ * Orchestrates the hourly → daily → monthly cascade with progress reporting.
6
+ * Runs each step in sequence, passing date ranges through the pipeline.
7
+ *
8
+ * Prerequisites:
9
+ * CLOUDFLARE_API_TOKEN — API token with Analytics:Read + D1:Write permissions
10
+ * CLOUDFLARE_ACCOUNT_ID — Your Cloudflare account ID
11
+ * D1_DATABASE_ID — Your platform-metrics D1 database ID
12
+ *
13
+ * Usage:
14
+ * npx tsx scripts/ops/universal-backfill.ts
15
+ * npx tsx scripts/ops/universal-backfill.ts --dry-run
16
+ * npx tsx scripts/ops/universal-backfill.ts --start 2026-02-01 --end 2026-02-28
17
+ * npx tsx scripts/ops/universal-backfill.ts --skip-hourly
18
+ */
19
+
20
+ import { execSync } from 'node:child_process';
21
+ import { resolve } from 'node:path';
22
+
23
+ const SCRIPTS_DIR = resolve(import.meta.dirname);
24
+
25
+ interface Args {
26
+ start?: string;
27
+ end?: string;
28
+ dryRun: boolean;
29
+ skipHourly: boolean;
30
+ }
31
+
32
+ function parseArgs(): Args {
33
+ const args = process.argv.slice(2);
34
+ const result: Args = { dryRun: false, skipHourly: false };
35
+
36
+ for (let i = 0; i < args.length; i++) {
37
+ if (args[i] === '--start' && args[i + 1]) result.start = args[++i];
38
+ else if (args[i] === '--end' && args[i + 1]) result.end = args[++i];
39
+ else if (args[i] === '--dry-run') result.dryRun = true;
40
+ else if (args[i] === '--skip-hourly') result.skipHourly = true;
41
+ }
42
+
43
+ if (!result.start) {
44
+ const d = new Date();
45
+ d.setDate(d.getDate() - 7);
46
+ result.start = d.toISOString().slice(0, 10);
47
+ }
48
+ if (!result.end) {
49
+ result.end = new Date().toISOString().slice(0, 10);
50
+ }
51
+
52
+ return result;
53
+ }
54
+
55
+ function runStep(name: string, script: string, extraArgs: string[]) {
56
+ console.log(`\n${'='.repeat(60)}`);
57
+ console.log(`Step: ${name}`);
58
+ console.log('='.repeat(60));
59
+
60
+ const cmd = `npx tsx ${script} ${extraArgs.join(' ')}`;
61
+ console.log(`Running: ${cmd}\n`);
62
+
63
+ try {
64
+ execSync(cmd, { stdio: 'inherit', env: process.env });
65
+ console.log(`\n${name}: COMPLETE`);
66
+ return true;
67
+ } catch (err) {
68
+ console.error(`\n${name}: FAILED`);
69
+ return false;
70
+ }
71
+ }
72
+
73
+ async function main() {
74
+ const args = parseArgs();
75
+ const dateArgs = [];
76
+ if (args.start) dateArgs.push('--start', args.start);
77
+ if (args.end) dateArgs.push('--end', args.end);
78
+ if (args.dryRun) dateArgs.push('--dry-run');
79
+
80
+ console.log('Universal Backfill Pipeline');
81
+ console.log(`Range: ${args.start} → ${args.end}`);
82
+ console.log(`Dry run: ${args.dryRun}`);
83
+ console.log(`Skip hourly: ${args.skipHourly}`);
84
+
85
+ const startTime = Date.now();
86
+ const results: Array<{ step: string; success: boolean }> = [];
87
+
88
+ // Step 1: Hourly backfill (optional — requires GraphQL API, rate-limited)
89
+ if (!args.skipHourly) {
90
+ const success = runStep(
91
+ '1/3: Hourly Backfill',
92
+ resolve(SCRIPTS_DIR, 'backfill-cloudflare-hourly.ts'),
93
+ dateArgs
94
+ );
95
+ results.push({ step: 'Hourly', success });
96
+ if (!success && !args.dryRun) {
97
+ console.warn('\nHourly backfill failed — continuing with daily rollups using existing hourly data.');
98
+ }
99
+ } else {
100
+ console.log('\nSkipping hourly backfill (--skip-hourly)');
101
+ results.push({ step: 'Hourly', success: true });
102
+ }
103
+
104
+ // Step 2: Daily rollups
105
+ const dailySuccess = runStep(
106
+ '2/3: Daily Rollups',
107
+ resolve(SCRIPTS_DIR, 'backfill-cloudflare-daily.ts'),
108
+ dateArgs
109
+ );
110
+ results.push({ step: 'Daily', success: dailySuccess });
111
+
112
+ // Step 3: Monthly rollups
113
+ const monthStart = args.start?.slice(0, 7) ?? '';
114
+ const monthEnd = args.end?.slice(0, 7) ?? '';
115
+ const monthlyArgs = [];
116
+ if (monthStart) monthlyArgs.push('--start', monthStart);
117
+ if (monthEnd) monthlyArgs.push('--end', monthEnd);
118
+ if (args.dryRun) monthlyArgs.push('--dry-run');
119
+
120
+ const monthlySuccess = runStep(
121
+ '3/3: Monthly Rollups',
122
+ resolve(SCRIPTS_DIR, 'backfill-monthly-rollups.ts'),
123
+ monthlyArgs
124
+ );
125
+ results.push({ step: 'Monthly', success: monthlySuccess });
126
+
127
+ // Summary
128
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
129
+ console.log(`\n${'='.repeat(60)}`);
130
+ console.log('Pipeline Summary');
131
+ console.log('='.repeat(60));
132
+ for (const r of results) {
133
+ console.log(` ${r.success ? 'PASS' : 'FAIL'} ${r.step}`);
134
+ }
135
+ console.log(`\nTotal time: ${elapsed}s`);
136
+
137
+ const failed = results.filter((r) => !r.success);
138
+ if (failed.length > 0) {
139
+ console.error(`\n${failed.length} step(s) failed.`);
140
+ process.exit(1);
141
+ }
142
+ }
143
+
144
+ main().catch((err) => {
145
+ console.error('Fatal error:', err);
146
+ process.exit(1);
147
+ });
@@ -0,0 +1,181 @@
1
+ /**
2
+ * AI Judge Response Schema
3
+ *
4
+ * Zod schemas for validating AI Judge responses from Gemini via AI Gateway.
5
+ * Implements rubric-based scoring with chain-of-thought reasoning.
6
+ * Designed to be lenient to handle AI output variations.
7
+ *
8
+ * Rubric dimensions (1-5 scale):
9
+ * sdk (30%) — SDK integration patterns, feature budgets, tracking
10
+ * observability (25%) — Logging, tracing, metrics, error reporting
11
+ * costProtection (25%) — Circuit breakers, budgets, DLQ, rate limiting
12
+ * security (20%) — Auth, validation, secrets, CORS
13
+ *
14
+ * @module workers/lib/ai-judge-schema
15
+ */
16
+
17
+ import { z } from 'zod';
18
+
19
+ // -----------------------------------------------------------------------------
20
+ // Rubric Score Schema (1-5 scale with evidence)
21
+ // -----------------------------------------------------------------------------
22
+
23
+ export const RubricScoreSchema = z.object({
24
+ score: z
25
+ .number()
26
+ .min(1)
27
+ .max(5)
28
+ .transform((v) => Math.round(v)),
29
+ evidence: z.array(z.string()).default(['No specific evidence cited']),
30
+ });
31
+
32
+ export type RubricScore = z.infer<typeof RubricScoreSchema>;
33
+
34
+ // -----------------------------------------------------------------------------
35
+ // Rubric Scores (all four dimensions) - lenient with aliases
36
+ // -----------------------------------------------------------------------------
37
+
38
+ export const RubricScoresSchema = z
39
+ .object({
40
+ sdk: RubricScoreSchema.optional(),
41
+ observability: RubricScoreSchema.optional(),
42
+ costProtection: RubricScoreSchema.optional(),
43
+ cost: RubricScoreSchema.optional(), // Alias for costProtection
44
+ security: RubricScoreSchema.optional(),
45
+ })
46
+ .transform((scores) => ({
47
+ sdk: scores.sdk ?? { score: 3, evidence: ['Not evaluated'] },
48
+ observability: scores.observability ?? { score: 3, evidence: ['Not evaluated'] },
49
+ costProtection: scores.costProtection ??
50
+ scores.cost ?? { score: 3, evidence: ['Not evaluated'] },
51
+ security: scores.security ?? { score: 3, evidence: ['Not evaluated'] },
52
+ }));
53
+
54
+ export type RubricScores = z.infer<typeof RubricScoresSchema>;
55
+
56
+ // -----------------------------------------------------------------------------
57
+ // Static Check Correction (lenient)
58
+ // -----------------------------------------------------------------------------
59
+
60
+ export const StaticCheckCorrectionSchema = z.object({
61
+ field: z.string().optional().default('unknown'),
62
+ expected: z.boolean().optional().default(true),
63
+ actual: z.boolean().optional().default(false),
64
+ reason: z.string().optional().default(''),
65
+ confidence: z
66
+ .union([z.number(), z.string().transform((v) => parseFloat(v))])
67
+ .optional()
68
+ .default(0.8)
69
+ .transform((v) => (typeof v === 'number' && v > 1 ? v / 100 : v)),
70
+ });
71
+
72
+ export type StaticCheckCorrection = z.infer<typeof StaticCheckCorrectionSchema>;
73
+
74
+ // -----------------------------------------------------------------------------
75
+ // Categorised Issue (lenient)
76
+ // -----------------------------------------------------------------------------
77
+
78
+ export const CategorisedIssueSchema = z.object({
79
+ category: z.enum(['sdk', 'observability', 'cost', 'security']).catch('sdk'),
80
+ severity: z.enum(['critical', 'high', 'medium', 'low']).catch('medium'),
81
+ description: z.string().optional().default('No description'),
82
+ file: z.string().optional().nullable(),
83
+ line: z
84
+ .union([z.number(), z.string().transform((v) => parseInt(v, 10))])
85
+ .optional()
86
+ .nullable(),
87
+ });
88
+
89
+ export type CategorisedIssue = z.infer<typeof CategorisedIssueSchema>;
90
+
91
+ // -----------------------------------------------------------------------------
92
+ // Full AI Judge Response (lenient)
93
+ // -----------------------------------------------------------------------------
94
+
95
+ export const AIJudgeResponseSchema = z.object({
96
+ reasoning: z.string().optional().default('Analysis completed.'),
97
+ rubricScores: z.preprocess((val) => val ?? {}, RubricScoresSchema),
98
+ staticCheckCorrections: z.array(StaticCheckCorrectionSchema).optional().default([]),
99
+ issues: z.array(CategorisedIssueSchema).optional().default([]),
100
+ summary: z.string().optional().default('Analysis complete.'),
101
+ });
102
+
103
+ export type AIJudgeResponse = z.infer<typeof AIJudgeResponseSchema>;
104
+
105
+ // -----------------------------------------------------------------------------
106
+ // Rubric Weights (for composite score calculation)
107
+ // -----------------------------------------------------------------------------
108
+
109
+ /** Default rubric weights — configurable via audit-targets.yaml */
110
+ export const DEFAULT_RUBRIC_WEIGHTS = {
111
+ sdk: 0.3,
112
+ observability: 0.25,
113
+ costProtection: 0.25,
114
+ security: 0.2,
115
+ } as const;
116
+
117
+ // -----------------------------------------------------------------------------
118
+ // Composite Score Calculation
119
+ // -----------------------------------------------------------------------------
120
+
121
+ /**
122
+ * Calculate composite score from rubric scores.
123
+ * Converts 1-5 scale to 0-100 scale using weighted average.
124
+ */
125
+ export function calculateCompositeScore(
126
+ rubricScores: RubricScores,
127
+ weights: typeof DEFAULT_RUBRIC_WEIGHTS = DEFAULT_RUBRIC_WEIGHTS
128
+ ): number {
129
+ const weightedSum =
130
+ rubricScores.sdk.score * weights.sdk +
131
+ rubricScores.observability.score * weights.observability +
132
+ rubricScores.costProtection.score * weights.costProtection +
133
+ rubricScores.security.score * weights.security;
134
+
135
+ return Math.round(((weightedSum - 1) / 4) * 100);
136
+ }
137
+
138
+ // -----------------------------------------------------------------------------
139
+ // Validation Helper
140
+ // -----------------------------------------------------------------------------
141
+
142
+ export interface ValidationResult {
143
+ success: true;
144
+ data: AIJudgeResponse;
145
+ }
146
+
147
+ export interface ValidationError {
148
+ success: false;
149
+ errors: string[];
150
+ }
151
+
152
+ /**
153
+ * Validate and parse AI Judge response.
154
+ * Returns structured result with either parsed data or error messages.
155
+ */
156
+ export function validateAIJudgeResponse(response: unknown): ValidationResult | ValidationError {
157
+ const result = AIJudgeResponseSchema.safeParse(response);
158
+
159
+ if (result.success) {
160
+ return { success: true, data: result.data };
161
+ }
162
+
163
+ return {
164
+ success: false,
165
+ errors: result.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`),
166
+ };
167
+ }
168
+
169
+ // -----------------------------------------------------------------------------
170
+ // Prompt Error Feedback
171
+ // -----------------------------------------------------------------------------
172
+
173
+ /**
174
+ * Format validation errors for inclusion in retry prompt.
175
+ */
176
+ export function formatValidationErrors(errors: string[]): string {
177
+ return `Your previous response had validation errors:
178
+ ${errors.map((e) => `- ${e}`).join('\n')}
179
+
180
+ Please fix these issues and respond again with valid JSON.`;
181
+ }