outcome-cli 1.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/README.md +261 -0
- package/package.json +95 -0
- package/src/agents/README.md +139 -0
- package/src/agents/adapters/anthropic.adapter.ts +166 -0
- package/src/agents/adapters/dalle.adapter.ts +145 -0
- package/src/agents/adapters/gemini.adapter.ts +134 -0
- package/src/agents/adapters/imagen.adapter.ts +106 -0
- package/src/agents/adapters/nano-banana.adapter.ts +129 -0
- package/src/agents/adapters/openai.adapter.ts +165 -0
- package/src/agents/adapters/veo.adapter.ts +130 -0
- package/src/agents/agent.schema.property.test.ts +379 -0
- package/src/agents/agent.schema.test.ts +148 -0
- package/src/agents/agent.schema.ts +263 -0
- package/src/agents/index.ts +60 -0
- package/src/agents/registered-agent.schema.ts +356 -0
- package/src/agents/registry.ts +97 -0
- package/src/agents/tournament-configs.property.test.ts +266 -0
- package/src/cli/README.md +145 -0
- package/src/cli/commands/define.ts +79 -0
- package/src/cli/commands/list.ts +46 -0
- package/src/cli/commands/logs.ts +83 -0
- package/src/cli/commands/run.ts +416 -0
- package/src/cli/commands/verify.ts +110 -0
- package/src/cli/index.ts +81 -0
- package/src/config/README.md +128 -0
- package/src/config/env.ts +262 -0
- package/src/config/index.ts +19 -0
- package/src/eval/README.md +318 -0
- package/src/eval/ai-judge.test.ts +435 -0
- package/src/eval/ai-judge.ts +368 -0
- package/src/eval/code-validators.ts +414 -0
- package/src/eval/evaluateOutcome.property.test.ts +1174 -0
- package/src/eval/evaluateOutcome.ts +591 -0
- package/src/eval/immigration-validators.ts +122 -0
- package/src/eval/index.ts +90 -0
- package/src/eval/judge-cache.ts +402 -0
- package/src/eval/tournament-validators.property.test.ts +439 -0
- package/src/eval/validators.property.test.ts +1118 -0
- package/src/eval/validators.ts +1199 -0
- package/src/eval/weighted-scorer.ts +285 -0
- package/src/index.ts +17 -0
- package/src/league/README.md +188 -0
- package/src/league/health-check.ts +353 -0
- package/src/league/index.ts +93 -0
- package/src/league/killAgent.ts +151 -0
- package/src/league/league.test.ts +1151 -0
- package/src/league/runLeague.ts +843 -0
- package/src/league/scoreAgent.ts +175 -0
- package/src/modules/omnibridge/__tests__/.gitkeep +1 -0
- package/src/modules/omnibridge/__tests__/auth-tunnel.property.test.ts +524 -0
- package/src/modules/omnibridge/__tests__/deterministic-logger.property.test.ts +965 -0
- package/src/modules/omnibridge/__tests__/ghost-api.property.test.ts +461 -0
- package/src/modules/omnibridge/__tests__/omnibridge-integration.test.ts +542 -0
- package/src/modules/omnibridge/__tests__/parallel-executor.property.test.ts +671 -0
- package/src/modules/omnibridge/__tests__/semantic-normalizer.property.test.ts +521 -0
- package/src/modules/omnibridge/__tests__/semantic-normalizer.test.ts +254 -0
- package/src/modules/omnibridge/__tests__/session-vault.property.test.ts +367 -0
- package/src/modules/omnibridge/__tests__/shadow-session.property.test.ts +523 -0
- package/src/modules/omnibridge/__tests__/triangulation-engine.property.test.ts +292 -0
- package/src/modules/omnibridge/__tests__/verification-engine.property.test.ts +769 -0
- package/src/modules/omnibridge/api/.gitkeep +1 -0
- package/src/modules/omnibridge/api/ghost-api.ts +1087 -0
- package/src/modules/omnibridge/auth/.gitkeep +1 -0
- package/src/modules/omnibridge/auth/auth-tunnel.ts +843 -0
- package/src/modules/omnibridge/auth/session-vault.ts +577 -0
- package/src/modules/omnibridge/core/.gitkeep +1 -0
- package/src/modules/omnibridge/core/semantic-normalizer.ts +702 -0
- package/src/modules/omnibridge/core/triangulation-engine.ts +530 -0
- package/src/modules/omnibridge/core/types.ts +610 -0
- package/src/modules/omnibridge/execution/.gitkeep +1 -0
- package/src/modules/omnibridge/execution/deterministic-logger.ts +629 -0
- package/src/modules/omnibridge/execution/parallel-executor.ts +542 -0
- package/src/modules/omnibridge/execution/shadow-session.ts +794 -0
- package/src/modules/omnibridge/index.ts +212 -0
- package/src/modules/omnibridge/omnibridge.ts +510 -0
- package/src/modules/omnibridge/verification/.gitkeep +1 -0
- package/src/modules/omnibridge/verification/verification-engine.ts +783 -0
- package/src/outcomes/README.md +75 -0
- package/src/outcomes/acquire-pilot-customer.ts +297 -0
- package/src/outcomes/code-delivery-outcomes.ts +89 -0
- package/src/outcomes/code-outcomes.ts +256 -0
- package/src/outcomes/code_review_battle.test.ts +135 -0
- package/src/outcomes/code_review_battle.ts +135 -0
- package/src/outcomes/cold_email_battle.ts +97 -0
- package/src/outcomes/content_creation_battle.ts +160 -0
- package/src/outcomes/f1_stem_opt_compliance.ts +61 -0
- package/src/outcomes/index.ts +107 -0
- package/src/outcomes/lead_gen_battle.test.ts +113 -0
- package/src/outcomes/lead_gen_battle.ts +99 -0
- package/src/outcomes/outcome.schema.property.test.ts +229 -0
- package/src/outcomes/outcome.schema.ts +187 -0
- package/src/outcomes/qualified_sales_interest.ts +118 -0
- package/src/outcomes/swarm_planner.property.test.ts +370 -0
- package/src/outcomes/swarm_planner.ts +96 -0
- package/src/outcomes/web_extraction.ts +234 -0
- package/src/runtime/README.md +220 -0
- package/src/runtime/agentRunner.test.ts +341 -0
- package/src/runtime/agentRunner.ts +746 -0
- package/src/runtime/claudeAdapter.ts +232 -0
- package/src/runtime/costTracker.ts +123 -0
- package/src/runtime/index.ts +34 -0
- package/src/runtime/modelAdapter.property.test.ts +305 -0
- package/src/runtime/modelAdapter.ts +144 -0
- package/src/runtime/openaiAdapter.ts +235 -0
- package/src/utils/README.md +122 -0
- package/src/utils/command-runner.ts +134 -0
- package/src/utils/cost-guard.ts +379 -0
- package/src/utils/errors.test.ts +290 -0
- package/src/utils/errors.ts +442 -0
- package/src/utils/index.ts +37 -0
- package/src/utils/logger.test.ts +361 -0
- package/src/utils/logger.ts +419 -0
- package/src/utils/output-parsers.ts +216 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Configuration Module
|
|
2
|
+
|
|
3
|
+
This module provides type-safe environment configuration with validation.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The configuration system:
|
|
8
|
+
- Validates all required environment variables on startup
|
|
9
|
+
- Fails fast in production if required API keys are missing
|
|
10
|
+
- Allows missing keys in development for easier local setup
|
|
11
|
+
- Provides type-safe access to configuration values
|
|
12
|
+
|
|
13
|
+
## Required API Keys (Production)
|
|
14
|
+
|
|
15
|
+
The following API keys **must** be set for production deployment:
|
|
16
|
+
|
|
17
|
+
| Key | Description | Get it at |
|
|
18
|
+
|-----|-------------|-----------|
|
|
19
|
+
| `ANTHROPIC_API_KEY` | Anthropic Claude API | https://console.anthropic.com/ |
|
|
20
|
+
| `OPENAI_API_KEY` | OpenAI API | https://platform.openai.com/api-keys |
|
|
21
|
+
| `GOOGLE_API_KEY` | Google Gemini API | https://makersuite.google.com/app/apikey |
|
|
22
|
+
| `TREMENDOUS_API_KEY` | Tremendous (fiat payouts) | https://www.tremendous.com |
|
|
23
|
+
| `CIRCLE_API_KEY` | Circle USDC (crypto payouts) | https://console.circle.com |
|
|
24
|
+
| `CIRCLE_ENTITY_SECRET` | Circle entity secret | Run `npx @circle-fin/usdckit register-entity-secret` |
|
|
25
|
+
| `CIRCLE_TREASURY_ADDRESS` | Treasury wallet address | Run `npx tsx scripts/setup-circle-treasury.ts create` |
|
|
26
|
+
| `NEXT_PUBLIC_SUPABASE_URL` | Supabase project URL | https://supabase.com/dashboard |
|
|
27
|
+
| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Supabase anon key | https://supabase.com/dashboard |
|
|
28
|
+
| `SUPABASE_SERVICE_ROLE_KEY` | Supabase service role key | https://supabase.com/dashboard |
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { getEnvConfig, requireApiKey, hasApiKey, printEnvStatus } from './config';
|
|
34
|
+
|
|
35
|
+
// Get full config
|
|
36
|
+
const config = getEnvConfig();
|
|
37
|
+
|
|
38
|
+
// Check if a key is configured
|
|
39
|
+
if (hasApiKey('ANTHROPIC_API_KEY')) {
|
|
40
|
+
// Use Anthropic
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Get a required key (throws if missing)
|
|
44
|
+
const apiKey = requireApiKey('OPENAI_API_KEY');
|
|
45
|
+
|
|
46
|
+
// Print status for debugging
|
|
47
|
+
printEnvStatus();
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Local Development
|
|
51
|
+
|
|
52
|
+
1. Copy `.env.example` to `.env`:
|
|
53
|
+
```bash
|
|
54
|
+
cp .env.example .env
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
2. Fill in the API keys you need for development
|
|
58
|
+
|
|
59
|
+
3. Missing keys are allowed in development mode
|
|
60
|
+
|
|
61
|
+
## Cloudflare Workers Deployment
|
|
62
|
+
|
|
63
|
+
For Cloudflare Workers, use `wrangler secret` to set secrets:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Set secrets for default environment
|
|
67
|
+
wrangler secret put ANTHROPIC_API_KEY
|
|
68
|
+
wrangler secret put OPENAI_API_KEY
|
|
69
|
+
wrangler secret put GOOGLE_API_KEY
|
|
70
|
+
wrangler secret put TREMENDOUS_API_KEY
|
|
71
|
+
wrangler secret put CIRCLE_API_KEY
|
|
72
|
+
wrangler secret put CIRCLE_ENTITY_SECRET
|
|
73
|
+
wrangler secret put SUPABASE_SERVICE_ROLE_KEY
|
|
74
|
+
|
|
75
|
+
# Set secrets for staging
|
|
76
|
+
wrangler secret put ANTHROPIC_API_KEY --env staging
|
|
77
|
+
# ... repeat for all secrets
|
|
78
|
+
|
|
79
|
+
# Set secrets for production
|
|
80
|
+
wrangler secret put ANTHROPIC_API_KEY --env production
|
|
81
|
+
# ... repeat for all secrets
|
|
82
|
+
|
|
83
|
+
# List secrets
|
|
84
|
+
wrangler secret list
|
|
85
|
+
wrangler secret list --env production
|
|
86
|
+
|
|
87
|
+
# Delete a secret
|
|
88
|
+
wrangler secret delete SECRET_NAME
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Environment Modes
|
|
92
|
+
|
|
93
|
+
| Mode | Behavior |
|
|
94
|
+
|------|----------|
|
|
95
|
+
| `development` | Missing keys allowed, uses sandbox/testnet |
|
|
96
|
+
| `staging` | Missing keys allowed, uses sandbox/testnet |
|
|
97
|
+
| `production` | All required keys must be set, uses production APIs |
|
|
98
|
+
|
|
99
|
+
## Payout Provider Modes
|
|
100
|
+
|
|
101
|
+
Control sandbox/production mode for payout providers:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Sandbox mode (default in development)
|
|
105
|
+
TREMENDOUS_SANDBOX=true
|
|
106
|
+
CIRCLE_TESTNET=true
|
|
107
|
+
|
|
108
|
+
# Production mode
|
|
109
|
+
TREMENDOUS_SANDBOX=false
|
|
110
|
+
CIRCLE_TESTNET=false
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Validation
|
|
114
|
+
|
|
115
|
+
The system validates environment variables on startup:
|
|
116
|
+
|
|
117
|
+
- **Production**: Fails with clear error message listing missing keys
|
|
118
|
+
- **Development**: Logs warnings but continues
|
|
119
|
+
|
|
120
|
+
Example error in production:
|
|
121
|
+
```
|
|
122
|
+
Environment validation failed in production mode.
|
|
123
|
+
Missing required API keys:
|
|
124
|
+
- ANTHROPIC_API_KEY
|
|
125
|
+
- TREMENDOUS_API_KEY
|
|
126
|
+
|
|
127
|
+
Set these environment variables or use Cloudflare Workers secrets.
|
|
128
|
+
```
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Configuration Module
|
|
3
|
+
*
|
|
4
|
+
* Validates and provides type-safe access to environment variables.
|
|
5
|
+
* Fails fast with clear error messages when required variables are missing.
|
|
6
|
+
*
|
|
7
|
+
* Requirements: 9.1, 9.3, 9.4
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Schema for required environment variables in production
|
|
14
|
+
*/
|
|
15
|
+
const productionRequiredSchema = z.object({
|
|
16
|
+
// AI Model Providers
|
|
17
|
+
ANTHROPIC_API_KEY: z.string().min(1, 'ANTHROPIC_API_KEY is required'),
|
|
18
|
+
OPENAI_API_KEY: z.string().min(1, 'OPENAI_API_KEY is required'),
|
|
19
|
+
GOOGLE_API_KEY: z.string().min(1, 'GOOGLE_API_KEY is required'),
|
|
20
|
+
|
|
21
|
+
// Payout Providers
|
|
22
|
+
TREMENDOUS_API_KEY: z.string().min(1, 'TREMENDOUS_API_KEY is required'),
|
|
23
|
+
CIRCLE_API_KEY: z.string().min(1, 'CIRCLE_API_KEY is required'),
|
|
24
|
+
CIRCLE_ENTITY_SECRET: z.string().min(1, 'CIRCLE_ENTITY_SECRET is required'),
|
|
25
|
+
|
|
26
|
+
// Authentication (Supabase)
|
|
27
|
+
NEXT_PUBLIC_SUPABASE_URL: z.string().min(1, 'NEXT_PUBLIC_SUPABASE_URL is required'),
|
|
28
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1, 'NEXT_PUBLIC_SUPABASE_ANON_KEY is required'),
|
|
29
|
+
SUPABASE_SERVICE_ROLE_KEY: z.string().min(1, 'SUPABASE_SERVICE_ROLE_KEY is required'),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Schema for optional environment variables
|
|
34
|
+
*/
|
|
35
|
+
const optionalSchema = z.object({
|
|
36
|
+
// Environment mode
|
|
37
|
+
NODE_ENV: z.enum(['development', 'staging', 'production']).default('development'),
|
|
38
|
+
ENVIRONMENT: z.enum(['development', 'staging', 'production']).default('development'),
|
|
39
|
+
|
|
40
|
+
// Payout provider modes
|
|
41
|
+
TREMENDOUS_SANDBOX: z.string().transform(v => v === 'true').default('true'),
|
|
42
|
+
CIRCLE_TESTNET: z.string().transform(v => v === 'true').default('true'),
|
|
43
|
+
|
|
44
|
+
// Email (SendGrid)
|
|
45
|
+
SENDGRID_API_KEY: z.string().optional(),
|
|
46
|
+
SENDGRID_FROM_EMAIL: z.string().email().optional(),
|
|
47
|
+
SENDGRID_FROM_NAME: z.string().optional(),
|
|
48
|
+
|
|
49
|
+
// Notion Integration
|
|
50
|
+
NOTION_API_KEY: z.string().optional(),
|
|
51
|
+
NOTION_BRIEFINGS_DB: z.string().optional(),
|
|
52
|
+
NOTION_ACTIVITY_DB: z.string().optional(),
|
|
53
|
+
NOTION_SPONSORS_DB: z.string().optional(),
|
|
54
|
+
NOTION_TOURNAMENTS_DB: z.string().optional(),
|
|
55
|
+
NOTION_FEEDBACK_DB: z.string().optional(),
|
|
56
|
+
NOTION_CONTENT_DB: z.string().optional(),
|
|
57
|
+
|
|
58
|
+
// Google Integration
|
|
59
|
+
GOOGLE_SERVICE_ACCOUNT_PATH: z.string().optional(),
|
|
60
|
+
GOOGLE_METRICS_SHEET: z.string().optional(),
|
|
61
|
+
GOOGLE_SPONSORS_SHEET: z.string().optional(),
|
|
62
|
+
GOOGLE_TOURNAMENTS_SHEET: z.string().optional(),
|
|
63
|
+
GOOGLE_USERS_SHEET: z.string().optional(),
|
|
64
|
+
GOOGLE_CONTENT_SHEET: z.string().optional(),
|
|
65
|
+
|
|
66
|
+
// Slack Integration
|
|
67
|
+
SLACK_BOT_TOKEN: z.string().optional(),
|
|
68
|
+
SLACK_ALERTS_CHANNEL: z.string().optional(),
|
|
69
|
+
SLACK_BRIEFINGS_CHANNEL: z.string().optional(),
|
|
70
|
+
SLACK_TOURNAMENTS_CHANNEL: z.string().optional(),
|
|
71
|
+
SLACK_SPONSORS_CHANNEL: z.string().optional(),
|
|
72
|
+
SLACK_SUPPORT_CHANNEL: z.string().optional(),
|
|
73
|
+
SLACK_MARKETING_CHANNEL: z.string().optional(),
|
|
74
|
+
|
|
75
|
+
// Discord Bot
|
|
76
|
+
DISCORD_BOT_TOKEN: z.string().optional(),
|
|
77
|
+
DISCORD_GUILD_ID: z.string().optional(),
|
|
78
|
+
|
|
79
|
+
// Local Models (Ollama)
|
|
80
|
+
OLLAMA_BASE_URL: z.string().url().default('http://localhost:11434'),
|
|
81
|
+
|
|
82
|
+
// Infrastructure
|
|
83
|
+
MAX_AGENTS_PER_LEAGUE: z.string().transform(Number).default('10'),
|
|
84
|
+
GLOBAL_SPEND_CEILING: z.string().transform(Number).default('100000'),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Development schema - all required fields are optional
|
|
89
|
+
*/
|
|
90
|
+
const developmentSchema = z.object({
|
|
91
|
+
ANTHROPIC_API_KEY: z.string().optional(),
|
|
92
|
+
OPENAI_API_KEY: z.string().optional(),
|
|
93
|
+
GOOGLE_API_KEY: z.string().optional(),
|
|
94
|
+
TREMENDOUS_API_KEY: z.string().optional(),
|
|
95
|
+
CIRCLE_API_KEY: z.string().optional(),
|
|
96
|
+
CIRCLE_ENTITY_SECRET: z.string().optional(),
|
|
97
|
+
NEXT_PUBLIC_SUPABASE_URL: z.string().optional(),
|
|
98
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().optional(),
|
|
99
|
+
SUPABASE_SERVICE_ROLE_KEY: z.string().optional(),
|
|
100
|
+
}).merge(optionalSchema);
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Full environment schema combining required and optional
|
|
104
|
+
*/
|
|
105
|
+
const fullSchema = productionRequiredSchema.merge(optionalSchema);
|
|
106
|
+
|
|
107
|
+
export type EnvConfig = z.infer<typeof fullSchema>;
|
|
108
|
+
export type DevEnvConfig = z.infer<typeof developmentSchema>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Validation error with details about missing keys
|
|
112
|
+
*/
|
|
113
|
+
export class EnvValidationError extends Error {
|
|
114
|
+
constructor(
|
|
115
|
+
message: string,
|
|
116
|
+
public readonly missingKeys: string[],
|
|
117
|
+
public readonly details: z.ZodError
|
|
118
|
+
) {
|
|
119
|
+
super(message);
|
|
120
|
+
this.name = 'EnvValidationError';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Determines if we're in production mode
|
|
127
|
+
*/
|
|
128
|
+
function isProduction(): boolean {
|
|
129
|
+
const env = process.env.NODE_ENV || process.env.ENVIRONMENT || 'development';
|
|
130
|
+
return env === 'production';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Validates environment variables based on current mode.
|
|
135
|
+
* In production, all required API keys must be set.
|
|
136
|
+
* In development/staging, missing keys are allowed.
|
|
137
|
+
*
|
|
138
|
+
* @throws EnvValidationError if validation fails in production
|
|
139
|
+
*/
|
|
140
|
+
export function validateEnv(): EnvConfig | DevEnvConfig {
|
|
141
|
+
const isProd = isProduction();
|
|
142
|
+
|
|
143
|
+
if (isProd) {
|
|
144
|
+
const result = fullSchema.safeParse(process.env);
|
|
145
|
+
|
|
146
|
+
if (!result.success) {
|
|
147
|
+
const missingKeys = result.error.issues
|
|
148
|
+
.filter(issue => issue.code === 'invalid_type' || issue.code === 'too_small')
|
|
149
|
+
.map(issue => issue.path.join('.'));
|
|
150
|
+
|
|
151
|
+
const errorMessage = [
|
|
152
|
+
'Environment validation failed in production mode.',
|
|
153
|
+
'Missing required API keys:',
|
|
154
|
+
...missingKeys.map(key => ` - ${key}`),
|
|
155
|
+
'',
|
|
156
|
+
'Set these environment variables or use Cloudflare Workers secrets.',
|
|
157
|
+
].join('\n');
|
|
158
|
+
|
|
159
|
+
throw new EnvValidationError(errorMessage, missingKeys, result.error);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return result.data;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Development mode - use relaxed schema
|
|
166
|
+
const result = developmentSchema.safeParse(process.env);
|
|
167
|
+
|
|
168
|
+
if (!result.success) {
|
|
169
|
+
// Log warnings but don't fail in development
|
|
170
|
+
console.warn('[env] Some environment variables are invalid:', result.error.format());
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return result.data ?? ({} as DevEnvConfig);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Gets a validated environment configuration.
|
|
178
|
+
* Caches the result after first validation.
|
|
179
|
+
*/
|
|
180
|
+
let cachedConfig: EnvConfig | DevEnvConfig | null = null;
|
|
181
|
+
|
|
182
|
+
export function getEnvConfig(): EnvConfig | DevEnvConfig {
|
|
183
|
+
if (!cachedConfig) {
|
|
184
|
+
cachedConfig = validateEnv();
|
|
185
|
+
}
|
|
186
|
+
return cachedConfig;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Checks if a specific API key is configured
|
|
191
|
+
*/
|
|
192
|
+
export function hasApiKey(key: keyof EnvConfig): boolean {
|
|
193
|
+
const config = getEnvConfig();
|
|
194
|
+
const value = config[key as keyof typeof config];
|
|
195
|
+
return typeof value === 'string' && value.length > 0;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Gets an API key, throwing if not configured
|
|
200
|
+
*/
|
|
201
|
+
export function requireApiKey(key: keyof EnvConfig): string {
|
|
202
|
+
const config = getEnvConfig();
|
|
203
|
+
const value = config[key as keyof typeof config];
|
|
204
|
+
|
|
205
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
206
|
+
throw new Error(`Required API key not configured: ${key}`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return value;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Checks if running in sandbox/testnet mode for payout providers
|
|
214
|
+
*/
|
|
215
|
+
export function isPayoutSandbox(): boolean {
|
|
216
|
+
const config = getEnvConfig();
|
|
217
|
+
return config.TREMENDOUS_SANDBOX === true || config.CIRCLE_TESTNET === true;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Gets the current environment mode
|
|
222
|
+
*/
|
|
223
|
+
export function getEnvironment(): 'development' | 'staging' | 'production' {
|
|
224
|
+
const config = getEnvConfig();
|
|
225
|
+
return config.ENVIRONMENT || config.NODE_ENV || 'development';
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Lists all required API keys for production
|
|
230
|
+
*/
|
|
231
|
+
export const REQUIRED_API_KEYS = [
|
|
232
|
+
'ANTHROPIC_API_KEY',
|
|
233
|
+
'OPENAI_API_KEY',
|
|
234
|
+
'GOOGLE_API_KEY',
|
|
235
|
+
'TREMENDOUS_API_KEY',
|
|
236
|
+
'CIRCLE_API_KEY',
|
|
237
|
+
'CIRCLE_ENTITY_SECRET',
|
|
238
|
+
'NEXT_PUBLIC_SUPABASE_URL',
|
|
239
|
+
'NEXT_PUBLIC_SUPABASE_ANON_KEY',
|
|
240
|
+
'SUPABASE_SERVICE_ROLE_KEY',
|
|
241
|
+
] as const;
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Prints environment status (for debugging)
|
|
245
|
+
*/
|
|
246
|
+
export function printEnvStatus(): void {
|
|
247
|
+
// Ensure config is loaded
|
|
248
|
+
getEnvConfig();
|
|
249
|
+
const env = getEnvironment();
|
|
250
|
+
|
|
251
|
+
console.log(`\n[env] Environment: ${env}`);
|
|
252
|
+
console.log('[env] API Key Status:');
|
|
253
|
+
|
|
254
|
+
for (const key of REQUIRED_API_KEYS) {
|
|
255
|
+
const keyConfigured = hasApiKey(key);
|
|
256
|
+
const status = keyConfigured ? '✓' : '✗';
|
|
257
|
+
console.log(` ${status} ${key}`);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
console.log(`[env] Payout Mode: ${isPayoutSandbox() ? 'Sandbox/Testnet' : 'Production'}`);
|
|
261
|
+
console.log('');
|
|
262
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Module
|
|
3
|
+
*
|
|
4
|
+
* Exports environment configuration utilities.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
validateEnv,
|
|
9
|
+
getEnvConfig,
|
|
10
|
+
hasApiKey,
|
|
11
|
+
requireApiKey,
|
|
12
|
+
isPayoutSandbox,
|
|
13
|
+
getEnvironment,
|
|
14
|
+
printEnvStatus,
|
|
15
|
+
REQUIRED_API_KEYS,
|
|
16
|
+
EnvValidationError,
|
|
17
|
+
type EnvConfig,
|
|
18
|
+
type DevEnvConfig,
|
|
19
|
+
} from './env.js';
|