diff --git a/scripts/pack-artifact-policy.ts b/scripts/pack-artifact-policy.ts index 06793c10c6..5417abecce 100644 --- a/scripts/pack-artifact-policy.ts +++ b/scripts/pack-artifact-policy.ts @@ -61,6 +61,7 @@ export const PACK_ARTIFACT_ROOT_ALLOWED_EXACT_PATHS: string[] = [ ".env.example", "LICENSE", "README.md", + "bin/cli-commands.mjs", "bin/mcp-server.mjs", "bin/nodeRuntimeSupport.mjs", "bin/omniroute.mjs", @@ -84,6 +85,7 @@ export const PACK_ARTIFACT_ROOT_ALLOWED_EXACT_PATHS: string[] = [ ]; export const PACK_ARTIFACT_ROOT_ALLOWED_PATH_PREFIXES: string[] = [ + "bin/cli/", "open-sse/mcp-server/schemas/", "open-sse/mcp-server/tools/", "src/shared/contracts/", @@ -95,6 +97,8 @@ export const PACK_ARTIFACT_REQUIRED_PATHS: string[] = [ "app/server.js", "app/server-ws.mjs", "app/responses-ws-proxy.mjs", + "bin/cli-commands.mjs", + "bin/cli/index.mjs", "bin/mcp-server.mjs", "bin/nodeRuntimeSupport.mjs", "bin/omniroute.mjs", diff --git a/src/app/api/memory/[id]/route.ts b/src/app/api/memory/[id]/route.ts index a7be2e7748..b4a27e16c1 100644 --- a/src/app/api/memory/[id]/route.ts +++ b/src/app/api/memory/[id]/route.ts @@ -1,7 +1,11 @@ import { NextResponse } from "next/server"; +import { requireManagementAuth } from "@/lib/api/requireManagementAuth"; import { deleteMemory, getMemory } from "@/lib/memory/store"; export async function DELETE(request: Request, props: { params: Promise<{ id: string }> }) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const { id } = await props.params; const success = await deleteMemory(id); @@ -16,6 +20,9 @@ export async function DELETE(request: Request, props: { params: Promise<{ id: st } export async function GET(request: Request, props: { params: Promise<{ id: string }> }) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const { id } = await props.params; const memory = await getMemory(id); diff --git a/src/app/api/memory/route.ts b/src/app/api/memory/route.ts index 785b75002f..4d3cf913e1 100644 --- a/src/app/api/memory/route.ts +++ b/src/app/api/memory/route.ts @@ -1,4 +1,5 @@ import { NextResponse } from "next/server"; +import { requireManagementAuth } from "@/lib/api/requireManagementAuth"; import { listMemories, createMemory } from "@/lib/memory/store"; import { MemoryType } from "@/lib/memory/types"; import { parsePaginationParams, buildPaginatedResponse } from "@/shared/types/pagination"; @@ -16,6 +17,9 @@ const createMemorySchema = z.object({ }); export async function GET(request: Request) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const url = new URL(request.url); const { searchParams } = url; @@ -68,6 +72,9 @@ export async function GET(request: Request) { } export async function POST(request: Request) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const rawBody = await request.json(); const validation = validateBody(createMemorySchema, rawBody); diff --git a/src/app/api/model-combo-mappings/[id]/route.ts b/src/app/api/model-combo-mappings/[id]/route.ts index 88c0733c53..57d0cd6d74 100644 --- a/src/app/api/model-combo-mappings/[id]/route.ts +++ b/src/app/api/model-combo-mappings/[id]/route.ts @@ -6,6 +6,7 @@ import { z } from "zod"; import { NextResponse } from "next/server"; +import { requireManagementAuth } from "@/lib/api/requireManagementAuth"; import { updateModelComboMapping, deleteModelComboMapping, @@ -21,7 +22,10 @@ const updateMappingSchema = z.object({ description: z.string().max(1000).optional(), }); -export async function GET(_request: Request, { params }: { params: Promise<{ id: string }> }) { +export async function GET(request: Request, { params }: { params: Promise<{ id: string }> }) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const { id } = await params; const mapping = await getModelComboMappingById(id); @@ -35,6 +39,9 @@ export async function GET(_request: Request, { params }: { params: Promise<{ id: } export async function PUT(request: Request, { params }: { params: Promise<{ id: string }> }) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const { id } = await params; const rawBody = await request.json(); @@ -58,7 +65,10 @@ export async function PUT(request: Request, { params }: { params: Promise<{ id: } } -export async function DELETE(_request: Request, { params }: { params: Promise<{ id: string }> }) { +export async function DELETE(request: Request, { params }: { params: Promise<{ id: string }> }) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const { id } = await params; const deleted = await deleteModelComboMapping(id); diff --git a/src/app/api/model-combo-mappings/route.ts b/src/app/api/model-combo-mappings/route.ts index 359a7fb569..5b5e54dc62 100644 --- a/src/app/api/model-combo-mappings/route.ts +++ b/src/app/api/model-combo-mappings/route.ts @@ -6,6 +6,7 @@ import { z } from "zod"; import { NextResponse } from "next/server"; +import { requireManagementAuth } from "@/lib/api/requireManagementAuth"; import { getModelComboMappings, createModelComboMapping } from "@/lib/localDb"; import { validateBody, isValidationFailure } from "@/shared/validation/helpers"; @@ -17,7 +18,10 @@ const createMappingSchema = z.object({ description: z.string().max(1000).optional().default(""), }); -export async function GET() { +export async function GET(request: Request) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const mappings = await getModelComboMappings(); return NextResponse.json({ mappings }); @@ -30,6 +34,9 @@ export async function GET() { } export async function POST(request: Request) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const rawBody = await request.json(); const validation = validateBody(createMappingSchema, rawBody); diff --git a/src/app/api/pricing/route.ts b/src/app/api/pricing/route.ts index 3ea891cb78..7db9659574 100644 --- a/src/app/api/pricing/route.ts +++ b/src/app/api/pricing/route.ts @@ -1,4 +1,5 @@ import { NextResponse } from "next/server"; +import { requireManagementAuth } from "@/lib/api/requireManagementAuth"; import { getPricing, getPricingWithSources, @@ -14,6 +15,9 @@ import { isValidationFailure, validateBody } from "@/shared/validation/helpers"; * Get current pricing configuration (merged user + defaults) */ export async function GET(request: Request) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const includeSources = new URL(request.url).searchParams.get("includeSources") === "1"; if (includeSources) { @@ -34,6 +38,9 @@ export async function GET(request: Request) { * Body: { provider: { model: { input: number, output: number, cached: number, ... } } } */ export async function PATCH(request) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + let rawBody; try { rawBody = await request.json(); @@ -70,6 +77,9 @@ export async function PATCH(request) { * Query params: ?provider=xxx&model=yyy (optional) */ export async function DELETE(request) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const { searchParams } = new URL(request.url); const provider = searchParams.get("provider"); diff --git a/src/app/api/pricing/sync/route.ts b/src/app/api/pricing/sync/route.ts index b60d78fccf..1ff9ece74f 100644 --- a/src/app/api/pricing/sync/route.ts +++ b/src/app/api/pricing/sync/route.ts @@ -7,10 +7,14 @@ */ import { NextRequest, NextResponse } from "next/server"; +import { requireManagementAuth } from "@/lib/api/requireManagementAuth"; import { pricingSyncRequestSchema } from "@/shared/validation/schemas"; import { isValidationFailure, validateBody } from "@/shared/validation/helpers"; export async function POST(request: NextRequest) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + let rawBody: unknown; try { rawBody = await request.json(); @@ -43,7 +47,10 @@ export async function POST(request: NextRequest) { } } -export async function GET() { +export async function GET(request: NextRequest) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const { getSyncStatus } = await import("@/lib/pricingSync"); return NextResponse.json(getSyncStatus()); @@ -53,7 +60,10 @@ export async function GET() { } } -export async function DELETE() { +export async function DELETE(request: NextRequest) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const { clearSyncedPricing } = await import("@/lib/pricingSync"); clearSyncedPricing(); diff --git a/src/app/api/provider-nodes/validate/route.ts b/src/app/api/provider-nodes/validate/route.ts index bc2d13c962..22d9136f73 100644 --- a/src/app/api/provider-nodes/validate/route.ts +++ b/src/app/api/provider-nodes/validate/route.ts @@ -1,4 +1,5 @@ import { NextResponse } from "next/server"; +import { requireManagementAuth } from "@/lib/api/requireManagementAuth"; import { getAuditRequestContext, logAuditEvent } from "@/lib/compliance/index"; import { validateClaudeCodeCompatibleProvider } from "@/lib/providers/validation"; import { @@ -41,6 +42,9 @@ function sanitizeAuditBaseUrl(baseUrl: string) { // POST /api/provider-nodes/validate - Validate API key against base URL export async function POST(request) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + const auditContext = getAuditRequestContext(request); let rawBody; try { diff --git a/src/app/api/providers/validate/route.ts b/src/app/api/providers/validate/route.ts index 41f198d992..687d935bf9 100644 --- a/src/app/api/providers/validate/route.ts +++ b/src/app/api/providers/validate/route.ts @@ -1,4 +1,5 @@ import { NextResponse } from "next/server"; +import { requireManagementAuth } from "@/lib/api/requireManagementAuth"; import { getAuditRequestContext, logAuditEvent } from "@/lib/compliance/index"; import { getProviderNodeById } from "@/models"; import { @@ -24,6 +25,9 @@ function sanitizeAuditUrl(url: string | null | undefined) { // POST /api/providers/validate - Validate API key with provider export async function POST(request) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + const auditContext = getAuditRequestContext(request); let rawBody; try { diff --git a/src/app/api/usage/analytics/route.ts b/src/app/api/usage/analytics/route.ts index 546d41c481..17fc459e74 100644 --- a/src/app/api/usage/analytics/route.ts +++ b/src/app/api/usage/analytics/route.ts @@ -1,4 +1,5 @@ import { NextResponse } from "next/server"; +import { requireManagementAuth } from "@/lib/api/requireManagementAuth"; import { getApiKeys } from "@/lib/db/apiKeys"; import { getDbInstance } from "@/lib/db/core"; @@ -87,7 +88,6 @@ function uniqueValues(values: Array): string[] { return result; } - function makeApiKeyUsageGroup(apiKeyId: string, fallbackName: string): string { return apiKeyId ? `id:${apiKeyId}` : `name:${fallbackName}`; } @@ -98,7 +98,6 @@ function addApiKeyAlias(target: Set, value: unknown): void { if (trimmed) target.add(trimmed); } - function stripCodexEffortSuffix(model: string): string { return model.replace(/-(?:xhigh|high|medium|low|none)$/i, ""); } @@ -263,6 +262,9 @@ function computeActivityStreak(activityMap: Record): number { } export async function GET(request: Request) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const { searchParams } = new URL(request.url); const range = searchParams.get("range") || "30d"; diff --git a/src/app/api/usage/history/route.ts b/src/app/api/usage/history/route.ts index 16a5d40725..6094e7baec 100644 --- a/src/app/api/usage/history/route.ts +++ b/src/app/api/usage/history/route.ts @@ -1,7 +1,11 @@ import { NextResponse } from "next/server"; +import { requireManagementAuth } from "@/lib/api/requireManagementAuth"; import { getUsageStats } from "@/lib/usageDb"; -export async function GET() { +export async function GET(request: Request) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const stats = await getUsageStats(); return NextResponse.json(stats); diff --git a/src/app/api/usage/logs/route.ts b/src/app/api/usage/logs/route.ts index b5b875ec94..ebb409d2f2 100644 --- a/src/app/api/usage/logs/route.ts +++ b/src/app/api/usage/logs/route.ts @@ -1,7 +1,11 @@ import { NextResponse } from "next/server"; +import { requireManagementAuth } from "@/lib/api/requireManagementAuth"; import { getRecentLogs } from "@/lib/usageDb"; -export async function GET() { +export async function GET(request: Request) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const logs = await getRecentLogs(200); return NextResponse.json(logs); diff --git a/src/app/api/usage/request-logs/route.ts b/src/app/api/usage/request-logs/route.ts index 0ae5e961cc..0e335c26d9 100644 --- a/src/app/api/usage/request-logs/route.ts +++ b/src/app/api/usage/request-logs/route.ts @@ -1,7 +1,11 @@ import { NextResponse } from "next/server"; +import { requireManagementAuth } from "@/lib/api/requireManagementAuth"; import { getRecentLogs } from "@/lib/usageDb"; -export async function GET() { +export async function GET(request: Request) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + try { const logs = await getRecentLogs(200); return NextResponse.json(logs); diff --git a/src/lib/db/migrationRunner.ts b/src/lib/db/migrationRunner.ts index bfc32b5e63..d53c66e9a9 100644 --- a/src/lib/db/migrationRunner.ts +++ b/src/lib/db/migrationRunner.ts @@ -133,6 +133,12 @@ const RENAMED_MIGRATION_COMPATIBILITY = [ toVersion: "039", toName: "compression_cache_stats", }, + { + fromVersion: "041", + fromName: "session_account_affinity", + toVersion: "050", + toName: "session_account_affinity", + }, ] as const; const LEGACY_VERSION_SLOT_MIGRATIONS = [ @@ -144,6 +150,15 @@ const LEGACY_VERSION_SLOT_MIGRATIONS = [ { version: "033", name: "provider_connections_block_extra_usage" }, ] as const; +const SUPERSEDED_DUPLICATE_MIGRATIONS = [ + { + version: "041", + name: "session_account_affinity", + supersededByVersion: "050", + supersededByName: "session_account_affinity", + }, +] as const; + const PHYSICAL_SCHEMA_SENTINELS = [ { version: "028", tableName: "batches", description: "batches table" }, { version: "024", tableName: "sync_tokens", description: "sync_tokens table" }, @@ -198,6 +213,34 @@ function getMigrationFiles(): Array<{ version: string; name: string; path: strin .filter(Boolean) as Array<{ version: string; name: string; path: string }>; } +function filterSupersededDuplicateMigrations( + files: Array<{ version: string; name: string; path: string }> +): Array<{ version: string; name: string; path: string }> { + return files.filter((file) => { + const superseded = SUPERSEDED_DUPLICATE_MIGRATIONS.find( + (migration) => migration.version === file.version && migration.name === file.name + ); + if (!superseded) { + return true; + } + + const hasReplacement = files.some( + (candidate) => + candidate.version === superseded.supersededByVersion && + candidate.name === superseded.supersededByName + ); + if (!hasReplacement) { + return true; + } + + console.warn( + `[Migration] Ignoring superseded duplicate migration ${file.version}_${file.name}; ` + + `${superseded.supersededByVersion}_${superseded.supersededByName} is the canonical slot.` + ); + return false; + }); +} + /** * Get list of already-applied migration versions. */ @@ -286,6 +329,9 @@ function isSchemaAlreadyApplied( case "040": return hasColumn(db, "proxy_registry", "source"); case "041": + if (migration.name === "session_account_affinity") { + return hasTable(db, "session_account_affinity"); + } return ( hasColumn(db, "compression_analytics", "actual_prompt_tokens") && hasColumn(db, "compression_analytics", "actual_completion_tokens") && @@ -667,7 +713,7 @@ export function runMigrations(db: Database.Database, options?: { isNewDb?: boole const isNewDb = options?.isNewDb === true; ensureMigrationsTable(db); - const files = getMigrationFiles(); + const files = filterSupersededDuplicateMigrations(getMigrationFiles()); rehomeLegacyVersionSlotMigrations(db, files); reconcileRenumberedMigrations(db, files); const applied = getAppliedVersions(db); @@ -779,7 +825,7 @@ export function runMigrations(db: Database.Database, options?: { isNewDb?: boole ); } else if (migration.version === "032") { applyApiKeyLifecycleMigration(db); - } else if (migration.version === "041") { + } else if (migration.version === "041" && migration.name === "compression_receipts") { applyCompressionReceiptsMigration(db); } else if (migration.version === "042") { applyCompressionCombosMigration(db, migration.path); diff --git a/tests/unit/db-migration-runner.test.ts b/tests/unit/db-migration-runner.test.ts index a0595ad532..519fcebf74 100644 --- a/tests/unit/db-migration-runner.test.ts +++ b/tests/unit/db-migration-runner.test.ts @@ -857,6 +857,141 @@ test( } ); +test( + "runMigrations ignores superseded 041 session affinity duplicate when 050 exists", + serial, + async () => { + const runner = await importFresh("src/lib/db/migrationRunner.ts"); + const db = createDb(); + + try { + const count = withMockedMigrationFs( + { + "038_compression_analytics.sql": ` + CREATE TABLE compression_analytics ( + id TEXT PRIMARY KEY, + request_id TEXT + ); + `, + "041_compression_receipts.sql": "-- handled by migrationRunner", + "041_session_account_affinity.sql": ` + CREATE TABLE duplicate_041_session_account_affinity (id TEXT PRIMARY KEY); + `, + "050_session_account_affinity.sql": ` + CREATE TABLE IF NOT EXISTS session_account_affinity ( + session_key TEXT NOT NULL, + provider TEXT NOT NULL, + connection_id TEXT NOT NULL, + created_at INTEGER NOT NULL, + last_seen_at INTEGER NOT NULL, + PRIMARY KEY (session_key, provider) + ); + `, + }, + () => runner.runMigrations(db) + ); + + assert.equal(count, 3); + assert.equal( + db.prepare("SELECT name FROM _omniroute_migrations WHERE version = ?").get("041")?.name, + "compression_receipts" + ); + assert.equal( + db.prepare("SELECT name FROM _omniroute_migrations WHERE version = ?").get("050")?.name, + "session_account_affinity" + ); + assert.deepEqual( + db + .prepare( + "SELECT name FROM sqlite_master WHERE type = 'table' AND name IN (?, ?) ORDER BY name" + ) + .all("duplicate_041_session_account_affinity", "session_account_affinity"), + [{ name: "session_account_affinity" }] + ); + } finally { + db.close(); + } + } +); + +test( + "reconcileRenumberedMigrations moves legacy 041 session affinity marker to 050", + serial, + async () => { + const runner = await importFresh("src/lib/db/migrationRunner.ts"); + const db = createDb(); + + try { + db.exec(` + CREATE TABLE _omniroute_migrations ( + version TEXT PRIMARY KEY, + name TEXT NOT NULL, + applied_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + CREATE TABLE compression_analytics ( + id TEXT PRIMARY KEY, + request_id TEXT + ); + `); + db.prepare("INSERT INTO _omniroute_migrations (version, name) VALUES (?, ?)").run( + "041", + "session_account_affinity" + ); + + const consoleErrors: string[] = []; + const originalError = console.error; + console.error = (...args: any[]) => { + consoleErrors.push(args.map(String).join(" ")); + }; + + try { + const count = withMockedMigrationFs( + { + "041_compression_receipts.sql": "-- handled by migrationRunner", + "041_session_account_affinity.sql": ` + CREATE TABLE duplicate_041_session_account_affinity (id TEXT PRIMARY KEY); + `, + "050_session_account_affinity.sql": ` + CREATE TABLE IF NOT EXISTS session_account_affinity ( + session_key TEXT NOT NULL, + provider TEXT NOT NULL, + connection_id TEXT NOT NULL, + created_at INTEGER NOT NULL, + last_seen_at INTEGER NOT NULL, + PRIMARY KEY (session_key, provider) + ); + `, + }, + () => runner.runMigrations(db) + ); + + assert.equal(count, 1); + assert.equal( + db.prepare("SELECT name FROM _omniroute_migrations WHERE version = ?").get("041")?.name, + "compression_receipts" + ); + assert.equal( + db.prepare("SELECT name FROM _omniroute_migrations WHERE version = ?").get("050")?.name, + "session_account_affinity" + ); + + const renumberingWarnings = consoleErrors.filter( + (e) => e.includes("CRITICAL") && e.includes("renumbered") + ); + assert.equal( + renumberingWarnings.length, + 0, + `Expected no renumbering warnings, got: ${renumberingWarnings.join("; ")}` + ); + } finally { + console.error = originalError; + } + } finally { + db.close(); + } + } +); + test( "full upgrade simulation: all 3 renumbered migrations reconciled without CRITICAL warnings", serial, diff --git a/tests/unit/management-auth-hardening.test.ts b/tests/unit/management-auth-hardening.test.ts index 2b14e81a3e..dd9540b3a5 100644 --- a/tests/unit/management-auth-hardening.test.ts +++ b/tests/unit/management-auth-hardening.test.ts @@ -43,3 +43,76 @@ test("compression analytics route requires management authentication before retu content.indexOf("getCompressionAnalyticsSummary(") ); }); + +test("administrative pricing and routing routes require management authentication", () => { + const routePaths = [ + "src/app/api/pricing/route.ts", + "src/app/api/pricing/sync/route.ts", + "src/app/api/model-combo-mappings/route.ts", + "src/app/api/model-combo-mappings/[id]/route.ts", + ]; + + for (const routePath of routePaths) { + const content = fs.readFileSync(routePath, "utf8"); + assert.ok(content.includes('from "@/lib/api/requireManagementAuth"'), routePath); + assert.ok( + content.includes("const authError = await requireManagementAuth(request);"), + routePath + ); + assert.ok(content.includes("if (authError) return authError;"), routePath); + } +}); + +test("memory management routes require management authentication", () => { + const routePaths = ["src/app/api/memory/route.ts", "src/app/api/memory/[id]/route.ts"]; + + for (const routePath of routePaths) { + const content = fs.readFileSync(routePath, "utf8"); + assert.ok(content.includes('from "@/lib/api/requireManagementAuth"'), routePath); + assert.ok( + content.includes("const authError = await requireManagementAuth(request);"), + routePath + ); + assert.ok(content.includes("if (authError) return authError;"), routePath); + } +}); + +test("provider validation routes require management authentication before reading credentials", () => { + const routePaths = [ + "src/app/api/provider-nodes/validate/route.ts", + "src/app/api/providers/validate/route.ts", + ]; + + for (const routePath of routePaths) { + const content = fs.readFileSync(routePath, "utf8"); + assert.ok(content.includes('from "@/lib/api/requireManagementAuth"'), routePath); + assert.ok( + content.includes("const authError = await requireManagementAuth(request);"), + routePath + ); + assert.ok(content.includes("if (authError) return authError;"), routePath); + assert.ok( + content.indexOf("requireManagementAuth(request)") < content.indexOf("request.json()"), + `${routePath} should authenticate before parsing submitted provider credentials` + ); + } +}); + +test("usage analytics and request log routes require management authentication", () => { + const routePaths = [ + "src/app/api/usage/analytics/route.ts", + "src/app/api/usage/history/route.ts", + "src/app/api/usage/request-logs/route.ts", + "src/app/api/usage/logs/route.ts", + ]; + + for (const routePath of routePaths) { + const content = fs.readFileSync(routePath, "utf8"); + assert.ok(content.includes('from "@/lib/api/requireManagementAuth"'), routePath); + assert.ok( + content.includes("const authError = await requireManagementAuth(request);"), + routePath + ); + assert.ok(content.includes("if (authError) return authError;"), routePath); + } +}); diff --git a/tests/unit/pack-artifact-policy.test.ts b/tests/unit/pack-artifact-policy.test.ts index e47925474f..21f09440f1 100644 --- a/tests/unit/pack-artifact-policy.test.ts +++ b/tests/unit/pack-artifact-policy.test.ts @@ -73,6 +73,8 @@ test("findMissingArtifactPaths flags missing root runtime files in the tarball", "app/open-sse/services/compression/rules/en/filler.json", "app/responses-ws-proxy.mjs", "app/server-ws.mjs", + "bin/cli-commands.mjs", + "bin/cli/index.mjs", "bin/mcp-server.mjs", "bin/nodeRuntimeSupport.mjs", "scripts/native-binary-compat.mjs",