@littlebearapps/platform-admin-sdk 1.4.2 → 1.5.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 +121 -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/PatternStats.tsx +60 -0
- package/templates/full/dashboard/src/components/patterns/SuggestionsQueue.tsx +115 -0
- package/templates/full/dashboard/src/components/patterns/index.ts +2 -0
- package/templates/full/dashboard/src/components/search/SearchModal.tsx +108 -0
- package/templates/full/dashboard/src/pages/api/notifications/index.ts +47 -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/index.ts +36 -0
- package/templates/full/dashboard/src/pages/api/patterns/reject.ts +54 -0
- package/templates/full/dashboard/src/pages/api/search/index.ts +74 -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/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/platform-check.yml.hbs +28 -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 +57 -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/Sparkline.tsx +127 -0
- package/templates/shared/dashboard/src/components/ui/StatusDot.tsx +21 -0
- package/templates/shared/dashboard/src/components/ui/index.ts +3 -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/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/overview/summary.ts +311 -0
- package/templates/shared/dashboard/src/pages/api/usage/circuit-breakers.ts +44 -0
- package/templates/shared/dashboard/src/pages/api/usage/status.ts +42 -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/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/package.json.hbs +12 -1
- package/templates/shared/scripts/ops/backfill-cloudflare-hourly.ts +473 -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-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/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/DlqStatusCard.tsx +52 -0
- package/templates/standard/dashboard/src/components/health/HealthTabs.tsx +86 -0
- package/templates/standard/dashboard/src/components/health/index.ts +2 -0
- package/templates/standard/dashboard/src/lib/errors.ts +28 -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/dlq.ts +43 -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/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,182 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* Set KV Pricing Configuration
|
|
4
|
+
*
|
|
5
|
+
* This script sets the Cloudflare pricing configuration in the PLATFORM_CACHE KV namespace.
|
|
6
|
+
* The platform-usage worker reads this configuration at runtime to calculate costs.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* npx tsx scripts/ops/set-kv-pricing.ts [--dry-run]
|
|
10
|
+
*
|
|
11
|
+
* Prerequisites:
|
|
12
|
+
* - wrangler authenticated (npx wrangler login)
|
|
13
|
+
* - KV namespace ID set below (or via PLATFORM_CACHE_KV_ID env var)
|
|
14
|
+
*
|
|
15
|
+
* Pricing sources:
|
|
16
|
+
* - https://developers.cloudflare.com/workers/platform/pricing/
|
|
17
|
+
* - https://developers.cloudflare.com/d1/platform/pricing/
|
|
18
|
+
* - https://developers.cloudflare.com/kv/platform/pricing/
|
|
19
|
+
* - https://developers.cloudflare.com/r2/pricing/
|
|
20
|
+
* - https://developers.cloudflare.com/vectorize/platform/pricing/
|
|
21
|
+
* - https://developers.cloudflare.com/workers-ai/platform/pricing/
|
|
22
|
+
* - https://developers.cloudflare.com/durable-objects/platform/pricing/
|
|
23
|
+
* - https://developers.cloudflare.com/queues/platform/pricing/
|
|
24
|
+
* - https://developers.cloudflare.com/pages/platform/pricing/
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const PRICING_KEY = 'platform-usage:pricing:v1';
|
|
28
|
+
|
|
29
|
+
// TODO: Set your PLATFORM_CACHE KV namespace ID here or via PLATFORM_CACHE_KV_ID env var
|
|
30
|
+
const KV_NAMESPACE_ID = process.env.PLATFORM_CACHE_KV_ID || 'YOUR_PLATFORM_CACHE_KV_ID';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Current Cloudflare pricing (as of January 2026)
|
|
34
|
+
*
|
|
35
|
+
* Notes:
|
|
36
|
+
* - All prices in USD
|
|
37
|
+
* - Workers Paid plan includes 10M requests/month and 30M CPU ms/month
|
|
38
|
+
* - D1: First 25B rows read and 50M rows written are included
|
|
39
|
+
* - KV: First 10M reads, 1M writes, 1M deletes, 1M lists, 1GB storage included
|
|
40
|
+
* - R2: First 10GB storage, 1M Class A ops, 10M Class B ops included
|
|
41
|
+
* - Vectorize: First 10M stored dimensions and 50M queried dimensions included
|
|
42
|
+
* - Workers AI: First 10,000 neurons/day included
|
|
43
|
+
* - Durable Objects: First 1M requests, 400K GB-seconds, 1GB storage included
|
|
44
|
+
* - Queues: First 1M operations included
|
|
45
|
+
* - Pages: 500 builds/month included
|
|
46
|
+
*
|
|
47
|
+
* When Cloudflare updates pricing, update this config and re-run the script.
|
|
48
|
+
* Also create a new row in pricing_versions (migration 010) for audit trail.
|
|
49
|
+
*/
|
|
50
|
+
const PRICING_CONFIG = {
|
|
51
|
+
version: '2026-01-14',
|
|
52
|
+
lastUpdated: new Date().toISOString(),
|
|
53
|
+
source: 'cloudflare-pricing-pages',
|
|
54
|
+
|
|
55
|
+
workers: {
|
|
56
|
+
baseCostMonthly: 5.0,
|
|
57
|
+
includedRequests: 10_000_000,
|
|
58
|
+
requestsPerMillion: 0.3,
|
|
59
|
+
cpuMsPerMillion: 0.02,
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
d1: {
|
|
63
|
+
rowsReadPerBillion: 0.001,
|
|
64
|
+
rowsWrittenPerMillion: 1.0,
|
|
65
|
+
storagePerGb: 0.75,
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
kv: {
|
|
69
|
+
readsPerMillion: 0.5,
|
|
70
|
+
writesPerMillion: 5.0,
|
|
71
|
+
deletesPerMillion: 5.0,
|
|
72
|
+
listsPerMillion: 5.0,
|
|
73
|
+
storagePerGb: 0.5,
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
r2: {
|
|
77
|
+
storagePerGbMonth: 0.015,
|
|
78
|
+
classAPerMillion: 4.5,
|
|
79
|
+
classBPerMillion: 0.36,
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
vectorize: {
|
|
83
|
+
storedDimensionsPerMillion: 0.01,
|
|
84
|
+
queriedDimensionsPerMillion: 0.04,
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
workersAI: {
|
|
88
|
+
neuronsPerThousand: 0.011,
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
durableObjects: {
|
|
92
|
+
requestsPerMillion: 0.15,
|
|
93
|
+
gbSecondsPerMillion: 12.5,
|
|
94
|
+
storagePerGbMonth: 0.2,
|
|
95
|
+
readsPerMillion: 0.2,
|
|
96
|
+
writesPerMillion: 1.0,
|
|
97
|
+
deletesPerMillion: 1.0,
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
queues: {
|
|
101
|
+
messagesPerMillion: 0.4,
|
|
102
|
+
operationsPerMillion: 0.4,
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
pages: {
|
|
106
|
+
buildCost: 0.0,
|
|
107
|
+
bandwidthPerGb: 0.0,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
async function main() {
|
|
112
|
+
const isDryRun = process.argv.includes('--dry-run');
|
|
113
|
+
|
|
114
|
+
if (KV_NAMESPACE_ID === 'YOUR_PLATFORM_CACHE_KV_ID') {
|
|
115
|
+
console.error('ERROR: Set PLATFORM_CACHE_KV_ID env var or update KV_NAMESPACE_ID in this script.');
|
|
116
|
+
console.error('Find your namespace ID: npx wrangler kv namespace list');
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
console.log('='.repeat(60));
|
|
121
|
+
console.log('Platform Usage Pricing Configuration');
|
|
122
|
+
console.log('='.repeat(60));
|
|
123
|
+
console.log(`Version: ${PRICING_CONFIG.version}`);
|
|
124
|
+
console.log(`KV Key: ${PRICING_KEY}`);
|
|
125
|
+
console.log(`Namespace: ${KV_NAMESPACE_ID}`);
|
|
126
|
+
console.log(`Dry Run: ${isDryRun}`);
|
|
127
|
+
console.log('');
|
|
128
|
+
|
|
129
|
+
console.log('Pricing Summary:');
|
|
130
|
+
console.log('-'.repeat(40));
|
|
131
|
+
console.log(`Workers: $${PRICING_CONFIG.workers.requestsPerMillion}/M requests`);
|
|
132
|
+
console.log(
|
|
133
|
+
`D1: $${PRICING_CONFIG.d1.rowsReadPerBillion}/B rows read, $${PRICING_CONFIG.d1.rowsWrittenPerMillion}/M rows written`
|
|
134
|
+
);
|
|
135
|
+
console.log(
|
|
136
|
+
`KV: $${PRICING_CONFIG.kv.readsPerMillion}/M reads, $${PRICING_CONFIG.kv.writesPerMillion}/M writes`
|
|
137
|
+
);
|
|
138
|
+
console.log(
|
|
139
|
+
`R2: $${PRICING_CONFIG.r2.storagePerGbMonth}/GB storage, $${PRICING_CONFIG.r2.classAPerMillion}/M Class A`
|
|
140
|
+
);
|
|
141
|
+
console.log(`Vectorize: $${PRICING_CONFIG.vectorize.storedDimensionsPerMillion}/M stored dims`);
|
|
142
|
+
console.log(`Workers AI: $${PRICING_CONFIG.workersAI.neuronsPerThousand}/1K neurons`);
|
|
143
|
+
console.log(`Durable Objects: $${PRICING_CONFIG.durableObjects.requestsPerMillion}/M requests`);
|
|
144
|
+
console.log(`Queues: $${PRICING_CONFIG.queues.messagesPerMillion}/M messages`);
|
|
145
|
+
console.log('');
|
|
146
|
+
|
|
147
|
+
if (isDryRun) {
|
|
148
|
+
console.log('[DRY RUN] Would write the following to KV:');
|
|
149
|
+
console.log(JSON.stringify(PRICING_CONFIG, null, 2));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Write to KV using wrangler
|
|
154
|
+
const { execSync } = await import('child_process');
|
|
155
|
+
const pricingJson = JSON.stringify(PRICING_CONFIG);
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
console.log('Writing pricing to KV...');
|
|
159
|
+
execSync(
|
|
160
|
+
`npx wrangler kv key put "${PRICING_KEY}" '${pricingJson}' --namespace-id ${KV_NAMESPACE_ID} --remote`,
|
|
161
|
+
{ stdio: 'inherit', cwd: process.cwd() }
|
|
162
|
+
);
|
|
163
|
+
console.log('');
|
|
164
|
+
console.log('Pricing configuration saved successfully.');
|
|
165
|
+
console.log('');
|
|
166
|
+
console.log('To verify:');
|
|
167
|
+
console.log(
|
|
168
|
+
` npx wrangler kv key get "${PRICING_KEY}" --namespace-id ${KV_NAMESPACE_ID} --remote`
|
|
169
|
+
);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error('Failed to write pricing to KV:', error);
|
|
172
|
+
console.log('');
|
|
173
|
+
console.log('Alternative: Copy the JSON below and use Cloudflare dashboard:');
|
|
174
|
+
console.log('1. Go to Workers & Pages > KV > PLATFORM_CACHE');
|
|
175
|
+
console.log(`2. Create key: ${PRICING_KEY}`);
|
|
176
|
+
console.log('3. Paste value:');
|
|
177
|
+
console.log(JSON.stringify(PRICING_CONFIG, null, 2));
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
main();
|
|
@@ -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
|
+
}
|