From d437a195b695f6cb60411db8dbf1f23f22e298db Mon Sep 17 00:00:00 2001 From: Anso Date: Sun, 5 Apr 2026 23:31:33 -0400 Subject: [PATCH] fix(fleet): resolve getSenchoVersion crash in Docker containers (#391) getSenchoVersion() used require('../../../package.json') which resolves correctly in dev (src/services/ -> 3 up -> root) but fails in Docker where the compiled file is at dist/services/ (2 levels from /app/). The 500 error on /api/meta caused fetchRemoteMeta to always return empty capabilities, blocking all capability-gated features on remote nodes. Replace with a __dirname-based walk that finds the root package.json (name === 'sencho') regardless of directory depth. --- CHANGELOG.md | 1 + backend/src/services/CapabilityRegistry.ts | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3efc63d1..8b6e3039 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * **licensing:** license card now shows "Duration: Lifetime" for lifetime licenses instead of an empty renewal date * **fleet:** remote node capability detection now works reliably; `/api/meta` and `/api/health` are exempt from the global rate limiter so they are never blocked by proxied traffic, and a backend-side cache with stale-while-revalidate prevents transient failures from disabling capability-gated features (Auto-Update, Schedules, Audit, Console) on remote nodes * **fleet:** failed capability fetches now retry after 30 seconds instead of being cached for the full 5-minute TTL +* **fleet:** `getSenchoVersion()` now resolves `package.json` via `__dirname` walk instead of a hardcoded relative path; the previous `require('../../../package.json')` resolved correctly in dev but crashed with a 500 in Docker (compiled path `dist/services/` has a different depth than source `src/services/`), causing `/api/meta` to always fail on containerized instances and all capability-gated features to appear unsupported on remote nodes ### Changed diff --git a/backend/src/services/CapabilityRegistry.ts b/backend/src/services/CapabilityRegistry.ts index 9705a94e..9266b591 100644 --- a/backend/src/services/CapabilityRegistry.ts +++ b/backend/src/services/CapabilityRegistry.ts @@ -1,4 +1,6 @@ import axios from 'axios'; +import path from 'path'; +import fs from 'fs'; /** * Static registry of capabilities supported by THIS Sencho instance. @@ -33,8 +35,21 @@ export const CAPABILITIES = [ export type Capability = (typeof CAPABILITIES)[number]; export function getSenchoVersion(): string { - // eslint-disable-next-line @typescript-eslint/no-require-imports - return require('../../../package.json').version; + // Walk up from __dirname to find the root package.json (name === 'sencho'). + // In dev this is 3 levels up (src/services/ -> root), in Docker it's 2 + // (dist/services/ -> /app/). Skips intermediate package.json files like + // backend/package.json which has a different name and version. + let dir = __dirname; + for (let i = 0; i < 5; i++) { + const candidate = path.join(dir, 'package.json'); + if (fs.existsSync(candidate)) { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const pkg = require(candidate); + if (pkg.name === 'sencho') return pkg.version; + } + dir = path.dirname(dir); + } + return 'unknown'; } export interface RemoteMeta {