diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 293b549..4177439 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,7 +32,7 @@ importers: version: 1.13.2 axios: specifier: ^1.3.4 - version: 1.12.2 + version: 1.13.5 boxen: specifier: 5.1.2 version: 5.1.2 @@ -406,28 +406,24 @@ packages: engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - libc: [musl] '@biomejs/cli-linux-arm64@1.9.4': resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - libc: [glibc] '@biomejs/cli-linux-x64-musl@1.9.4': resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - libc: [musl] '@biomejs/cli-linux-x64@1.9.4': resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - libc: [glibc] '@biomejs/cli-win32-arm64@1.9.4': resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==} @@ -868,67 +864,56 @@ packages: resolution: {integrity: sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.53.2': resolution: {integrity: sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.53.2': resolution: {integrity: sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.53.2': resolution: {integrity: sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.53.2': resolution: {integrity: sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.53.2': resolution: {integrity: sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.53.2': resolution: {integrity: sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.53.2': resolution: {integrity: sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.53.2': resolution: {integrity: sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.53.2': resolution: {integrity: sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.53.2': resolution: {integrity: sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-openharmony-arm64@4.53.2': resolution: {integrity: sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==} @@ -1447,8 +1432,8 @@ packages: aws4@1.13.2: resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} - axios@1.12.2: - resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==} + axios@1.13.5: + resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==} backo2@1.0.2: resolution: {integrity: sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==} @@ -2077,8 +2062,8 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} - form-data@4.0.4: - resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} forwarded@0.2.0: @@ -2152,12 +2137,12 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me global-dirs@3.0.1: resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} @@ -3759,7 +3744,7 @@ snapshots: '@noble/hashes': 1.8.0 '@web-std/file': 3.0.3 '@web-std/form-data': 3.1.0 - axios: 1.12.2 + axios: 1.13.5 dotenv: 16.6.1 files-from-path: 1.0.0 lodash: 4.17.21 @@ -5378,10 +5363,10 @@ snapshots: aws4@1.13.2: {} - axios@1.12.2: + axios@1.13.5: dependencies: follow-redirects: 1.15.11 - form-data: 4.0.4 + form-data: 4.0.5 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug @@ -6137,7 +6122,7 @@ snapshots: dependencies: is-callable: 1.2.7 - form-data@4.0.4: + form-data@4.0.5: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 diff --git a/src/commands/auth/index.ts b/src/commands/auth/index.ts index 572cea5..5403f5b 100644 --- a/src/commands/auth/index.ts +++ b/src/commands/auth/index.ts @@ -13,11 +13,17 @@ export default (cmd: Command): Command => { .command('login') .description(t('cmdAuthLoginDescription')) .option('-e, --email', 'Login via email verification (no browser required)') - .option('--auth-url ', 'Override auth service URL (e.g., http://localhost:3001)') + .option( + '--auth-url ', + 'Override auth service URL (e.g., http://localhost:3001)', + ) .action((options) => { // If --email flag is provided, use email-based login (no browser needed) if (options.email) { - const authApiUrl = options.authUrl || getDefined('AUTH__API_URL') || 'https://auth.alternatefutures.ai'; + const authApiUrl = + options.authUrl || + getDefined('AUTH__API_URL') || + 'https://auth.alternatefutures.ai'; return emailLoginActionHandler({ authApiUrl }); } @@ -39,7 +45,8 @@ export default (cmd: Command): Command => { .command('signup') .description('Create a new account using email verification') .action(() => { - const authApiUrl = getDefined('AUTH__API_URL') || 'https://auth.alternatefutures.ai'; + const authApiUrl = + getDefined('AUTH__API_URL') || 'https://auth.alternatefutures.ai'; return signupActionHandler({ authApiUrl, diff --git a/src/commands/auth/loginEmail.ts b/src/commands/auth/loginEmail.ts index 827b1be..3b1ec9d 100644 --- a/src/commands/auth/loginEmail.ts +++ b/src/commands/auth/loginEmail.ts @@ -1,4 +1,4 @@ -import * as readline from 'readline'; +import * as readline from 'node:readline'; import { output } from '../../cli'; import { config } from '../../config'; @@ -30,7 +30,10 @@ const createPrompt = (): readline.Interface => { }); }; -const askQuestion = (rl: readline.Interface, question: string): Promise => { +const askQuestion = ( + rl: readline.Interface, + question: string, +): Promise => { return new Promise((resolve) => { rl.question(question, (answer) => { resolve(answer.trim()); @@ -38,7 +41,9 @@ const askQuestion = (rl: readline.Interface, question: string): Promise }); }; -export const emailLoginActionHandler = async ({ authApiUrl }: EmailLoginActionHandlerArgs) => { +export const emailLoginActionHandler = async ({ + authApiUrl, +}: EmailLoginActionHandlerArgs) => { const rl = createPrompt(); try { @@ -78,11 +83,16 @@ export const emailLoginActionHandler = async ({ authApiUrl }: EmailLoginActionHa output.success('Verification code sent to your email'); output.log(''); - output.log(`📬 Check your inbox for a 6-digit code (expires in ${Math.floor((requestData.expiresIn || 300) / 60)} minutes)`); + output.log( + `📬 Check your inbox for a 6-digit code (expires in ${Math.floor((requestData.expiresIn || 300) / 60)} minutes)`, + ); output.log(''); // Step 3: Get verification code - const code = await askQuestion(rl, '🔐 Enter the 6-digit verification code: '); + const code = await askQuestion( + rl, + '🔐 Enter the 6-digit verification code: ', + ); if (!code || code.length !== 6) { output.error('Invalid verification code. Please enter a 6-digit code.'); @@ -117,7 +127,7 @@ export const emailLoginActionHandler = async ({ authApiUrl }: EmailLoginActionHa method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${accessToken}`, + Authorization: `Bearer ${accessToken}`, }, body: JSON.stringify({ name: `CLI Token - ${new Date().toISOString().split('T')[0]}`, @@ -143,7 +153,9 @@ export const emailLoginActionHandler = async ({ authApiUrl }: EmailLoginActionHa output.log(''); output.log(`✅ Logged in as: ${email}`); output.log(''); - output.log('You can now use the AlternateFutures CLI to deploy your projects.'); + output.log( + 'You can now use the AlternateFutures CLI to deploy your projects.', + ); output.printNewLine(); rl.close(); @@ -163,14 +175,20 @@ export const emailLoginActionHandler = async ({ authApiUrl }: EmailLoginActionHa output.log(''); if (fetchError.cause) { - output.log(`Technical details: ${fetchError.cause.message || fetchError.cause}`); + output.log( + `Technical details: ${fetchError.cause.message || fetchError.cause}`, + ); } output.log(''); output.log('Troubleshooting:'); - output.log(` 1. Verify the auth service is running: curl -k ${authApiUrl}/health`); + output.log( + ` 1. Verify the auth service is running: curl -k ${authApiUrl}/health`, + ); output.log(' 2. Check your network connection'); - output.log(' 3. Try setting NODE_TLS_REJECT_UNAUTHORIZED=0 if using self-signed certs'); + output.log( + ' 3. Try setting NODE_TLS_REJECT_UNAUTHORIZED=0 if using self-signed certs', + ); } else { output.error(error.message); if (fetchError.cause) { diff --git a/src/commands/auth/signup.ts b/src/commands/auth/signup.ts index 2c15aff..070bca3 100644 --- a/src/commands/auth/signup.ts +++ b/src/commands/auth/signup.ts @@ -1,4 +1,4 @@ -import * as readline from 'readline'; +import * as readline from 'node:readline'; import { output } from '../../cli'; import { config } from '../../config'; @@ -30,7 +30,10 @@ const createPrompt = (): readline.Interface => { }); }; -const askQuestion = (rl: readline.Interface, question: string): Promise => { +const askQuestion = ( + rl: readline.Interface, + question: string, +): Promise => { return new Promise((resolve) => { rl.question(question, (answer) => { resolve(answer.trim()); @@ -38,7 +41,9 @@ const askQuestion = (rl: readline.Interface, question: string): Promise }); }; -export const signupActionHandler = async ({ authApiUrl }: SignupActionHandlerArgs) => { +export const signupActionHandler = async ({ + authApiUrl, +}: SignupActionHandlerArgs) => { const rl = createPrompt(); try { @@ -76,11 +81,16 @@ export const signupActionHandler = async ({ authApiUrl }: SignupActionHandlerArg output.success('Verification code sent to your email'); output.log(''); - output.log(`📬 Check your inbox for a 6-digit code (expires in ${Math.floor((requestData.expiresIn || 300) / 60)} minutes)`); + output.log( + `📬 Check your inbox for a 6-digit code (expires in ${Math.floor((requestData.expiresIn || 300) / 60)} minutes)`, + ); output.log(''); // Step 3: Get verification code - const code = await askQuestion(rl, '🔐 Enter the 6-digit verification code: '); + const code = await askQuestion( + rl, + '🔐 Enter the 6-digit verification code: ', + ); if (!code || code.length !== 6) { output.error('Invalid verification code. Please enter a 6-digit code.'); @@ -115,7 +125,7 @@ export const signupActionHandler = async ({ authApiUrl }: SignupActionHandlerArg method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${accessToken}`, + Authorization: `Bearer ${accessToken}`, }, body: JSON.stringify({ name: `CLI Token - ${new Date().toISOString().split('T')[0]}`, @@ -141,7 +151,9 @@ export const signupActionHandler = async ({ authApiUrl }: SignupActionHandlerArg output.log(''); output.log(`✅ Logged in as: ${email}`); output.log(''); - output.log('You can now use the AlternateFutures CLI to deploy your projects.'); + output.log( + 'You can now use the AlternateFutures CLI to deploy your projects.', + ); output.printNewLine(); rl.close(); @@ -161,14 +173,20 @@ export const signupActionHandler = async ({ authApiUrl }: SignupActionHandlerArg output.log(''); if (fetchError.cause) { - output.log(`Technical details: ${fetchError.cause.message || fetchError.cause}`); + output.log( + `Technical details: ${fetchError.cause.message || fetchError.cause}`, + ); } output.log(''); output.log('Troubleshooting:'); - output.log(` 1. Verify the auth service is running: curl -k ${authApiUrl}/health`); + output.log( + ` 1. Verify the auth service is running: curl -k ${authApiUrl}/health`, + ); output.log(' 2. Check your network connection'); - output.log(' 3. Try setting NODE_TLS_REJECT_UNAUTHORIZED=0 if using self-signed certs'); + output.log( + ' 3. Try setting NODE_TLS_REJECT_UNAUTHORIZED=0 if using self-signed certs', + ); } else { output.error(error.message); if (fetchError.cause) { diff --git a/src/commands/billing/invoices.ts b/src/commands/billing/invoices.ts index 9e2b4a1..d6adbec 100644 --- a/src/commands/billing/invoices.ts +++ b/src/commands/billing/invoices.ts @@ -41,9 +41,10 @@ const invoicesAction: SdkGuardedFunction = async ({ 'Paid At': invoice.paidAt ? new Date(invoice.paidAt * 1000).toLocaleDateString() : 'N/A', - Period: invoice.periodStart && invoice.periodEnd - ? `${new Date(invoice.periodStart * 1000).toLocaleDateString()} - ${new Date(invoice.periodEnd * 1000).toLocaleDateString()}` - : 'N/A', + Period: + invoice.periodStart && invoice.periodEnd + ? `${new Date(invoice.periodStart * 1000).toLocaleDateString()} - ${new Date(invoice.periodEnd * 1000).toLocaleDateString()}` + : 'N/A', PDF: invoice.pdfUrl || 'N/A', })); diff --git a/src/commands/billing/utils/getBillingClient.ts b/src/commands/billing/utils/getBillingClient.ts index eb7aa24..16f6489 100644 --- a/src/commands/billing/utils/getBillingClient.ts +++ b/src/commands/billing/utils/getBillingClient.ts @@ -16,9 +16,14 @@ export const getBillingClient = (sdk: AnySdk): AnyBillingClient | null => { try { return sdk.billing(); } catch (error) { - if (error instanceof Error && error.message.includes('SDK__AUTH_SERVICE_URL')) { + if ( + error instanceof Error && + error.message.includes('SDK__AUTH_SERVICE_URL') + ) { output.error('Billing requires SDK__AUTH_SERVICE_URL to be configured.'); - output.hint('Add SDK__AUTH_SERVICE_URL to your environment configuration.'); + output.hint( + 'Add SDK__AUTH_SERVICE_URL to your environment configuration.', + ); return null; } throw error; diff --git a/src/commands/observability/index.ts b/src/commands/observability/index.ts index ae1ddef..79fbff7 100644 --- a/src/commands/observability/index.ts +++ b/src/commands/observability/index.ts @@ -10,7 +10,9 @@ export default (program: Command): Command => { const cmd = program .command('observability') .alias('obs') - .description('Query and manage APM observability data (traces, logs, metrics)'); + .description( + 'Query and manage APM observability data (traces, logs, metrics)', + ); cmd .command('traces') @@ -39,7 +41,10 @@ export default (program: Command): Command => { .command('logs') .description('Query logs') .option('--service ', 'Filter by service name') - .option('--severity ', 'Filter by severity (DEBUG, INFO, WARN, ERROR)') + .option( + '--severity ', + 'Filter by severity (DEBUG, INFO, WARN, ERROR)', + ) .option('--search ', 'Search in log body') .option('--hours ', 'Look back N hours (default: 1)', '1') .option('--limit ', 'Maximum number of logs to return', '50') diff --git a/src/commands/observability/logs.ts b/src/commands/observability/logs.ts index e6c14f1..664bb2c 100644 --- a/src/commands/observability/logs.ts +++ b/src/commands/observability/logs.ts @@ -31,7 +31,9 @@ const logsAction: SdkGuardedFunction = async ({ const projectId = config.projectId.get(); if (!projectId) { - output.error('No project selected. Use `af projects switch` to select a project.'); + output.error( + 'No project selected. Use `af projects switch` to select a project.', + ); return; } @@ -45,7 +47,7 @@ const logsAction: SdkGuardedFunction = async ({ try { // eslint-disable-next-line @typescript-eslint/no-explicit-any - const logs = await (sdk as any).observability().queryLogs({ + const logs = (await (sdk as any).observability().queryLogs({ projectId, startTime, endTime, @@ -53,7 +55,7 @@ const logsAction: SdkGuardedFunction = async ({ severityText: args.severity, bodyContains: args.search, limit, - }) as LogEntry[]; + })) as LogEntry[]; output.stopSpinner(); diff --git a/src/commands/observability/services.ts b/src/commands/observability/services.ts index 8c87e94..54663bc 100644 --- a/src/commands/observability/services.ts +++ b/src/commands/observability/services.ts @@ -25,7 +25,9 @@ const servicesAction: SdkGuardedFunction = async ({ const projectId = config.projectId.get(); if (!projectId) { - output.error('No project selected. Use `af projects switch` to select a project.'); + output.error( + 'No project selected. Use `af projects switch` to select a project.', + ); return; } @@ -34,20 +36,24 @@ const servicesAction: SdkGuardedFunction = async ({ const endTime = new Date(); const startTime = new Date(endTime.getTime() - hours * 60 * 60 * 1000); - output.spinner(`Fetching service statistics for the last ${hours} hour(s)...`); + output.spinner( + `Fetching service statistics for the last ${hours} hour(s)...`, + ); try { // eslint-disable-next-line @typescript-eslint/no-explicit-any - const services = await (sdk as any) + const services = (await (sdk as any) .observability() - .getServices(projectId, startTime, endTime) as ServiceStats[]; + .getServices(projectId, startTime, endTime)) as ServiceStats[]; output.stopSpinner(); if (!services || services.length === 0) { output.warn('No services found with telemetry data'); output.printNewLine(); - output.log('Make sure your application is sending traces to AlternateFutures.'); + output.log( + 'Make sure your application is sending traces to AlternateFutures.', + ); output.log('See: https://docs.alternatefutures.ai/observability/setup'); return; } @@ -72,9 +78,18 @@ const servicesAction: SdkGuardedFunction = async ({ output.printNewLine(); // Calculate totals - const totalSpans = services.reduce((sum: number, s: ServiceStats) => sum + s.spanCount, 0); - const totalErrors = services.reduce((sum: number, s: ServiceStats) => sum + s.errorCount, 0); - const totalTraces = services.reduce((sum: number, s: ServiceStats) => sum + s.traceCount, 0); + const totalSpans = services.reduce( + (sum: number, s: ServiceStats) => sum + s.spanCount, + 0, + ); + const totalErrors = services.reduce( + (sum: number, s: ServiceStats) => sum + s.errorCount, + 0, + ); + const totalTraces = services.reduce( + (sum: number, s: ServiceStats) => sum + s.traceCount, + 0, + ); output.log('Totals:'); output.log(` Services: ${services.length}`); diff --git a/src/commands/observability/settings.ts b/src/commands/observability/settings.ts index 139beb0..7a0cc9d 100644 --- a/src/commands/observability/settings.ts +++ b/src/commands/observability/settings.ts @@ -18,7 +18,9 @@ const settingsAction: SdkGuardedFunction = async ({ sdk }) => { const projectId = config.projectId.get(); if (!projectId) { - output.error('No project selected. Use `af projects switch` to select a project.'); + output.error( + 'No project selected. Use `af projects switch` to select a project.', + ); return; } @@ -26,7 +28,9 @@ const settingsAction: SdkGuardedFunction = async ({ sdk }) => { try { // eslint-disable-next-line @typescript-eslint/no-explicit-any - const settings = await (sdk as any).observability().getSettings(projectId) as ObservabilitySettings | null; + const settings = (await (sdk as any) + .observability() + .getSettings(projectId)) as ObservabilitySettings | null; output.stopSpinner(); @@ -86,7 +90,9 @@ const settingsAction: SdkGuardedFunction = async ({ sdk }) => { output.table(tableData); output.printNewLine(); - output.log('Use `af observability settings:update` to modify these settings'); + output.log( + 'Use `af observability settings:update` to modify these settings', + ); output.printNewLine(); } catch (error) { output.stopSpinner(); @@ -103,79 +109,86 @@ type UpdateSettingsActionArgs = { logRetention?: string; }; -const updateSettingsAction: SdkGuardedFunction = - async ({ sdk, args }) => { - const projectId = config.projectId.get(); - - if (!projectId) { - output.error('No project selected. Use `af projects switch` to select a project.'); - return; - } +const updateSettingsAction: SdkGuardedFunction< + UpdateSettingsActionArgs +> = async ({ sdk, args }) => { + const projectId = config.projectId.get(); - // Parse boolean strings - const parseBoolean = (value: string | undefined): boolean | undefined => { - if (value === undefined) return undefined; - return value.toLowerCase() === 'true'; - }; + if (!projectId) { + output.error( + 'No project selected. Use `af projects switch` to select a project.', + ); + return; + } - const updates: Record = {}; + // Parse boolean strings + const parseBoolean = (value: string | undefined): boolean | undefined => { + if (value === undefined) return undefined; + return value.toLowerCase() === 'true'; + }; - if (args.traces !== undefined) { - updates.tracesEnabled = parseBoolean(args.traces); - } - if (args.metrics !== undefined) { - updates.metricsEnabled = parseBoolean(args.metrics); - } - if (args.logs !== undefined) { - updates.logsEnabled = parseBoolean(args.logs); - } - if (args.sampleRate !== undefined) { - const rate = Number.parseFloat(args.sampleRate); - if (rate < 0 || rate > 1) { - output.error('Sample rate must be between 0.0 and 1.0'); - return; - } - updates.sampleRate = rate; - } - if (args.traceRetention !== undefined) { - updates.traceRetention = Number.parseInt(args.traceRetention, 10); - } - if (args.logRetention !== undefined) { - updates.logRetention = Number.parseInt(args.logRetention, 10); - } + const updates: Record = {}; - if (Object.keys(updates).length === 0) { - output.warn('No settings to update. Use --help to see available options.'); + if (args.traces !== undefined) { + updates.tracesEnabled = parseBoolean(args.traces); + } + if (args.metrics !== undefined) { + updates.metricsEnabled = parseBoolean(args.metrics); + } + if (args.logs !== undefined) { + updates.logsEnabled = parseBoolean(args.logs); + } + if (args.sampleRate !== undefined) { + const rate = Number.parseFloat(args.sampleRate); + if (rate < 0 || rate > 1) { + output.error('Sample rate must be between 0.0 and 1.0'); return; } + updates.sampleRate = rate; + } + if (args.traceRetention !== undefined) { + updates.traceRetention = Number.parseInt(args.traceRetention, 10); + } + if (args.logRetention !== undefined) { + updates.logRetention = Number.parseInt(args.logRetention, 10); + } - output.spinner('Updating observability settings...'); + if (Object.keys(updates).length === 0) { + output.warn('No settings to update. Use --help to see available options.'); + return; + } - try { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const settings = await (sdk as any) - .observability() - .updateSettings(projectId, updates) as ObservabilitySettings; + output.spinner('Updating observability settings...'); - output.stopSpinner(); + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const settings = (await (sdk as any) + .observability() + .updateSettings(projectId, updates)) as ObservabilitySettings; - output.printNewLine(); - output.success('Observability settings updated successfully!'); - output.printNewLine(); + output.stopSpinner(); - output.log('New settings:'); - output.log(` Traces Enabled: ${settings.tracesEnabled ? 'Yes' : 'No'}`); - output.log(` Metrics Enabled: ${settings.metricsEnabled ? 'Yes' : 'No'}`); - output.log(` Logs Enabled: ${settings.logsEnabled ? 'Yes' : 'No'}`); - output.log(` Sample Rate: ${(settings.sampleRate * 100).toFixed(0)}%`); - output.log(` Trace Retention: ${settings.traceRetention} days`); - output.log(` Log Retention: ${settings.logRetention} days`); - output.printNewLine(); - } catch (error) { - output.stopSpinner(); - throw error; - } - }; + output.printNewLine(); + output.success('Observability settings updated successfully!'); + output.printNewLine(); + + output.log('New settings:'); + output.log(` Traces Enabled: ${settings.tracesEnabled ? 'Yes' : 'No'}`); + output.log( + ` Metrics Enabled: ${settings.metricsEnabled ? 'Yes' : 'No'}`, + ); + output.log(` Logs Enabled: ${settings.logsEnabled ? 'Yes' : 'No'}`); + output.log( + ` Sample Rate: ${(settings.sampleRate * 100).toFixed(0)}%`, + ); + output.log(` Trace Retention: ${settings.traceRetention} days`); + output.log(` Log Retention: ${settings.logRetention} days`); + output.printNewLine(); + } catch (error) { + output.stopSpinner(); + throw error; + } +}; export const settingsActionHandler = withGuards(settingsAction, { scopes: { diff --git a/src/commands/observability/traces.ts b/src/commands/observability/traces.ts index e0b1ff3..5ee8074 100644 --- a/src/commands/observability/traces.ts +++ b/src/commands/observability/traces.ts @@ -37,7 +37,9 @@ const tracesAction: SdkGuardedFunction = async ({ const projectId = config.projectId.get(); if (!projectId) { - output.error('No project selected. Use `af projects switch` to select a project.'); + output.error( + 'No project selected. Use `af projects switch` to select a project.', + ); return; } @@ -54,7 +56,7 @@ const tracesAction: SdkGuardedFunction = async ({ try { // eslint-disable-next-line @typescript-eslint/no-explicit-any - const traces = await (sdk as any).observability().queryTraces({ + const traces = (await (sdk as any).observability().queryTraces({ projectId, startTime, endTime, @@ -62,7 +64,7 @@ const tracesAction: SdkGuardedFunction = async ({ statusCode: args.status, minDurationMs, limit, - }) as Trace[]; + })) as Trace[]; output.stopSpinner(); @@ -76,7 +78,7 @@ const tracesAction: SdkGuardedFunction = async ({ output.printNewLine(); const tableData = traces.map((trace: Trace) => ({ - 'Trace ID': trace.traceId.substring(0, 16) + '...', + 'Trace ID': `${trace.traceId.substring(0, 16)}...`, Service: trace.serviceName, Duration: `${trace.durationMs.toFixed(2)}ms`, Spans: trace.spanCount, @@ -105,7 +107,9 @@ const traceAction: SdkGuardedFunction = async ({ const projectId = config.projectId.get(); if (!projectId) { - output.error('No project selected. Use `af projects switch` to select a project.'); + output.error( + 'No project selected. Use `af projects switch` to select a project.', + ); return; } @@ -113,7 +117,9 @@ const traceAction: SdkGuardedFunction = async ({ try { // eslint-disable-next-line @typescript-eslint/no-explicit-any - const trace = await (sdk as any).observability().getTrace(projectId, args.traceId) as Trace | null; + const trace = (await (sdk as any) + .observability() + .getTrace(projectId, args.traceId)) as Trace | null; output.stopSpinner(); diff --git a/src/commands/observability/usage.ts b/src/commands/observability/usage.ts index 61ed9c8..d4f33b1 100644 --- a/src/commands/observability/usage.ts +++ b/src/commands/observability/usage.ts @@ -25,7 +25,9 @@ const usageAction: SdkGuardedFunction = async ({ const projectId = config.projectId.get(); if (!projectId) { - output.error('No project selected. Use `af projects switch` to select a project.'); + output.error( + 'No project selected. Use `af projects switch` to select a project.', + ); return; } @@ -38,9 +40,9 @@ const usageAction: SdkGuardedFunction = async ({ try { // eslint-disable-next-line @typescript-eslint/no-explicit-any - const usage = await (sdk as any) + const usage = (await (sdk as any) .observability() - .getUsage(projectId, startDate, endDate) as TelemetryUsage; + .getUsage(projectId, startDate, endDate)) as TelemetryUsage; output.stopSpinner(); @@ -48,7 +50,9 @@ const usageAction: SdkGuardedFunction = async ({ output.log('Telemetry Usage Summary:'); output.printNewLine(); - output.log(` Period: ${startDate.toLocaleDateString()} - ${endDate.toLocaleDateString()}`); + output.log( + ` Period: ${startDate.toLocaleDateString()} - ${endDate.toLocaleDateString()}`, + ); output.log(` Project ID: ${projectId}`); output.printNewLine(); @@ -80,7 +84,9 @@ const usageAction: SdkGuardedFunction = async ({ // Show pricing info output.log('Pricing: $0.35 per GB ingested'); - output.log('View detailed billing at: https://app.alternatefutures.ai/billing'); + output.log( + 'View detailed billing at: https://app.alternatefutures.ai/billing', + ); output.printNewLine(); } catch (error) { output.stopSpinner(); diff --git a/src/guards/sdkGuard.ts b/src/guards/sdkGuard.ts index 5de4676..5bb6e1f 100644 --- a/src/guards/sdkGuard.ts +++ b/src/guards/sdkGuard.ts @@ -35,7 +35,8 @@ export const getSdkClient = () => { }; // Add authServiceUrl if available (for billing support in newer SDK versions) - const authServiceUrl = getDefined('SDK__AUTH_SERVICE_URL') || getDefined('AUTH__API_URL'); + const authServiceUrl = + getDefined('SDK__AUTH_SERVICE_URL') || getDefined('AUTH__API_URL'); if (authServiceUrl) { (sdkOptions as Record).authServiceUrl = authServiceUrl; }