@littlebearapps/platform-admin-sdk 2.0.0 → 2.2.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 (185) hide show
  1. package/README.md +4 -7
  2. package/dist/templates.d.ts +1 -1
  3. package/dist/templates.js +206 -4
  4. package/package.json +1 -1
  5. package/templates/full/dashboard/src/components/notifications/NotificationDropdown.tsx +130 -0
  6. package/templates/full/dashboard/src/components/notifications/NotificationItem.tsx +264 -0
  7. package/templates/full/dashboard/src/components/patterns/PatternInfoButton.tsx +60 -0
  8. package/templates/full/dashboard/src/components/reports/DigestStats.tsx +151 -0
  9. package/templates/full/dashboard/src/components/reports/FeatureUsageReport.tsx +339 -0
  10. package/templates/full/dashboard/src/components/reports/HealthTrendsReport.tsx +192 -0
  11. package/templates/full/dashboard/src/components/search/SearchResultGroup.tsx +46 -0
  12. package/templates/full/dashboard/src/components/search/SearchResultItem.tsx +212 -0
  13. package/templates/full/dashboard/src/components/usage/AIModelBreakdown.tsx +364 -0
  14. package/templates/full/dashboard/src/components/usage/unified/Recommendations.tsx +149 -0
  15. package/templates/full/dashboard/src/lib/cloudflare/alerting.ts +486 -0
  16. package/templates/full/dashboard/src/lib/cloudflare/graphql.ts +4785 -0
  17. package/templates/full/dashboard/src/lib/cloudflare/project-registry.ts +451 -0
  18. package/templates/full/dashboard/src/lib/notifications/api.ts +197 -0
  19. package/templates/full/dashboard/src/lib/notifications/types.ts.hbs +97 -0
  20. package/templates/full/dashboard/src/lib/patterns/api.ts +120 -0
  21. package/templates/full/dashboard/src/lib/patterns/types.ts +127 -0
  22. package/templates/full/dashboard/src/lib/reports/types.ts +231 -0
  23. package/templates/full/dashboard/src/lib/search/api.ts +258 -0
  24. package/templates/full/dashboard/src/lib/search/types.ts.hbs +115 -0
  25. package/templates/full/dashboard/src/lib/settings/api.ts.hbs +201 -0
  26. package/templates/full/dashboard/src/lib/settings/types.ts.hbs +104 -0
  27. package/templates/full/dashboard/src/lib/usage/allowance-config.ts.hbs +547 -0
  28. package/templates/full/dashboard/src/lib/usage/providers.ts +331 -0
  29. package/templates/full/dashboard/src/pages/api/patterns/[id]/approve.ts +49 -0
  30. package/templates/full/dashboard/src/pages/api/patterns/[id]/reject.ts +50 -0
  31. package/templates/full/dashboard/src/pages/api/reports/digests/stats.ts +38 -0
  32. package/templates/full/dashboard/src/pages/api/reports/digests.ts +39 -0
  33. package/templates/full/dashboard/src/pages/api/search/reindex/[type].ts +56 -0
  34. package/templates/full/dashboard/src/pages/api/test-reports/[id].ts +102 -0
  35. package/templates/full/dashboard/src/pages/feedback.astro +365 -0
  36. package/templates/full/dashboard/src/pages/kiosk.astro +206 -0
  37. package/templates/full/dashboard/src/pages/map.astro +561 -0
  38. package/templates/full/dashboard/src/pages/revenue.astro +72 -0
  39. package/templates/full/dashboard/src/pages/tests.astro +431 -0
  40. package/templates/full/scripts/ops/audit-cost-anomaly.ts +430 -0
  41. package/templates/full/scripts/ops/verify-account-total.ts +256 -0
  42. package/templates/full/tests/integration/feedback-schema.test.ts +361 -0
  43. package/templates/full/tests/integration/r2-archive.test.ts +108 -0
  44. package/templates/shared/.github/workflows/dependabot-automerge.yml +41 -0
  45. package/templates/shared/.github/workflows/validate-controls.yml +27 -0
  46. package/templates/shared/dashboard/src/components/Breadcrumbs.astro +101 -0
  47. package/templates/shared/dashboard/src/components/EmptyState.astro +46 -0
  48. package/templates/shared/dashboard/src/components/ErrorBoundary.astro +79 -0
  49. package/templates/shared/dashboard/src/components/LoadingSkeleton.astro +105 -0
  50. package/templates/shared/dashboard/src/components/PageShell.astro +72 -0
  51. package/templates/shared/dashboard/src/components/SkipLinks.astro +22 -0
  52. package/templates/shared/dashboard/src/components/Toast.astro +170 -0
  53. package/templates/shared/dashboard/src/components/ToastContainer.astro +156 -0
  54. package/templates/shared/dashboard/src/components/costs/ProviderCostsGrid.tsx +401 -0
  55. package/templates/shared/dashboard/src/components/costs/index.ts +4 -0
  56. package/templates/shared/dashboard/src/components/overview/AlertBanner.tsx +94 -0
  57. package/templates/shared/dashboard/src/components/overview/index.ts +9 -0
  58. package/templates/shared/dashboard/src/components/reports/ReportInfoButton.tsx +98 -0
  59. package/templates/shared/dashboard/src/components/resources/CostChart.tsx +170 -0
  60. package/templates/shared/dashboard/src/components/resources/ProviderCard.tsx +272 -0
  61. package/templates/shared/dashboard/src/components/resources/ProviderDetail.tsx +293 -0
  62. package/templates/shared/dashboard/src/components/settings/SettingsCard.astro +102 -0
  63. package/templates/shared/dashboard/src/components/usage/AllowanceGauge.astro +170 -0
  64. package/templates/shared/dashboard/src/components/usage/AnomalyAlerts.astro +633 -0
  65. package/templates/shared/dashboard/src/components/usage/BillingCycleCountdown.astro +192 -0
  66. package/templates/shared/dashboard/src/components/usage/BurnRateHero.astro +539 -0
  67. package/templates/shared/dashboard/src/components/usage/CircuitBreakerEventLog.astro +542 -0
  68. package/templates/shared/dashboard/src/components/usage/CircuitBreakerPanel.tsx +292 -0
  69. package/templates/shared/dashboard/src/components/usage/CircuitBreakerStatus.astro +669 -0
  70. package/templates/shared/dashboard/src/components/usage/CompactThresholdBanner.astro +531 -0
  71. package/templates/shared/dashboard/src/components/usage/ComparisonModeSelector.astro +651 -0
  72. package/templates/shared/dashboard/src/components/usage/CostBreakdownChart.astro +381 -0
  73. package/templates/shared/dashboard/src/components/usage/CostBreakdownTable.astro +210 -0
  74. package/templates/shared/dashboard/src/components/usage/CostDataTable.astro +0 -0
  75. package/templates/shared/dashboard/src/components/usage/CostDonutChart.astro +311 -0
  76. package/templates/shared/dashboard/src/components/usage/DailyCostChart.astro +632 -0
  77. package/templates/shared/dashboard/src/components/usage/ExportButton.astro +114 -0
  78. package/templates/shared/dashboard/src/components/usage/FeatureBudgetsTable.astro +872 -0
  79. package/templates/shared/dashboard/src/components/usage/FilterBar.astro +190 -0
  80. package/templates/shared/dashboard/src/components/usage/FilterToggles.astro +175 -0
  81. package/templates/shared/dashboard/src/components/usage/GitHubUsageCard.astro +537 -0
  82. package/templates/shared/dashboard/src/components/usage/OverageCostCard.astro +212 -0
  83. package/templates/shared/dashboard/src/components/usage/PlanUtilizationCard.astro +193 -0
  84. package/templates/shared/dashboard/src/components/usage/ProjectCard.astro +640 -0
  85. package/templates/shared/dashboard/src/components/usage/ProjectCardsGrid.astro +272 -0
  86. package/templates/shared/dashboard/src/components/usage/ResourceSearch.astro +279 -0
  87. package/templates/shared/dashboard/src/components/usage/ServiceUtilizationList.astro +604 -0
  88. package/templates/shared/dashboard/src/components/usage/SparklineCard.astro +399 -0
  89. package/templates/shared/dashboard/src/components/usage/StatsHero.astro +600 -0
  90. package/templates/shared/dashboard/src/components/usage/TableFilters.astro +1033 -0
  91. package/templates/shared/dashboard/src/components/usage/ThresholdAlert.astro +271 -0
  92. package/templates/shared/dashboard/src/components/usage/ThresholdSettings.astro +618 -0
  93. package/templates/shared/dashboard/src/components/usage/TopSpenderCard.astro +170 -0
  94. package/templates/shared/dashboard/src/components/usage/UnifiedResourceTable.astro +1737 -0
  95. package/templates/shared/dashboard/src/components/usage/UsageCard.astro +135 -0
  96. package/templates/shared/dashboard/src/components/usage/UsageHealthBanner.astro +387 -0
  97. package/templates/shared/dashboard/src/components/usage/UtilizationBar.astro +159 -0
  98. package/templates/shared/dashboard/src/components/usage/WorkersBreakdownTable.astro +659 -0
  99. package/templates/shared/dashboard/src/components/usage/daily/CostChart.astro +461 -0
  100. package/templates/shared/dashboard/src/components/usage/daily/CostTable.astro +946 -0
  101. package/templates/shared/dashboard/src/components/usage/daily/DailyOverview.astro +1079 -0
  102. package/templates/shared/dashboard/src/components/usage/design-tokens.ts +187 -0
  103. package/templates/shared/dashboard/src/components/usage/filters/InlineDateRange.astro +285 -0
  104. package/templates/shared/dashboard/src/components/usage/filters/PeriodButtons.astro +157 -0
  105. package/templates/shared/dashboard/src/components/usage/filters/ProjectSelect.astro +284 -0
  106. package/templates/shared/dashboard/src/components/usage/react/DashboardShell.tsx +263 -0
  107. package/templates/shared/dashboard/src/components/usage/react/StatusBadge.tsx +77 -0
  108. package/templates/shared/dashboard/src/components/usage/react/UsageChart.tsx +391 -0
  109. package/templates/shared/dashboard/src/components/usage/react/index.ts.hbs +30 -0
  110. package/templates/shared/dashboard/src/components/usage/react/types.ts +137 -0
  111. package/templates/shared/dashboard/src/components/usage/scripts/ai-tab-controller.ts +419 -0
  112. package/templates/shared/dashboard/src/components/usage/scripts/constants.ts +60 -0
  113. package/templates/shared/dashboard/src/components/usage/scripts/formatters.ts +62 -0
  114. package/templates/shared/dashboard/src/components/usage/scripts/overview-controller.ts +1633 -0
  115. package/templates/shared/dashboard/src/components/usage/scripts/resource-table-builder.ts +294 -0
  116. package/templates/shared/dashboard/src/components/usage/scripts/tabs-filters-controller.ts +464 -0
  117. package/templates/shared/dashboard/src/components/usage/state/index.ts +55 -0
  118. package/templates/shared/dashboard/src/components/usage/state/usageActions.ts +439 -0
  119. package/templates/shared/dashboard/src/components/usage/state/usageStore.ts +376 -0
  120. package/templates/shared/dashboard/src/components/usage/transformers.ts +478 -0
  121. package/templates/shared/dashboard/src/components/usage/types.ts +283 -0
  122. package/templates/shared/dashboard/src/components/usage/unified/AlertBanner.tsx +172 -0
  123. package/templates/shared/dashboard/src/components/usage/unified/HeroCardsRow.tsx +757 -0
  124. package/templates/shared/dashboard/src/components/usage/unified/LiveHeader.tsx +169 -0
  125. package/templates/shared/dashboard/src/components/usage/unified/ProjectsTable.tsx +448 -0
  126. package/templates/shared/dashboard/src/components/usage/unified/ResourceBreakdown.tsx +236 -0
  127. package/templates/shared/dashboard/src/components/usage/unified/Sparkline.tsx +127 -0
  128. package/templates/shared/dashboard/src/components/usage/unified/UnifiedShell.tsx +893 -0
  129. package/templates/shared/dashboard/src/components/usage/unified/index.ts.hbs +50 -0
  130. package/templates/shared/dashboard/src/components/usage/unified/types.ts +416 -0
  131. package/templates/shared/dashboard/src/components/usage/usage-colors.ts +292 -0
  132. package/templates/shared/dashboard/src/lib/cloudflare/analytics.ts +310 -0
  133. package/templates/shared/dashboard/src/lib/cloudflare/d1.ts +55 -0
  134. package/templates/shared/dashboard/src/lib/cloudflare/index.ts.hbs +120 -0
  135. package/templates/shared/dashboard/src/lib/infrastructure/types.ts +116 -0
  136. package/templates/shared/dashboard/src/lib/usage/fetchWithDedup.ts +101 -0
  137. package/templates/shared/dashboard/src/lib/usage/index.ts.hbs +12 -0
  138. package/templates/shared/dashboard/src/pages/api/usage/ai-models.ts +235 -0
  139. package/templates/shared/dashboard/src/pages/api/usage/billing-context.ts +296 -0
  140. package/templates/shared/scripts/test-telemetry-flow.ts +464 -0
  141. package/templates/shared/tests/e2e/usage-api.test.ts +909 -0
  142. package/templates/shared/tests/e2e/usage-export.test.ts +784 -0
  143. package/templates/shared/tests/e2e/usage-mobile.test.ts +531 -0
  144. package/templates/shared/tests/helpers/mock-storage.ts +166 -0
  145. package/templates/shared/tests/integration/kv-cache.test.ts +252 -0
  146. package/templates/shared/tests/integration/platform-usage.test.ts +956 -0
  147. package/templates/shared/tests/unit/billing.test.ts +331 -0
  148. package/templates/shared/tests/unit/cloudflare/graphql.test.ts +217 -0
  149. package/templates/shared/tests/unit/components/usage-transformers.test.ts +473 -0
  150. package/templates/shared/tests/unit/control.test.ts +226 -0
  151. package/templates/shared/tests/unit/cost-calculator.test.ts +141 -0
  152. package/templates/shared/tests/unit/economics.test.ts +365 -0
  153. package/templates/shared/tests/unit/telemetry-sampling.test.ts +401 -0
  154. package/templates/standard/dashboard/src/components/errors/PriorityBadge.astro +27 -0
  155. package/templates/standard/dashboard/src/components/infrastructure/HealthchecksStatus.tsx +293 -0
  156. package/templates/standard/dashboard/src/components/infrastructure/InfrastructureTabs.tsx +268 -0
  157. package/templates/standard/dashboard/src/components/reports/CircuitBreakerReport.tsx +474 -0
  158. package/templates/standard/dashboard/src/components/reports/CostTrendsReport.tsx +229 -0
  159. package/templates/standard/dashboard/src/components/reports/ErrorTrendsReport.tsx +244 -0
  160. package/templates/standard/dashboard/src/components/reports/ProjectHealthTable.tsx +251 -0
  161. package/templates/standard/dashboard/src/components/reports/WarningDigestsTable.tsx +298 -0
  162. package/templates/standard/dashboard/src/components/usage/react/UsageTable.tsx +385 -0
  163. package/templates/standard/dashboard/src/components/usage/unified/CircuitBreakerEvents.tsx +305 -0
  164. package/templates/standard/dashboard/src/components/usage/unified/FeatureBudgets.tsx +472 -0
  165. package/templates/standard/dashboard/src/lib/errors/api.ts +84 -0
  166. package/templates/standard/dashboard/src/lib/errors/types.ts +75 -0
  167. package/templates/standard/dashboard/src/lib/infrastructure/api.ts +141 -0
  168. package/templates/standard/dashboard/src/lib/infrastructure/gatus.ts.hbs +112 -0
  169. package/templates/standard/dashboard/src/lib/services/proxy/index.ts +20 -0
  170. package/templates/standard/dashboard/src/lib/services/proxy/proxy.ts +244 -0
  171. package/templates/standard/dashboard/src/lib/services/proxy/types.ts +81 -0
  172. package/templates/standard/dashboard/src/pages/analytics.astro +64 -0
  173. package/templates/standard/dashboard/src/pages/api/infrastructure/alerts.ts +85 -0
  174. package/templates/standard/dashboard/src/pages/api/infrastructure/healthchecks/[id]/flips.ts +110 -0
  175. package/templates/standard/dashboard/src/pages/api/infrastructure/healthchecks.ts +101 -0
  176. package/templates/standard/dashboard/src/pages/api/infrastructure/uptime/[id]/response-times.ts +121 -0
  177. package/templates/standard/dashboard/src/pages/api/infrastructure/uptime.ts +89 -0
  178. package/templates/standard/dashboard/src/pages/api/test/service-auth.ts +178 -0
  179. package/templates/standard/tests/integration/connectors.test.ts +241 -0
  180. package/templates/standard/tests/integration/github-monitor.test.ts +143 -0
  181. package/templates/standard/tests/integration/ingestion.test.ts +211 -0
  182. package/templates/standard/tests/integration/platform-sentinel.test.ts +497 -0
  183. package/templates/standard/tests/unit/cloudflare/alerting.test.ts +480 -0
  184. package/templates/standard/tests/unit/error-collector/dedup.test.ts +350 -0
  185. package/templates/standard/tests/unit/error-collector/github.test.ts +187 -0
@@ -0,0 +1,331 @@
1
+ /**
2
+ * Third-Party Provider Stubs
3
+ *
4
+ * Interface definitions and fetcher stubs for third-party service usage tracking.
5
+ * These will be implemented incrementally as integrations are built.
6
+ *
7
+ * Supported providers (planned):
8
+ * - GitHub (Actions minutes, Advanced Security seats, storage)
9
+ * - OpenAI (API usage, tokens)
10
+ * - Apify (Actor runs, compute units)
11
+ * - Anthropic (API usage, tokens)
12
+ * - Resend (emails sent)
13
+ */
14
+
15
+ /**
16
+ * Provider identifier
17
+ */
18
+ export type ProviderId = 'github' | 'openai' | 'apify' | 'anthropic' | 'resend';
19
+
20
+ /**
21
+ * Usage record from a third-party provider
22
+ */
23
+ export interface ThirdPartyUsageRecord {
24
+ /** Provider identifier */
25
+ provider: ProviderId;
26
+ /** Type of resource (e.g., 'actions_minutes', 'api_tokens') */
27
+ resourceType: string;
28
+ /** Optional specific resource name */
29
+ resourceName?: string;
30
+ /** Numeric usage value */
31
+ usageValue: number;
32
+ /** Unit of measurement */
33
+ usageUnit: string;
34
+ /** Estimated cost in USD */
35
+ costUsd: number;
36
+ /** When this data was collected */
37
+ collectedAt: Date;
38
+ /** Period this usage covers */
39
+ periodStart: Date;
40
+ periodEnd: Date;
41
+ }
42
+
43
+ /**
44
+ * Provider configuration
45
+ */
46
+ export interface ProviderConfig {
47
+ /** Provider identifier */
48
+ id: ProviderId;
49
+ /** Human-readable name */
50
+ name: string;
51
+ /** Whether the provider is currently enabled */
52
+ enabled: boolean;
53
+ /** API key environment variable name */
54
+ apiKeyEnvVar: string;
55
+ /** Pricing model description */
56
+ pricingModel: string;
57
+ /** Documentation URL */
58
+ docsUrl: string;
59
+ }
60
+
61
+ /**
62
+ * Provider interface for fetching usage data
63
+ */
64
+ export interface Provider {
65
+ /** Provider configuration */
66
+ config: ProviderConfig;
67
+
68
+ /**
69
+ * Fetch current usage data from the provider
70
+ * @param apiKey - API key for authentication
71
+ * @param options - Additional fetch options
72
+ * @returns Array of usage records
73
+ */
74
+ fetchUsage(
75
+ apiKey: string,
76
+ options?: {
77
+ startDate?: Date;
78
+ endDate?: Date;
79
+ includeBreakdown?: boolean;
80
+ }
81
+ ): Promise<ThirdPartyUsageRecord[]>;
82
+
83
+ /**
84
+ * Check if the provider credentials are valid
85
+ * @param apiKey - API key to validate
86
+ */
87
+ validateCredentials(apiKey: string): Promise<boolean>;
88
+ }
89
+
90
+ /**
91
+ * Provider configurations
92
+ */
93
+ export const PROVIDER_CONFIGS: Record<ProviderId, ProviderConfig> = {
94
+ github: {
95
+ id: 'github',
96
+ name: 'GitHub',
97
+ enabled: true,
98
+ apiKeyEnvVar: 'GITHUB_TOKEN',
99
+ pricingModel: 'Actions minutes, Advanced Security seats, storage',
100
+ docsUrl: 'https://docs.github.com/en/billing',
101
+ },
102
+ openai: {
103
+ id: 'openai',
104
+ name: 'OpenAI',
105
+ enabled: false,
106
+ apiKeyEnvVar: 'OPENAI_ADMIN_API_KEY',
107
+ pricingModel: 'Per-token pricing for API usage',
108
+ docsUrl: 'https://platform.openai.com/docs/api-reference/usage',
109
+ },
110
+ apify: {
111
+ id: 'apify',
112
+ name: 'Apify',
113
+ enabled: false,
114
+ apiKeyEnvVar: 'APIFY_API_KEY',
115
+ pricingModel: 'Compute units for actor runs',
116
+ docsUrl: 'https://docs.apify.com/platform/billing',
117
+ },
118
+ anthropic: {
119
+ id: 'anthropic',
120
+ name: 'Anthropic',
121
+ enabled: false,
122
+ apiKeyEnvVar: 'ANTHROPIC_ADMIN_API_KEY',
123
+ pricingModel: 'Per-token pricing for Claude API',
124
+ docsUrl: 'https://docs.anthropic.com/en/api/admin-api',
125
+ },
126
+ resend: {
127
+ id: 'resend',
128
+ name: 'Resend',
129
+ enabled: false,
130
+ apiKeyEnvVar: 'RESEND_API_KEY',
131
+ pricingModel: 'Per-email pricing',
132
+ docsUrl: 'https://resend.com/docs/api-reference',
133
+ },
134
+ };
135
+
136
+ // =============================================================================
137
+ // PROVIDER FETCHER STUBS
138
+ // =============================================================================
139
+ // Each stub returns an empty array with a TODO comment.
140
+ // Implement these as integrations are built.
141
+ // =============================================================================
142
+
143
+ /**
144
+ * GitHub Usage Fetcher
145
+ *
146
+ * TODO: Implement GitHub billing API integration
147
+ * - GET /orgs/{org}/settings/billing/actions
148
+ * - GET /orgs/{org}/settings/billing/advanced-security
149
+ * - GET /orgs/{org}/settings/billing/shared-storage
150
+ *
151
+ * @see https://docs.github.com/en/rest/billing
152
+ */
153
+ export async function fetchGitHubUsage(
154
+ _apiKey: string,
155
+ _options?: {
156
+ organization?: string;
157
+ startDate?: Date;
158
+ endDate?: Date;
159
+ }
160
+ ): Promise<ThirdPartyUsageRecord[]> {
161
+ // TODO: Implement GitHub billing API calls
162
+ // 1. Fetch Actions minutes usage
163
+ // 2. Fetch Advanced Security seats
164
+ // 3. Fetch shared storage usage
165
+ // 4. Calculate costs based on GitHub pricing
166
+ console.log('[providers] GitHub usage fetcher not yet implemented');
167
+ return [];
168
+ }
169
+
170
+ /**
171
+ * OpenAI Usage Fetcher
172
+ *
173
+ * TODO: Implement OpenAI Usage API integration
174
+ * - GET /v1/usage (requires admin API key: sk-admin-...)
175
+ *
176
+ * @see https://platform.openai.com/docs/api-reference/usage
177
+ */
178
+ export async function fetchOpenAIUsage(
179
+ _apiKey: string,
180
+ _options?: {
181
+ startDate?: Date;
182
+ endDate?: Date;
183
+ }
184
+ ): Promise<ThirdPartyUsageRecord[]> {
185
+ // TODO: Implement OpenAI Usage API calls
186
+ // 1. Fetch token usage by model
187
+ // 2. Calculate costs based on model pricing
188
+ // 3. Group by date for time-series data
189
+ console.log('[providers] OpenAI usage fetcher not yet implemented');
190
+ return [];
191
+ }
192
+
193
+ /**
194
+ * Apify Usage Fetcher
195
+ *
196
+ * TODO: Implement Apify billing API integration
197
+ * - GET /v2/users/{userId}/usage
198
+ *
199
+ * @see https://docs.apify.com/api/v2#/reference/users/account-usage
200
+ */
201
+ export async function fetchApifyUsage(
202
+ _apiKey: string,
203
+ _options?: {
204
+ startDate?: Date;
205
+ endDate?: Date;
206
+ }
207
+ ): Promise<ThirdPartyUsageRecord[]> {
208
+ // TODO: Implement Apify usage API calls
209
+ // 1. Fetch compute unit usage
210
+ // 2. Fetch actor run counts
211
+ // 3. Calculate costs based on Apify pricing
212
+ console.log('[providers] Apify usage fetcher not yet implemented');
213
+ return [];
214
+ }
215
+
216
+ /**
217
+ * Anthropic Usage Fetcher
218
+ *
219
+ * TODO: Implement Anthropic Admin API integration
220
+ * - GET /v1/organizations/{organization_id}/usage (Admin API)
221
+ *
222
+ * @see https://docs.anthropic.com/en/api/admin-api
223
+ */
224
+ export async function fetchAnthropicUsage(
225
+ _apiKey: string,
226
+ _options?: {
227
+ organizationId?: string;
228
+ startDate?: Date;
229
+ endDate?: Date;
230
+ }
231
+ ): Promise<ThirdPartyUsageRecord[]> {
232
+ // TODO: Implement Anthropic Admin API calls
233
+ // 1. Fetch token usage by model
234
+ // 2. Calculate costs based on Claude pricing
235
+ // 3. Group by date for time-series data
236
+ console.log('[providers] Anthropic usage fetcher not yet implemented');
237
+ return [];
238
+ }
239
+
240
+ /**
241
+ * Resend Usage Fetcher
242
+ *
243
+ * TODO: Implement Resend API integration
244
+ * - No direct usage endpoint; track via webhook or count emails
245
+ *
246
+ * @see https://resend.com/docs/api-reference
247
+ */
248
+ export async function fetchResendUsage(
249
+ _apiKey: string,
250
+ _options?: {
251
+ startDate?: Date;
252
+ endDate?: Date;
253
+ }
254
+ ): Promise<ThirdPartyUsageRecord[]> {
255
+ // TODO: Implement Resend usage tracking
256
+ // 1. Query sent emails count (if available)
257
+ // 2. Or track via our own email logs
258
+ // 3. Calculate costs based on Resend pricing
259
+ console.log('[providers] Resend usage fetcher not yet implemented');
260
+ return [];
261
+ }
262
+
263
+ // =============================================================================
264
+ // AGGREGATION UTILITIES
265
+ // =============================================================================
266
+
267
+ /**
268
+ * Fetch usage from all enabled providers
269
+ */
270
+ export async function fetchAllProviderUsage(
271
+ apiKeys: Partial<Record<ProviderId, string>>,
272
+ options?: {
273
+ startDate?: Date;
274
+ endDate?: Date;
275
+ }
276
+ ): Promise<ThirdPartyUsageRecord[]> {
277
+ const results: ThirdPartyUsageRecord[] = [];
278
+
279
+ // Only fetch from providers that have API keys configured
280
+ const fetchPromises: Promise<ThirdPartyUsageRecord[]>[] = [];
281
+
282
+ if (apiKeys.github) {
283
+ fetchPromises.push(fetchGitHubUsage(apiKeys.github, options));
284
+ }
285
+ if (apiKeys.openai) {
286
+ fetchPromises.push(fetchOpenAIUsage(apiKeys.openai, options));
287
+ }
288
+ if (apiKeys.apify) {
289
+ fetchPromises.push(fetchApifyUsage(apiKeys.apify, options));
290
+ }
291
+ if (apiKeys.anthropic) {
292
+ fetchPromises.push(fetchAnthropicUsage(apiKeys.anthropic, options));
293
+ }
294
+ if (apiKeys.resend) {
295
+ fetchPromises.push(fetchResendUsage(apiKeys.resend, options));
296
+ }
297
+
298
+ // Fetch all in parallel, handle individual failures gracefully
299
+ const settledResults = await Promise.allSettled(fetchPromises);
300
+
301
+ for (const result of settledResults) {
302
+ if (result.status === 'fulfilled') {
303
+ results.push(...result.value);
304
+ } else {
305
+ console.error('[providers] Failed to fetch from provider:', result.reason);
306
+ }
307
+ }
308
+
309
+ return results;
310
+ }
311
+
312
+ /**
313
+ * Calculate total third-party costs from usage records
314
+ */
315
+ export function calculateThirdPartyCosts(records: ThirdPartyUsageRecord[]): {
316
+ byProvider: Record<ProviderId, number>;
317
+ total: number;
318
+ } {
319
+ const byProvider: Partial<Record<ProviderId, number>> = {};
320
+ let total = 0;
321
+
322
+ for (const record of records) {
323
+ byProvider[record.provider] = (byProvider[record.provider] ?? 0) + record.costUsd;
324
+ total += record.costUsd;
325
+ }
326
+
327
+ return {
328
+ byProvider: byProvider as Record<ProviderId, number>,
329
+ total,
330
+ };
331
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * POST /api/patterns/:id/approve - Proxy to pattern-discovery /suggestions/:id?action=approve
3
+ * Approves a pending pattern suggestion
4
+ */
5
+ import type { APIRoute } from 'astro';
6
+
7
+ interface Env {
8
+ PATTERN_DISCOVERY_API?: Fetcher;
9
+ }
10
+
11
+ export const POST: APIRoute = async ({ locals, params, url }) => {
12
+ const api = (locals.runtime?.env as Env)?.PATTERN_DISCOVERY_API;
13
+
14
+ if (!api) {
15
+ return new Response(JSON.stringify({ error: 'Pattern Discovery API not configured' }), {
16
+ status: 503,
17
+ headers: { 'Content-Type': 'application/json' },
18
+ });
19
+ }
20
+
21
+ const { id } = params;
22
+ if (!id) {
23
+ return new Response(JSON.stringify({ error: 'Missing suggestion ID' }), {
24
+ status: 400,
25
+ headers: { 'Content-Type': 'application/json' },
26
+ });
27
+ }
28
+
29
+ try {
30
+ const by = url.searchParams.get('by') || 'dashboard';
31
+
32
+ const response = await api.fetch(
33
+ `https://pattern-discovery.littlebearapps.workers.dev/suggestions/${id}?action=approve&by=${by}`,
34
+ { method: 'POST' }
35
+ );
36
+
37
+ const data = await response.json();
38
+ return new Response(JSON.stringify(data), {
39
+ status: response.status,
40
+ headers: { 'Content-Type': 'application/json' },
41
+ });
42
+ } catch (error) {
43
+ console.error('Error approving suggestion:', error);
44
+ return new Response(JSON.stringify({ error: 'Failed to approve suggestion' }), {
45
+ status: 500,
46
+ headers: { 'Content-Type': 'application/json' },
47
+ });
48
+ }
49
+ };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * POST /api/patterns/:id/reject - Proxy to pattern-discovery /suggestions/:id?action=reject
3
+ * Rejects a pending pattern suggestion
4
+ */
5
+ import type { APIRoute } from 'astro';
6
+
7
+ interface Env {
8
+ PATTERN_DISCOVERY_API?: Fetcher;
9
+ }
10
+
11
+ export const POST: APIRoute = async ({ locals, params, url }) => {
12
+ const api = (locals.runtime?.env as Env)?.PATTERN_DISCOVERY_API;
13
+
14
+ if (!api) {
15
+ return new Response(JSON.stringify({ error: 'Pattern Discovery API not configured' }), {
16
+ status: 503,
17
+ headers: { 'Content-Type': 'application/json' },
18
+ });
19
+ }
20
+
21
+ const { id } = params;
22
+ if (!id) {
23
+ return new Response(JSON.stringify({ error: 'Missing suggestion ID' }), {
24
+ status: 400,
25
+ headers: { 'Content-Type': 'application/json' },
26
+ });
27
+ }
28
+
29
+ try {
30
+ const by = url.searchParams.get('by') || 'dashboard';
31
+ const reason = url.searchParams.get('reason') || 'Rejected via dashboard';
32
+
33
+ const response = await api.fetch(
34
+ `https://pattern-discovery.littlebearapps.workers.dev/suggestions/${id}?action=reject&by=${by}&reason=${encodeURIComponent(reason)}`,
35
+ { method: 'POST' }
36
+ );
37
+
38
+ const data = await response.json();
39
+ return new Response(JSON.stringify(data), {
40
+ status: response.status,
41
+ headers: { 'Content-Type': 'application/json' },
42
+ });
43
+ } catch (error) {
44
+ console.error('Error rejecting suggestion:', error);
45
+ return new Response(JSON.stringify({ error: 'Failed to reject suggestion' }), {
46
+ status: 500,
47
+ headers: { 'Content-Type': 'application/json' },
48
+ });
49
+ }
50
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * GET /api/reports/digests/stats - Proxy to error-collector /digests/stats
3
+ */
4
+ import type { APIRoute } from 'astro';
5
+
6
+ interface Env {
7
+ ERROR_COLLECTOR_API?: Fetcher;
8
+ }
9
+
10
+ export const GET: APIRoute = async ({ locals }) => {
11
+ const api = (locals.runtime?.env as Env)?.ERROR_COLLECTOR_API;
12
+
13
+ if (!api) {
14
+ return new Response(JSON.stringify({ error: 'Error Collector API not configured' }), {
15
+ status: 503,
16
+ headers: { 'Content-Type': 'application/json' },
17
+ });
18
+ }
19
+
20
+ try {
21
+ const response = await api.fetch(
22
+ 'https://error-collector.littlebearapps.workers.dev/digests/stats',
23
+ { method: 'GET' }
24
+ );
25
+
26
+ const data = await response.json();
27
+ return new Response(JSON.stringify(data), {
28
+ status: response.status,
29
+ headers: { 'Content-Type': 'application/json' },
30
+ });
31
+ } catch (error) {
32
+ console.error('Error fetching digest stats:', error);
33
+ return new Response(JSON.stringify({ error: 'Failed to fetch digest stats' }), {
34
+ status: 500,
35
+ headers: { 'Content-Type': 'application/json' },
36
+ });
37
+ }
38
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * GET /api/reports/digests - Proxy to error-collector /digests
3
+ */
4
+ import type { APIRoute } from 'astro';
5
+
6
+ interface Env {
7
+ ERROR_COLLECTOR_API?: Fetcher;
8
+ }
9
+
10
+ export const GET: APIRoute = async ({ request, locals }) => {
11
+ const api = (locals.runtime?.env as Env)?.ERROR_COLLECTOR_API;
12
+
13
+ if (!api) {
14
+ return new Response(JSON.stringify({ error: 'Error Collector API not configured' }), {
15
+ status: 503,
16
+ headers: { 'Content-Type': 'application/json' },
17
+ });
18
+ }
19
+
20
+ try {
21
+ const url = new URL(request.url);
22
+ const response = await api.fetch(
23
+ `https://error-collector.littlebearapps.workers.dev/digests${url.search}`,
24
+ { method: 'GET' }
25
+ );
26
+
27
+ const data = await response.json();
28
+ return new Response(JSON.stringify(data), {
29
+ status: response.status,
30
+ headers: { 'Content-Type': 'application/json' },
31
+ });
32
+ } catch (error) {
33
+ console.error('Error fetching digests:', error);
34
+ return new Response(JSON.stringify({ error: 'Failed to fetch digests' }), {
35
+ status: 500,
36
+ headers: { 'Content-Type': 'application/json' },
37
+ });
38
+ }
39
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Reindex API Proxy
3
+ *
4
+ * Proxies reindex requests to platform-search worker.
5
+ *
6
+ * @module dashboard/pages/api/search/reindex
7
+ * @created 2026-02-03
8
+ * @task task-303.3
9
+ */
10
+
11
+ import type { APIRoute } from 'astro';
12
+
13
+ /**
14
+ * POST /api/search/reindex/:type - Reindex all documents of a content type
15
+ */
16
+ export const POST: APIRoute = async ({ params, locals }) => {
17
+ const api = (locals.runtime?.env as { SEARCH_API?: Fetcher })?.SEARCH_API;
18
+ const { type } = params;
19
+
20
+ if (!api) {
21
+ return new Response(JSON.stringify({ error: 'Search API not available' }), {
22
+ status: 503,
23
+ headers: { 'Content-Type': 'application/json' },
24
+ });
25
+ }
26
+
27
+ if (!type) {
28
+ return new Response(JSON.stringify({ error: 'Content type required' }), {
29
+ status: 400,
30
+ headers: { 'Content-Type': 'application/json' },
31
+ });
32
+ }
33
+
34
+ try {
35
+ const response = await api.fetch(
36
+ new Request(
37
+ `https://platform-search.littlebearapps.workers.dev/search/reindex/${encodeURIComponent(type)}`,
38
+ {
39
+ method: 'POST',
40
+ }
41
+ )
42
+ );
43
+
44
+ const data = await response.json();
45
+ return new Response(JSON.stringify(data), {
46
+ status: response.status,
47
+ headers: { 'Content-Type': 'application/json' },
48
+ });
49
+ } catch (error) {
50
+ console.error('Reindex error:', error);
51
+ return new Response(JSON.stringify({ error: 'Failed to reindex content type' }), {
52
+ status: 500,
53
+ headers: { 'Content-Type': 'application/json' },
54
+ });
55
+ }
56
+ };
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Test Reports API Endpoint
3
+ *
4
+ * Serves HTML test reports from KV storage
5
+ * Reports are stored by platform-ingest-tester worker
6
+ * TTL: 7 days (passes) or 90 days (failures)
7
+ */
8
+
9
+ export const GET = async ({ params, locals }: any) => {
10
+ const { id: runId } = params;
11
+ const env = locals.runtime.env;
12
+
13
+ if (!runId) {
14
+ return new Response(
15
+ JSON.stringify({
16
+ error: 'Missing run ID',
17
+ message: 'Run ID is required',
18
+ }),
19
+ {
20
+ status: 400,
21
+ headers: { 'Content-Type': 'application/json' },
22
+ }
23
+ );
24
+ }
25
+
26
+ try {
27
+ // First, get the run from D1 to find the KV key
28
+ const run = await env.PLATFORM_DB.prepare(
29
+ `SELECT id, project, kv_report_key, timestamp, pass_rate
30
+ FROM runs
31
+ WHERE id = ?`
32
+ )
33
+ .bind(runId)
34
+ .first();
35
+
36
+ if (!run) {
37
+ return new Response(
38
+ JSON.stringify({
39
+ error: 'Run not found',
40
+ message: `No test run found with ID: ${runId}`,
41
+ }),
42
+ {
43
+ status: 404,
44
+ headers: { 'Content-Type': 'application/json' },
45
+ }
46
+ );
47
+ }
48
+
49
+ if (!run.kv_report_key) {
50
+ return new Response(
51
+ JSON.stringify({
52
+ error: 'Report not available',
53
+ message: 'HTML report was not uploaded for this run',
54
+ }),
55
+ {
56
+ status: 404,
57
+ headers: { 'Content-Type': 'application/json' },
58
+ }
59
+ );
60
+ }
61
+
62
+ // Fetch HTML report from KV
63
+ const htmlReport = await env.PLATFORM_CACHE.get(run.kv_report_key);
64
+
65
+ if (!htmlReport) {
66
+ return new Response(
67
+ JSON.stringify({
68
+ error: 'Report expired',
69
+ message: 'HTML report has expired or been deleted (TTL: 7d for passes, 90d for failures)',
70
+ }),
71
+ {
72
+ status: 410, // Gone
73
+ headers: { 'Content-Type': 'application/json' },
74
+ }
75
+ );
76
+ }
77
+
78
+ // Return HTML report
79
+ return new Response(htmlReport, {
80
+ headers: {
81
+ 'Content-Type': 'text/html; charset=utf-8',
82
+ 'Cache-Control': 'public, max-age=3600', // Cache for 1 hour
83
+ 'X-Run-ID': runId,
84
+ 'X-Project': run.project as string,
85
+ 'X-Timestamp': run.timestamp as string,
86
+ },
87
+ });
88
+ } catch (error: any) {
89
+ console.error('Failed to fetch test report:', error);
90
+
91
+ return new Response(
92
+ JSON.stringify({
93
+ error: 'Internal server error',
94
+ message: error.message,
95
+ }),
96
+ {
97
+ status: 500,
98
+ headers: { 'Content-Type': 'application/json' },
99
+ }
100
+ );
101
+ }
102
+ };