luxlabs 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.
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Lux CLI - Pricing Commands
3
+ *
4
+ * Manage AWS AppConfig pricing configuration for Lux credits.
5
+ *
6
+ * Commands:
7
+ * lux pricing show - Show current pricing config
8
+ * lux pricing export [file] - Export pricing config to JSON file
9
+ * lux pricing deploy [file] - Deploy pricing config to AWS AppConfig
10
+ * lux pricing set-secrets - Set AWS credentials in Cloudflare Workers
11
+ */
12
+
13
+ const chalk = require('chalk');
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+
17
+ // AppConfig details (from user's setup)
18
+ const APPCONFIG = {
19
+ region: 'us-east-2',
20
+ applicationId: 'wgicy0c',
21
+ environmentId: 'wz6k5o6',
22
+ environmentName: 'development',
23
+ configProfileId: 'gagerlg',
24
+ };
25
+
26
+ // Default pricing configuration (matches lux-studio-api/src/lib/appconfig-pricing.ts)
27
+ const DEFAULT_PRICING_CONFIG = {
28
+ usdToCredits: 100, // $1 = 100 credits
29
+ luxMarkup: 1.2, // 20% markup
30
+
31
+ sessionExpiryMinutes: 15,
32
+ defaultCreditReservation: 20,
33
+ lowBalanceThreshold: 100,
34
+
35
+ models: {
36
+ 'claude-opus-4-5-20250514': {
37
+ input: 15.0,
38
+ output: 75.0,
39
+ cacheRead: 1.50,
40
+ cacheWrite: 18.75,
41
+ },
42
+ 'claude-sonnet-4-5-20250514': {
43
+ input: 3.0,
44
+ output: 15.0,
45
+ cacheRead: 0.30,
46
+ cacheWrite: 3.75,
47
+ },
48
+ 'claude-opus-4-20250514': {
49
+ input: 15.0,
50
+ output: 75.0,
51
+ cacheRead: 1.50,
52
+ cacheWrite: 18.75,
53
+ },
54
+ 'claude-sonnet-4-20250514': {
55
+ input: 3.0,
56
+ output: 15.0,
57
+ cacheRead: 0.30,
58
+ cacheWrite: 3.75,
59
+ },
60
+ 'claude-3-5-sonnet-20241022': {
61
+ input: 3.0,
62
+ output: 15.0,
63
+ cacheRead: 0.30,
64
+ cacheWrite: 3.75,
65
+ },
66
+ 'claude-3-5-sonnet-20240620': {
67
+ input: 3.0,
68
+ output: 15.0,
69
+ cacheRead: 0.30,
70
+ cacheWrite: 3.75,
71
+ },
72
+ 'default': {
73
+ input: 3.0,
74
+ output: 15.0,
75
+ cacheRead: 0.30,
76
+ cacheWrite: 3.75,
77
+ },
78
+ },
79
+
80
+ features: {
81
+ enableCaching: true,
82
+ enableDetailedUsageTracking: true,
83
+ },
84
+
85
+ version: '1.0.0',
86
+ updatedAt: new Date().toISOString(),
87
+ };
88
+
89
+ async function showHelp() {
90
+ console.log(`
91
+ ${chalk.bold('Lux Pricing Commands')}
92
+
93
+ Manage AWS AppConfig pricing configuration for Lux credits.
94
+
95
+ ${chalk.yellow('Usage:')}
96
+ lux pricing <command> [options]
97
+
98
+ ${chalk.yellow('Commands:')}
99
+ show Show current/default pricing configuration
100
+ export [file] Export pricing config to JSON file (default: pricing-config.json)
101
+ deploy <file> Deploy pricing config to AWS AppConfig
102
+ set-secrets Show commands to set AWS credentials in Cloudflare Workers
103
+
104
+ ${chalk.yellow('AppConfig Details:')}
105
+ Region: ${APPCONFIG.region}
106
+ Application: ${APPCONFIG.applicationId}
107
+ Environment: ${APPCONFIG.environmentName} (${APPCONFIG.environmentId})
108
+ Profile: ${APPCONFIG.configProfileId}
109
+
110
+ ${chalk.yellow('Examples:')}
111
+ ${chalk.gray('# View current pricing')}
112
+ lux pricing show
113
+
114
+ ${chalk.gray('# Export default config to edit')}
115
+ lux pricing export ./my-pricing.json
116
+
117
+ ${chalk.gray('# Deploy updated pricing')}
118
+ lux pricing deploy ./my-pricing.json
119
+ `);
120
+ }
121
+
122
+ async function showPricing() {
123
+ console.log(chalk.bold('\nCurrent Pricing Configuration:\n'));
124
+
125
+ console.log(chalk.yellow('Credit Conversion:'));
126
+ console.log(` USD to Credits: ${DEFAULT_PRICING_CONFIG.usdToCredits} credits per $1`);
127
+ console.log(` Markup: ${((DEFAULT_PRICING_CONFIG.luxMarkup - 1) * 100).toFixed(0)}%`);
128
+
129
+ console.log(chalk.yellow('\nSession Settings:'));
130
+ console.log(` Expiry: ${DEFAULT_PRICING_CONFIG.sessionExpiryMinutes} minutes`);
131
+ console.log(` Default Reservation: ${DEFAULT_PRICING_CONFIG.defaultCreditReservation} credits`);
132
+ console.log(` Low Balance Warning: ${DEFAULT_PRICING_CONFIG.lowBalanceThreshold} credits`);
133
+
134
+ console.log(chalk.yellow('\nModel Pricing (per 1M tokens):'));
135
+ for (const [model, pricing] of Object.entries(DEFAULT_PRICING_CONFIG.models)) {
136
+ if (model === 'default') continue;
137
+ console.log(` ${chalk.cyan(model)}:`);
138
+ console.log(` Input: $${pricing.input.toFixed(2)}`);
139
+ console.log(` Output: $${pricing.output.toFixed(2)}`);
140
+ if (pricing.cacheRead) {
141
+ console.log(` Cache Read: $${pricing.cacheRead.toFixed(2)}`);
142
+ console.log(` Cache Write: $${pricing.cacheWrite.toFixed(2)}`);
143
+ }
144
+ }
145
+
146
+ console.log(chalk.yellow('\nDefault Fallback Pricing:'));
147
+ const defaultPricing = DEFAULT_PRICING_CONFIG.models.default;
148
+ console.log(` Input: $${defaultPricing.input.toFixed(2)}`);
149
+ console.log(` Output: $${defaultPricing.output.toFixed(2)}`);
150
+
151
+ console.log(chalk.gray('\n(Deploy via AppConfig to customize these values)'));
152
+ }
153
+
154
+ async function exportConfig(args) {
155
+ const outputFile = args[0] || 'pricing-config.json';
156
+ const outputPath = path.resolve(process.cwd(), outputFile);
157
+
158
+ const config = {
159
+ ...DEFAULT_PRICING_CONFIG,
160
+ version: '1.0.0',
161
+ updatedAt: new Date().toISOString(),
162
+ };
163
+
164
+ fs.writeFileSync(outputPath, JSON.stringify(config, null, 2));
165
+ console.log(chalk.green(`\nPricing configuration exported to: ${outputPath}`));
166
+ console.log(chalk.gray('\nEdit this file and use "lux pricing deploy <file>" to update pricing.'));
167
+ }
168
+
169
+ async function deployConfig(args) {
170
+ if (!args[0]) {
171
+ console.log(chalk.red('Error: Please provide a configuration file path'));
172
+ console.log(chalk.gray('Usage: lux pricing deploy <file>'));
173
+ process.exit(1);
174
+ }
175
+
176
+ const configPath = path.resolve(process.cwd(), args[0]);
177
+
178
+ if (!fs.existsSync(configPath)) {
179
+ console.log(chalk.red(`Error: File not found: ${configPath}`));
180
+ process.exit(1);
181
+ }
182
+
183
+ let config;
184
+ try {
185
+ config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
186
+ } catch (err) {
187
+ console.log(chalk.red('Error: Invalid JSON in configuration file'));
188
+ console.log(chalk.gray(err.message));
189
+ process.exit(1);
190
+ }
191
+
192
+ // Validate configuration
193
+ const requiredFields = ['usdToCredits', 'luxMarkup', 'models'];
194
+ for (const field of requiredFields) {
195
+ if (!(field in config)) {
196
+ console.log(chalk.red(`Error: Missing required field: ${field}`));
197
+ process.exit(1);
198
+ }
199
+ }
200
+
201
+ // Update version and timestamp
202
+ config.version = config.version || '1.0.0';
203
+ config.updatedAt = new Date().toISOString();
204
+
205
+ console.log(chalk.yellow('\nDeploying pricing configuration to AWS AppConfig...\n'));
206
+
207
+ // Check for AWS credentials
208
+ const awsAccessKey = process.env.AWS_ACCESS_KEY_ID;
209
+ const awsSecretKey = process.env.AWS_SECRET_ACCESS_KEY;
210
+
211
+ if (!awsAccessKey || !awsSecretKey) {
212
+ console.log(chalk.red('Error: AWS credentials not found in environment'));
213
+ console.log(chalk.gray('\nSet the following environment variables:'));
214
+ console.log(chalk.gray(' export AWS_ACCESS_KEY_ID="your-access-key"'));
215
+ console.log(chalk.gray(' export AWS_SECRET_ACCESS_KEY="your-secret-key"'));
216
+ process.exit(1);
217
+ }
218
+
219
+ try {
220
+ // Use AWS SDK to deploy
221
+ const { AppConfigClient, CreateHostedConfigurationVersionCommand } = require('@aws-sdk/client-appconfig');
222
+
223
+ const client = new AppConfigClient({
224
+ region: APPCONFIG.region,
225
+ credentials: {
226
+ accessKeyId: awsAccessKey,
227
+ secretAccessKey: awsSecretKey,
228
+ },
229
+ });
230
+
231
+ const command = new CreateHostedConfigurationVersionCommand({
232
+ ApplicationId: APPCONFIG.applicationId,
233
+ ConfigurationProfileId: APPCONFIG.configProfileId,
234
+ Content: Buffer.from(JSON.stringify(config)),
235
+ ContentType: 'application/json',
236
+ Description: `Pricing update - ${config.version}`,
237
+ });
238
+
239
+ const response = await client.send(command);
240
+
241
+ console.log(chalk.green('Pricing configuration deployed successfully!'));
242
+ console.log(chalk.gray(` Version: ${response.VersionNumber}`));
243
+ console.log(chalk.gray(` Application: ${APPCONFIG.applicationId}`));
244
+ console.log(chalk.gray(` Environment: ${APPCONFIG.environmentName}`));
245
+ console.log(chalk.gray(` Profile: ${APPCONFIG.configProfileId}`));
246
+
247
+ console.log(chalk.yellow('\nNext step: Start a deployment in AWS AppConfig console'));
248
+ console.log(chalk.gray('Or use AWS CLI:'));
249
+ console.log(chalk.cyan(` aws appconfig start-deployment \\
250
+ --application-id ${APPCONFIG.applicationId} \\
251
+ --environment-id ${APPCONFIG.environmentId} \\
252
+ --deployment-strategy-id AppConfig.AllAtOnce \\
253
+ --configuration-profile-id ${APPCONFIG.configProfileId} \\
254
+ --configuration-version ${response.VersionNumber} \\
255
+ --region ${APPCONFIG.region}`));
256
+
257
+ } catch (err) {
258
+ if (err.code === 'MODULE_NOT_FOUND') {
259
+ console.log(chalk.red('Error: AWS SDK not installed'));
260
+ console.log(chalk.gray('\nInstall the AWS SDK:'));
261
+ console.log(chalk.gray(' npm install @aws-sdk/client-appconfig'));
262
+ process.exit(1);
263
+ }
264
+
265
+ console.log(chalk.red('Error deploying configuration:'));
266
+ console.log(chalk.gray(err.message));
267
+ process.exit(1);
268
+ }
269
+ }
270
+
271
+ async function setSecrets() {
272
+ console.log(chalk.bold('\nSet AWS credentials in Cloudflare Workers\n'));
273
+
274
+ console.log(chalk.yellow('Run these commands in the lux-studio-api directory:\n'));
275
+
276
+ const secrets = [
277
+ { name: 'AWS_ACCESS_KEY_ID', desc: 'IAM user access key' },
278
+ { name: 'AWS_SECRET_ACCESS_KEY', desc: 'IAM user secret key' },
279
+ ];
280
+
281
+ for (const secret of secrets) {
282
+ console.log(chalk.gray(`# ${secret.desc}`));
283
+ console.log(chalk.cyan(`npx wrangler secret put ${secret.name}`));
284
+ console.log();
285
+ }
286
+
287
+ console.log(chalk.yellow('Optional (defaults are already set in code):'));
288
+ console.log(chalk.gray(`# Region: ${APPCONFIG.region}`));
289
+ console.log(chalk.cyan(`echo "${APPCONFIG.region}" | npx wrangler secret put AWS_REGION`));
290
+ console.log();
291
+ console.log(chalk.gray(`# Application ID: ${APPCONFIG.applicationId}`));
292
+ console.log(chalk.cyan(`echo "${APPCONFIG.applicationId}" | npx wrangler secret put AWS_APPCONFIG_APPLICATION_ID`));
293
+ console.log();
294
+ console.log(chalk.gray(`# Environment ID: ${APPCONFIG.environmentId}`));
295
+ console.log(chalk.cyan(`echo "${APPCONFIG.environmentId}" | npx wrangler secret put AWS_APPCONFIG_ENVIRONMENT_ID`));
296
+ console.log();
297
+ console.log(chalk.gray(`# Profile ID: ${APPCONFIG.configProfileId}`));
298
+ console.log(chalk.cyan(`echo "${APPCONFIG.configProfileId}" | npx wrangler secret put AWS_APPCONFIG_CONFIG_PROFILE_ID`));
299
+ }
300
+
301
+ async function handlePricing(args) {
302
+ const [subcommand, ...rest] = args;
303
+
304
+ switch (subcommand) {
305
+ case 'show':
306
+ await showPricing();
307
+ break;
308
+ case 'export':
309
+ await exportConfig(rest);
310
+ break;
311
+ case 'deploy':
312
+ await deployConfig(rest);
313
+ break;
314
+ case 'set-secrets':
315
+ await setSecrets();
316
+ break;
317
+ case 'help':
318
+ case undefined:
319
+ await showHelp();
320
+ break;
321
+ default:
322
+ console.log(chalk.red(`Unknown command: ${subcommand}`));
323
+ await showHelp();
324
+ process.exit(1);
325
+ }
326
+ }
327
+
328
+ module.exports = { handlePricing };