diff --git a/apps/desktop/scripts/ensure-pty-prebuilds.ts b/apps/desktop/scripts/ensure-pty-prebuilds.ts index a08856a51f2..59de0270cb5 100644 --- a/apps/desktop/scripts/ensure-pty-prebuilds.ts +++ b/apps/desktop/scripts/ensure-pty-prebuilds.ts @@ -16,11 +16,11 @@ import { execFileSync } from 'node:child_process' import { createHash, timingSafeEqual } from 'node:crypto' import { + cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, - renameSync, rmSync, writeFileSync, } from 'node:fs' @@ -103,7 +103,7 @@ async function fetchPrebuild(arch: string, version: string): Promise { const target = packageDir(arch) mkdirSync(dirname(target), { recursive: true }) rmSync(target, { recursive: true, force: true }) - renameSync(join(staging, 'package'), target) + cpSync(join(staging, 'package'), target, { recursive: true, force: true }) } finally { rmSync(staging, { recursive: true, force: true }) } diff --git a/apps/sim/.env.example b/apps/sim/.env.example index f5d05f0ac23..8723718c5fd 100644 --- a/apps/sim/.env.example +++ b/apps/sim/.env.example @@ -96,6 +96,7 @@ CRON_SECRET=your_cron_secret # Use `openssl rand -hex 32` to generate. Authentic # LITELLM_BASE_URL=http://localhost:4000 # Base URL for your LiteLLM proxy (OpenAI-compatible) # LITELLM_API_KEY= # Optional bearer token if your LiteLLM proxy requires auth # OPENROUTER_API_KEY= # Optional self-hosted fallback for OpenAI knowledge-base embeddings +# DISABLE_TELEMETRY=1 # Disable all OpenTelemetry collection (traces, metrics, logs). Prevents export timeouts when the telemetry endpoint is unreachable # NEXT_PUBLIC_FORCE_HOSTED=true # Dev only: treat this instance as hosted Sim (sim-auto pool, platform keys); ignored in production builds # FIREWORKS_API_KEY= # Optional Fireworks AI API key for model listing and inference # FIREWORKS_API_KEY_1= # Optional Fireworks API key for rotation (hosted deployments) diff --git a/apps/sim/instrumentation-node.ts b/apps/sim/instrumentation-node.ts index d9a3668d304..9a3194f03cb 100644 --- a/apps/sim/instrumentation-node.ts +++ b/apps/sim/instrumentation-node.ts @@ -35,7 +35,7 @@ const DEFAULT_TELEMETRY_CONFIG = { maxQueueSize: 2048, maxExportBatchSize: 512, scheduledDelayMillis: 5000, - exportTimeoutMillis: 30000, + exportTimeoutMillis: 10000, }, } @@ -151,8 +151,12 @@ class MothershipOriginSpanProcessor implements SpanProcessor { async function initializeOpenTelemetry() { try { - if (env.NEXT_TELEMETRY_DISABLED === '1' || process.env.NEXT_TELEMETRY_DISABLED === '1') { - logger.info('OpenTelemetry disabled via NEXT_TELEMETRY_DISABLED=1') + if ( + process.env.DISABLE_TELEMETRY === '1' || + env.NEXT_TELEMETRY_DISABLED === '1' || + process.env.NEXT_TELEMETRY_DISABLED === '1' + ) { + logger.info('OpenTelemetry disabled via env var') return } @@ -359,15 +363,19 @@ async function initializeOpenTelemetry() { const shutdownOtel = async () => { try { - await sdk.shutdown() + const shutdownPromise = sdk.shutdown() + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('OTel shutdown timed out')), 5000) + ) + await Promise.race([shutdownPromise, timeoutPromise]) logger.info('OpenTelemetry SDK shut down successfully') } catch (err) { logger.error('Error shutting down OpenTelemetry SDK', err) } } - process.on('SIGTERM', shutdownOtel) - process.on('SIGINT', shutdownOtel) + process.once('SIGTERM', shutdownOtel) + process.once('SIGINT', shutdownOtel) logger.info('OpenTelemetry instrumentation initialized', { serviceName: telemetryConfig.serviceName, @@ -398,8 +406,8 @@ export async function register() { } } - process.on('SIGTERM', shutdownPostHog) - process.on('SIGINT', shutdownPostHog) + process.once('SIGTERM', shutdownPostHog) + process.once('SIGINT', shutdownPostHog) const { startMemoryTelemetry } = await import('./lib/monitoring/memory-telemetry') startMemoryTelemetry() diff --git a/apps/sim/lib/auth/anonymous.ts b/apps/sim/lib/auth/anonymous.ts index c4be061bea0..7848ac7cb23 100644 --- a/apps/sim/lib/auth/anonymous.ts +++ b/apps/sim/lib/auth/anonymous.ts @@ -9,6 +9,19 @@ const logger = createLogger('AnonymousAuth') let anonymousUserEnsured = false +function isMissingTableError(error: unknown): boolean { + if (error && typeof error === 'object') { + const code = (error as Record).code + if (code === '42P01') return true + const cause = (error as Record).cause + if (cause && typeof cause === 'object') { + const innerCode = (cause as Record).code + if (innerCode === '42P01') return true + } + } + return false +} + /** * Ensures the anonymous user and their stats record exist in the database. * Called when DISABLE_AUTH is enabled to ensure DB operations work. @@ -47,6 +60,11 @@ export async function ensureAnonymousUserExists(): Promise { anonymousUserEnsured = true } catch (error) { + if (isMissingTableError(error)) { + throw new Error( + 'Database tables not found. Run database migrations before starting the app: bun run db:migrate' + ) + } if ( error instanceof Error && (error.message.includes('unique') || error.message.includes('duplicate')) diff --git a/apps/sim/telemetry.config.ts b/apps/sim/telemetry.config.ts index 0256b3f32db..533253e2a75 100644 --- a/apps/sim/telemetry.config.ts +++ b/apps/sim/telemetry.config.ts @@ -57,7 +57,7 @@ const config = { maxQueueSize: 2048, maxExportBatchSize: 512, scheduledDelayMillis: 5000, - exportTimeoutMillis: 30000, + exportTimeoutMillis: 10000, }, /**