@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.
- package/dist/templates.d.ts +1 -1
- package/dist/templates.js +232 -2
- package/package.json +1 -1
- package/templates/full/config/audit-targets.yaml +72 -0
- package/templates/full/dashboard/src/components/notifications/NotificationBell.tsx +30 -0
- package/templates/full/dashboard/src/components/notifications/NotificationList.tsx +116 -0
- package/templates/full/dashboard/src/components/notifications/index.ts +2 -0
- package/templates/full/dashboard/src/components/patterns/ActivePatterns.tsx +62 -0
- package/templates/full/dashboard/src/components/patterns/PatternStats.tsx +60 -0
- package/templates/full/dashboard/src/components/patterns/PatternTabs.tsx +116 -0
- package/templates/full/dashboard/src/components/patterns/SuggestionsQueue.tsx +115 -0
- package/templates/full/dashboard/src/components/patterns/SystemPatterns.tsx +52 -0
- package/templates/full/dashboard/src/components/patterns/index.ts +5 -0
- package/templates/full/dashboard/src/components/reports/GapDetectionReport.tsx +69 -0
- package/templates/full/dashboard/src/components/reports/SdkAuditReport.tsx +72 -0
- package/templates/full/dashboard/src/components/reports/index.ts +2 -0
- package/templates/full/dashboard/src/components/search/SearchModal.tsx +108 -0
- package/templates/full/dashboard/src/pages/api/notifications/[id]/read.ts +37 -0
- package/templates/full/dashboard/src/pages/api/notifications/index.ts +47 -0
- package/templates/full/dashboard/src/pages/api/notifications/read-all.ts +28 -0
- package/templates/full/dashboard/src/pages/api/notifications/unread-count.ts +31 -0
- package/templates/full/dashboard/src/pages/api/patterns/approve.ts +55 -0
- package/templates/full/dashboard/src/pages/api/patterns/cache-refresh.ts +38 -0
- package/templates/full/dashboard/src/pages/api/patterns/discover.ts +36 -0
- package/templates/full/dashboard/src/pages/api/patterns/index.ts +36 -0
- package/templates/full/dashboard/src/pages/api/patterns/ready-for-review.ts +39 -0
- package/templates/full/dashboard/src/pages/api/patterns/reject.ts +54 -0
- package/templates/full/dashboard/src/pages/api/patterns/stats.ts +39 -0
- package/templates/full/dashboard/src/pages/api/patterns/suggestions.ts +43 -0
- package/templates/full/dashboard/src/pages/api/reports/audit.ts +45 -0
- package/templates/full/dashboard/src/pages/api/reports/usage.ts +52 -0
- package/templates/full/dashboard/src/pages/api/search/index.ts +74 -0
- package/templates/full/dashboard/src/pages/api/search/reindex.ts +28 -0
- package/templates/full/dashboard/src/pages/api/search/stats.ts +27 -0
- package/templates/full/dashboard/src/pages/api/settings/index.ts +37 -0
- package/templates/full/dashboard/src/pages/api/settings/update.ts +41 -0
- package/templates/full/dashboard/src/pages/api/topology/index.ts +56 -0
- package/templates/full/dashboard/src/pages/notifications.astro +11 -0
- package/templates/full/migrations/008_auditor.sql +99 -0
- package/templates/full/migrations/010_pricing_versions.sql +110 -0
- package/templates/full/migrations/011_multi_account.sql +51 -0
- package/templates/full/scripts/ops/set-kv-pricing.ts +182 -0
- package/templates/full/scripts/ops/universal-backfill.ts +147 -0
- package/templates/full/workers/lib/ai-judge-schema.ts +181 -0
- package/templates/full/workers/lib/auditor/comprehensive-report.ts +407 -0
- package/templates/full/workers/lib/auditor/feature-coverage.ts +348 -0
- package/templates/full/workers/lib/auditor/index.ts +9 -0
- package/templates/full/workers/lib/auditor/types.ts +167 -0
- package/templates/full/workers/platform-auditor.ts +1071 -0
- package/templates/full/wrangler.auditor.jsonc.hbs +75 -0
- package/templates/shared/.github/workflows/contract-check.yml.hbs +42 -0
- package/templates/shared/.github/workflows/dashboard-deploy.yml.hbs +39 -0
- package/templates/shared/.github/workflows/platform-check.yml.hbs +28 -0
- package/templates/shared/.github/workflows/security.yml +33 -0
- package/templates/shared/config/observability.yaml.hbs +276 -0
- package/templates/shared/contracts/schemas/envelope.v1.schema.json +64 -0
- package/templates/shared/contracts/schemas/error_report.v1.schema.json +65 -0
- package/templates/shared/contracts/types/telemetry-envelope.types.ts +139 -0
- package/templates/shared/dashboard/astro.config.mjs +21 -0
- package/templates/shared/dashboard/package.json.hbs +29 -0
- package/templates/shared/dashboard/src/components/Header.astro +29 -0
- package/templates/shared/dashboard/src/components/Nav.astro.hbs +59 -0
- package/templates/shared/dashboard/src/components/infrastructure/AlertHistory.tsx +57 -0
- package/templates/shared/dashboard/src/components/infrastructure/InfrastructureStats.tsx +73 -0
- package/templates/shared/dashboard/src/components/infrastructure/ServiceRegistry.tsx +55 -0
- package/templates/shared/dashboard/src/components/infrastructure/UptimeStatus.tsx +56 -0
- package/templates/shared/dashboard/src/components/infrastructure/index.ts +4 -0
- package/templates/shared/dashboard/src/components/overview/ActivityFeed.tsx +134 -0
- package/templates/shared/dashboard/src/components/overview/CostQuadrant.tsx +131 -0
- package/templates/shared/dashboard/src/components/overview/ErrorsQuadrant.tsx +113 -0
- package/templates/shared/dashboard/src/components/overview/HealthQuadrant.tsx +87 -0
- package/templates/shared/dashboard/src/components/overview/MissionControl.tsx +139 -0
- package/templates/shared/dashboard/src/components/resources/AllowanceStatus.tsx +44 -0
- package/templates/shared/dashboard/src/components/resources/CostCentreOverview.tsx +42 -0
- package/templates/shared/dashboard/src/components/resources/ResourceTabs.tsx +69 -0
- package/templates/shared/dashboard/src/components/resources/index.ts +3 -0
- package/templates/shared/dashboard/src/components/settings/SettingsCard.tsx +21 -0
- package/templates/shared/dashboard/src/components/settings/index.ts +1 -0
- package/templates/shared/dashboard/src/components/ui/AlertBanner.tsx +39 -0
- package/templates/shared/dashboard/src/components/ui/Breadcrumbs.tsx +27 -0
- package/templates/shared/dashboard/src/components/ui/EmptyState.tsx +26 -0
- package/templates/shared/dashboard/src/components/ui/ErrorBoundary.tsx +42 -0
- package/templates/shared/dashboard/src/components/ui/LoadingSkeleton.tsx +18 -0
- package/templates/shared/dashboard/src/components/ui/PageShell.tsx +26 -0
- package/templates/shared/dashboard/src/components/ui/Sparkline.tsx +127 -0
- package/templates/shared/dashboard/src/components/ui/StatusDot.tsx +21 -0
- package/templates/shared/dashboard/src/components/ui/Toast.tsx +44 -0
- package/templates/shared/dashboard/src/components/ui/index.ts +9 -0
- package/templates/shared/dashboard/src/components/usage/AnomaliesWidget.tsx +68 -0
- package/templates/shared/dashboard/src/components/usage/HourlyUsageChart.tsx +55 -0
- package/templates/shared/dashboard/src/components/usage/PlanAllowanceDashboard.tsx +67 -0
- package/templates/shared/dashboard/src/components/usage/ProjectCostBreakdown.tsx +55 -0
- package/templates/shared/dashboard/src/components/usage/index.ts +4 -0
- package/templates/shared/dashboard/src/env.d.ts.hbs +34 -0
- package/templates/shared/dashboard/src/layouts/DashboardLayout.astro +37 -0
- package/templates/shared/dashboard/src/lib/cloudflare/costs.ts +21 -0
- package/templates/shared/dashboard/src/lib/fetch.ts +29 -0
- package/templates/shared/dashboard/src/lib/types.ts +72 -0
- package/templates/shared/dashboard/src/middleware/auth.ts +100 -0
- package/templates/shared/dashboard/src/middleware/index.ts +1 -0
- package/templates/shared/dashboard/src/pages/api/costs/overview.ts +65 -0
- package/templates/shared/dashboard/src/pages/api/costs/providers.ts +47 -0
- package/templates/shared/dashboard/src/pages/api/infrastructure/services.ts +55 -0
- package/templates/shared/dashboard/src/pages/api/infrastructure/stats.ts +99 -0
- package/templates/shared/dashboard/src/pages/api/overview/summary.ts +311 -0
- package/templates/shared/dashboard/src/pages/api/usage/allowances.ts +56 -0
- package/templates/shared/dashboard/src/pages/api/usage/anomalies.ts +45 -0
- package/templates/shared/dashboard/src/pages/api/usage/billing.ts +53 -0
- package/templates/shared/dashboard/src/pages/api/usage/circuit-breakers.ts +44 -0
- package/templates/shared/dashboard/src/pages/api/usage/granular.ts +50 -0
- package/templates/shared/dashboard/src/pages/api/usage/hourly.ts +45 -0
- package/templates/shared/dashboard/src/pages/api/usage/projects.ts +51 -0
- package/templates/shared/dashboard/src/pages/api/usage/status.ts +42 -0
- package/templates/shared/dashboard/src/pages/api/user/identity.ts +11 -0
- package/templates/shared/dashboard/src/pages/dashboard.astro +11 -0
- package/templates/shared/dashboard/src/pages/index.astro +3 -0
- package/templates/shared/dashboard/src/pages/resources.astro +11 -0
- package/templates/shared/dashboard/src/pages/settings/index.astro +28 -0
- package/templates/shared/dashboard/src/pages/settings/notifications.astro +34 -0
- package/templates/shared/dashboard/src/pages/settings/thresholds.astro +39 -0
- package/templates/shared/dashboard/src/pages/settings/usage.astro +28 -0
- package/templates/shared/dashboard/src/styles/global.css +29 -0
- package/templates/shared/dashboard/tailwind.config.mjs +9 -0
- package/templates/shared/dashboard/tsconfig.json +9 -0
- package/templates/shared/dashboard/wrangler.json.hbs +47 -0
- package/templates/shared/docs/architecture.md +89 -0
- package/templates/shared/docs/post-deploy-runbook.md +126 -0
- package/templates/shared/docs/troubleshooting.md +91 -0
- package/templates/shared/package.json.hbs +17 -1
- package/templates/shared/scripts/ops/backfill-cloudflare-daily.ts +145 -0
- package/templates/shared/scripts/ops/backfill-cloudflare-hourly.ts +473 -0
- package/templates/shared/scripts/ops/backfill-monthly-rollups.ts +125 -0
- package/templates/shared/scripts/ops/discover-graphql-datasets.ts +482 -0
- package/templates/shared/scripts/ops/reset-budget-state.ts +279 -0
- package/templates/shared/scripts/ops/validate-controls.js +141 -0
- package/templates/shared/scripts/ops/validate-pipeline.ts +237 -0
- package/templates/shared/scripts/ops/verify-account-completeness.ts +236 -0
- package/templates/shared/scripts/validate-schemas.js +61 -0
- package/templates/shared/tests/contract/validate-schemas.test.ts +130 -0
- package/templates/shared/tests/fixtures/telemetry-envelope-invalid.json +9 -0
- package/templates/shared/tests/fixtures/telemetry-envelope-valid.json +27 -0
- package/templates/shared/tests/helpers/mock-d1.ts +61 -0
- package/templates/shared/tests/helpers/mock-kv.ts +37 -0
- package/templates/shared/tests/unit/workers/batch-persistence.test.ts +133 -0
- package/templates/shared/tests/unit/workers/budget-enforcement.test.ts +214 -0
- package/templates/shared/vitest.config.ts +18 -0
- package/templates/shared/workers/lib/usage/collectors/anthropic.ts +114 -0
- package/templates/shared/workers/lib/usage/collectors/apify.ts +96 -0
- package/templates/shared/workers/lib/usage/collectors/custom-http.ts +151 -0
- package/templates/shared/workers/lib/usage/collectors/deepseek.ts +92 -0
- package/templates/shared/workers/lib/usage/collectors/gemini.ts +263 -0
- package/templates/shared/workers/lib/usage/collectors/github.ts +362 -0
- package/templates/shared/workers/lib/usage/collectors/index.ts +31 -15
- package/templates/shared/workers/lib/usage/collectors/minimax.ts +106 -0
- package/templates/shared/workers/lib/usage/collectors/openai.ts +171 -0
- package/templates/shared/workers/lib/usage/collectors/resend.ts +79 -0
- package/templates/shared/workers/lib/usage/collectors/stripe.ts +192 -0
- package/templates/shared/workers/lib/usage/shared/types.ts +46 -0
- package/templates/shared/workers/platform-usage.ts +98 -8
- package/templates/standard/dashboard/src/components/errors/ErrorStats.tsx +53 -0
- package/templates/standard/dashboard/src/components/errors/ErrorsTable.tsx +133 -0
- package/templates/standard/dashboard/src/components/errors/index.ts +2 -0
- package/templates/standard/dashboard/src/components/health/CircuitBreakerEvents.tsx +69 -0
- package/templates/standard/dashboard/src/components/health/CircuitBreakerPanel.tsx +97 -0
- package/templates/standard/dashboard/src/components/health/DlqStatusCard.tsx +52 -0
- package/templates/standard/dashboard/src/components/health/HealthTabs.tsx +86 -0
- package/templates/standard/dashboard/src/components/health/index.ts +4 -0
- package/templates/standard/dashboard/src/lib/errors.ts +28 -0
- package/templates/standard/dashboard/src/pages/api/errors/[fingerprint]/mute.ts +49 -0
- package/templates/standard/dashboard/src/pages/api/errors/[fingerprint]/resolve.ts +36 -0
- package/templates/standard/dashboard/src/pages/api/errors/[fingerprint].ts +55 -0
- package/templates/standard/dashboard/src/pages/api/errors/index.ts +58 -0
- package/templates/standard/dashboard/src/pages/api/errors/stats.ts +55 -0
- package/templates/standard/dashboard/src/pages/api/health/audit-history.ts +37 -0
- package/templates/standard/dashboard/src/pages/api/health/dlq.ts +43 -0
- package/templates/standard/dashboard/src/pages/circuit-breakers.astro +13 -0
- package/templates/standard/dashboard/src/pages/errors.astro +13 -0
- package/templates/standard/dashboard/src/pages/health.astro +11 -0
- package/templates/standard/migrations/009_topology_mapper.sql +65 -0
- package/templates/standard/tests/unit/error-collector/capture.test.ts +106 -0
- package/templates/standard/tests/unit/error-collector/fingerprint.test.ts +155 -0
- package/templates/standard/workers/lib/error-collector/email-health-alerts.ts +37 -3
- package/templates/standard/workers/lib/error-collector/gap-alerts.ts +32 -1
- package/templates/standard/workers/lib/mapper/attribution-check.ts +339 -0
- package/templates/standard/workers/lib/mapper/index.ts +7 -0
- package/templates/standard/workers/platform-mapper.ts +482 -0
- package/templates/standard/workers/platform-sdk-test-client.ts +125 -0
- package/templates/standard/wrangler.mapper.jsonc.hbs +85 -0
- 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
|
+
}
|