fix(cli): make the standalone fn binary boot PostgreSQL in both modes

The bun-compiled exe has been unbootable since the PG cutover: bun
standalone binaries do no node_modules resolution, so the deliberately
out-of-graph require("embedded-postgres") failed from /$bunfs, and
readFile'd migration .sql files were never embedded, so even external
DATABASE_URL mode died at schema init.

- schema-applier: resolveMigrationsDir() — FUSION_MIGRATIONS_DIR env >
  module-relative dist/migrations (npm/desktop, unchanged) >
  execPath-relative migrations/ (standalone exe), probe-based.
- embedded-lifecycle: require("embedded-postgres") first (npm/desktop
  untouched), falling back to a self-contained staged bundle at
  <execDir>/runtime/<platform>/embedded-postgres/dist/index.cjs
  (FUSION_EMBEDDED_PG_RUNTIME_DIR override) with the native
  initdb/pg_ctl/postgres payload beside it.
- build.ts: stage dist/migrations plus the per-target embedded-postgres
  bundle + native payload (warn when a cross-target payload is absent on
  the host, mirroring desktop's verifyEmbeddedPostgresPayloads).
- release.yml: package fn-cli-<os>-<arch>.tar.gz (binary + migrations +
  runtime + client) with sha256 per leg; prune staged payload files from
  the release-collection globs; bare fn-cli-* binaries still uploaded.

E2E-verified on the compiled binary: embedded mode initdb→/api/health
200 database healthy; DATABASE_URL mode applied migrations 0000–0019
(109 tables). Core typecheck clean; schema-applier 58/58 and
embedded-lifecycle 44/44 tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-07-17 18:45:47 -07:00
parent 90a1a4b157
commit 6b893f78ec
5 changed files with 386 additions and 41 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Fix the standalone `fn` binary failing to boot in both embedded-Postgres and DATABASE_URL modes.
category: fix
dev: Migrations now resolve via FUSION_MIGRATIONS_DIR > module-relative > execPath-relative; embedded-postgres ships as a self-contained bundle + native payload under runtime/<platform>/embedded-postgres (override root with FUSION_EMBEDDED_PG_RUNTIME_DIR); releases add self-contained fn-cli-<platform>.tar.gz assets.

View File

@@ -110,6 +110,47 @@ jobs:
$hash = (Get-FileHash ${{ matrix.binary }} -Algorithm SHA256).Hash.ToLower()
"$hash ${{ matrix.binary }}" | Out-File -Encoding ascii ${{ matrix.binary }}.sha256
# FNXC:Release 2026-07-17-13:45:
# The bare binary alone cannot boot: the compiled exe resolves PostgreSQL
# migrations and the embedded-postgres runtime payload execPath-relative
# (see packages/core/src/postgres/schema-applier.ts and
# embedded-lifecycle.ts). Ship a self-contained tarball (binary +
# migrations/ + runtime/<platform>/) per target so a downloaded release
# asset works out of the box. The bare binary + .sha256 continue to be
# uploaded unchanged so existing download links stay valid.
- name: Package release tarball
shell: bash
run: |
cd packages/cli/dist
BASE="${{ matrix.binary }}"
BASE="${BASE%.exe}"
PLAT="${{ matrix.target }}"
PLAT="${PLAT#bun-}"
STAGE="tarball-stage"
rm -rf "$STAGE"
mkdir -p "$STAGE/runtime"
cp "${{ matrix.binary }}" "$STAGE/"
if [ -d migrations ]; then
cp -R migrations "$STAGE/migrations"
else
echo "::warning::packages/cli/dist/migrations missing; tarball will lack schema migrations"
fi
if [ -d "runtime/$PLAT" ]; then
cp -R "runtime/$PLAT" "$STAGE/runtime/$PLAT"
else
echo "::warning::packages/cli/dist/runtime/$PLAT missing; tarball will lack native runtime assets"
fi
if [ -d client ]; then
cp -R client "$STAGE/client"
fi
tar -czf "$BASE.tar.gz" -C "$STAGE" .
rm -rf "$STAGE"
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$BASE.tar.gz" > "$BASE.tar.gz.sha256"
else
shasum -a 256 "$BASE.tar.gz" > "$BASE.tar.gz.sha256"
fi
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
@@ -117,6 +158,9 @@ jobs:
path: |
packages/cli/dist/${{ matrix.binary }}
packages/cli/dist/${{ matrix.binary }}.sha256
packages/cli/dist/*.tar.gz
packages/cli/dist/*.tar.gz.sha256
packages/cli/dist/migrations/**/*
packages/cli/dist/runtime/**/*
# ── Build Windows desktop EXE artifacts ──────────────────────────────
@@ -568,7 +612,14 @@ jobs:
id: collect
run: |
mkdir release-files
find artifacts -type f \( -name "fn-*" -o -name "*.sha256" -o -name "*.asc" -o -name "*.exe" -o -name "*.exe.sha256" -o -name "*.blockmap" -o -name "*.dmg" -o -name "*.dmg.sha256" -o -name "*.zip" -o -name "*.zip.sha256" -o -name "*.apk" -o -name "*.aab" -o -name "*.AppImage" -o -name "*.AppImage.sha256" -o -name "*.deb" -o -name "*.deb.sha256" -o -name "*.tar.gz" -o -name "*.tar.gz.sha256" -o -name "latest*.yml" \) -exec cp {} release-files/ \;
# FNXC:Release 2026-07-17-13:45:
# Prune the CLI runtime/ and migrations/ staging trees: they exist in the
# artifact only as tarball inputs and contain files that would otherwise
# match the flat collection globs (e.g. embedded-postgres postgres.exe).
# The self-contained fn-cli-<platform>.tar.gz (+ .sha256) matches the
# existing *.tar.gz globs and reaches the release alongside the bare
# fn-cli-* binaries.
find artifacts \( -path "*/runtime/*" -o -path "*/migrations/*" \) -prune -o -type f \( -name "fn-*" -o -name "*.sha256" -o -name "*.asc" -o -name "*.exe" -o -name "*.exe.sha256" -o -name "*.blockmap" -o -name "*.dmg" -o -name "*.dmg.sha256" -o -name "*.zip" -o -name "*.zip.sha256" -o -name "*.apk" -o -name "*.aab" -o -name "*.AppImage" -o -name "*.AppImage.sha256" -o -name "*.deb" -o -name "*.deb.sha256" -o -name "*.tar.gz" -o -name "*.tar.gz.sha256" -o -name "latest*.yml" \) -print -exec cp {} release-files/ \;
ls -la release-files/
count=$(find release-files -type f | wc -l | tr -d ' ')
echo "count=$count" >> "$GITHUB_OUTPUT"

View File

@@ -212,6 +212,165 @@ function ensureClientAssets(): ClientAssetMode {
return "stub";
}
/*
FNXC:StandaloneExeMigrations 2026-07-17-13:40:
The compiled binary cannot read module-relative assets out of /$bunfs, so the
PostgreSQL migrations must ship as real files next to the binary. Stage
packages/core/src/postgres/migrations (same source tsup.config.ts stages into
dist/migrations for the npm package) into the exe output dir; core's
schema-applier resolves them execPath-relative at runtime.
*/
const pgMigrationsSrc = join(workspaceRoot, "packages", "core", "src", "postgres", "migrations");
const pgMigrationsDest = join(outDir, "migrations");
function stageMigrations(): void {
if (!existsSync(pgMigrationsSrc)) {
console.warn(
`WARNING: PostgreSQL migrations source not found at ${pgMigrationsSrc}; the standalone binary will fail to apply schema migrations.`,
);
return;
}
if (existsSync(pgMigrationsDest)) {
rmSync(pgMigrationsDest, { recursive: true, force: true });
}
cpSync(pgMigrationsSrc, pgMigrationsDest, { recursive: true });
console.log(` → ${pgMigrationsDest}`);
}
// ── Embedded PostgreSQL runtime staging ───────────────────────────────
/*
FNXC:StandaloneExeEmbeddedPg 2026-07-17-14:20:
core's embedded-lifecycle loads `embedded-postgres` via createRequire at
runtime (deliberately outside the bundler graph). Inside the compiled binary
that resolution fails: bun --compile binaries perform NO node_modules
bare-specifier resolution at runtime — not even through a createRequire
anchored at a real on-disk directory (verified empirically: requiring an
absolute path works, but any bare import like "pg" from that file then fails).
A staged node_modules tree therefore cannot work. Instead, stage a fully
self-contained esbuild CJS bundle of embedded-postgres (pg, async-exit-hook,
and the matching @embedded-postgres/<platform> entry inlined) at
dist/runtime/<platform>/embedded-postgres/dist/index.cjs
plus the native initdb/pg_ctl/postgres payload at
dist/runtime/<platform>/embedded-postgres/native/
The platform package resolves its binaries via import.meta.url ("../native/
bin/..."), so import.meta.url is defined to the bundle's own file URL and the
native tree is staged one level up — the same relative layout the package
expects. embedded-lifecycle probes this execPath-relative dir (or
FUSION_EMBEDDED_PG_RUNTIME_DIR) only when normal resolution fails.
pnpm-workspace.yaml supportedArchitectures limits local installs to the host
OS, so targets whose platform payload is absent on the build host get a
warning and no embedded payload (DATABASE_URL mode is unaffected), mirroring
the spirit of verifyEmbeddedPostgresPayloads in
packages/desktop/scripts/workspace-tools.ts.
*/
const coreRequire = createRequire(join(workspaceRoot, "packages", "core", "package.json"));
const ALL_EMBEDDED_PG_PLATFORM_PACKAGES = [
"@embedded-postgres/darwin-arm64",
"@embedded-postgres/darwin-x64",
"@embedded-postgres/linux-arm64",
"@embedded-postgres/linux-x64",
"@embedded-postgres/linux-arm",
"@embedded-postgres/linux-ia32",
"@embedded-postgres/linux-ppc64",
"@embedded-postgres/windows-x64",
] as const;
/** Map a runtime prebuild name (e.g. "darwin-arm64", "windows-x64") to the platform package. */
function embeddedPgPlatformPackageFor(prebuildName: string): string | null {
const [plat, arch] = prebuildName.split("-");
const os = plat === "windows" || plat === "win32" ? "windows" : plat;
const name = `@embedded-postgres/${os}-${arch}`;
return (ALL_EMBEDDED_PG_PLATFORM_PACKAGES as readonly string[]).includes(name) ? name : null;
}
function stageEmbeddedPostgresRuntime(target?: BunTarget): boolean {
const prebuildName = target ? targetToPrebuildName(target) : hostPrebuildName();
const destRoot = join(runtimeDir, prebuildName, "embedded-postgres");
try {
if (existsSync(destRoot)) {
rmSync(destRoot, { recursive: true, force: true });
}
mkdirSync(join(destRoot, "dist"), { recursive: true });
let embeddedPgJsonPath: string;
try {
embeddedPgJsonPath = coreRequire.resolve("embedded-postgres/package.json");
} catch {
console.warn(
` WARNING: embedded-postgres is not resolvable from @fusion/core; the ${prebuildName} binary will not support the default embedded database mode.`,
);
return false;
}
const embeddedPgRoot = dirname(embeddedPgJsonPath);
const embeddedPgEntry = join(embeddedPgRoot, "dist", "index.js");
const embeddedPgRequire = createRequire(embeddedPgJsonPath);
// Resolve the target's native payload (an optionalDependency of
// embedded-postgres, resolved from its own location). Absent payloads are
// a warning, not a failure — pnpm only installs the host OS's packages.
const platformPkg = embeddedPgPlatformPackageFor(prebuildName);
let nativeSrc: string | null = null;
if (platformPkg) {
try {
const platformEntry = embeddedPgRequire.resolve(platformPkg);
const candidate = join(dirname(platformEntry), "..", "native");
if (existsSync(join(candidate, "bin"))) nativeSrc = candidate;
} catch {
nativeSrc = null;
}
}
if (!platformPkg || !nativeSrc) {
console.warn(
` WARNING: embedded-postgres native payload (${platformPkg ?? "unmapped platform"}) is not installed on this host for target ${prebuildName}. ` +
`Embedded database mode will be unavailable in this build (DATABASE_URL mode is unaffected).`,
);
}
// Bundle embedded-postgres + deps into one self-contained CJS file. The
// target's platform package is inlined; the other platforms' dynamic
// imports stay external (their branches never execute at runtime).
const esbuildBin = join(workspaceRoot, "node_modules", ".bin", process.platform === "win32" ? "esbuild.cmd" : "esbuild");
if (!existsSync(esbuildBin)) {
console.warn(` WARNING: esbuild not found at ${esbuildBin}; cannot stage embedded-postgres runtime.`);
return false;
}
const externals = ALL_EMBEDDED_PG_PLATFORM_PACKAGES.filter(
(name) => !(nativeSrc && name === platformPkg),
);
const outFile = join(destRoot, "dist", "index.cjs");
const esbuildArgs = [
embeddedPgEntry,
"--bundle",
"--platform=node",
"--format=cjs",
`--outfile=${outFile}`,
// The inlined platform package computes native binary paths from
// import.meta.url; point it at the bundle's own real file location.
"--define:import.meta.url=__fusionEmbeddedPgBundleUrl",
"--banner:js=const __fusionEmbeddedPgBundleUrl = require('node:url').pathToFileURL(__filename).href;",
"--external:pg-native",
...externals.map((name) => `--external:${name}`),
"--log-level=warning",
];
const bundleProc = Bun.spawnSync({ cmd: [esbuildBin, ...esbuildArgs], cwd: workspaceRoot, stdout: "inherit", stderr: "inherit" });
if (bundleProc.exitCode !== 0) {
console.error(` ERROR: esbuild bundling of embedded-postgres failed for ${prebuildName} (exit ${bundleProc.exitCode}).`);
return false;
}
if (nativeSrc) {
// Preserve symlinks (macOS dylib ABI-compat links) and executable bits.
cpSync(nativeSrc, join(destRoot, "native"), { recursive: true });
}
console.log(` → ${destRoot} (embedded-postgres runtime bundle${nativeSrc ? " + native payload" : ", JS only"})`);
return nativeSrc !== null;
} catch (err) {
console.error(` ERROR: Failed to stage embedded-postgres runtime for ${prebuildName}:`, err);
return false;
}
}
// ── Copy native terminal assets for a specific target ─────────────────
/**
* Stage @homebridge/node-pty-prebuilt-multiarch native assets for the given target.
@@ -325,6 +484,10 @@ function compileBinary(outFile: string, target: string, isCrossCompile: boolean)
? target.replace(/^bun-/, "")
: hostPrebuildName();
copyNativeAssets(isCrossCompile ? target as BunTarget : undefined);
// FNXC:StandaloneExeEmbeddedPg 2026-07-17-13:40:
// Must run AFTER copyNativeAssets — that function recreates runtime/<plat>/
// and would wipe a previously staged embedded-postgres tree.
stageEmbeddedPostgresRuntime(isCrossCompile ? target as BunTarget : undefined);
// Prepare asset paths for embedding
const nativeAssetDir = join(runtimeDir, prebuildName);
@@ -382,6 +545,9 @@ const { targets } = parseArgs();
// Stage assets once (shared across all binaries)
const clientAssetMode = ensureClientAssets();
// FNXC:StandaloneExeMigrations 2026-07-17-13:40:
// PostgreSQL migrations ship next to the binary (platform-independent, staged once).
stageMigrations();
if (targets === null) {
// Default: build for current platform → dist/fn

View File

@@ -84,6 +84,107 @@ export { isWindowsElevatedAdmin } from "./embedded-windows-admin.js";
const require = createRequire(import.meta.url);
/*
FNXC:StandaloneExeEmbeddedPg 2026-07-17-14:25:
Inside the bun-compiled standalone `fn` binary, `createRequire(import.meta.url)`
is anchored at the virtual /$bunfs/root filesystem, so the deliberate
out-of-bundle `require("embedded-postgres")` fails with "Cannot find package
'embedded-postgres'" and default embedded-PG boot dies. Worse, bun --compile
binaries perform NO node_modules bare-specifier resolution at runtime at all
(verified: requiring an absolute file path works, but that file's own bare
imports like "pg" then fail), so a staged node_modules tree cannot help.
The standalone build (packages/cli/build.ts) therefore stages a fully
self-contained CJS bundle of embedded-postgres (pg and the
@embedded-postgres/<platform> entry inlined) plus the native
initdb/pg_ctl/postgres payload at
<dir-of-binary>/runtime/<platform>-<arch>/embedded-postgres/
dist/index.cjs — the bundle, loaded below by absolute path
native/bin/... — binaries the bundle resolves via ../native/
These helpers locate that staged bundle (or FUSION_EMBEDDED_PG_RUNTIME_DIR
when the operator relocates the payload). Resolution order is strictly
additive: the normal createRequire path is tried first, so npm/tsup and
desktop installs are untouched; the staged bundle is consulted only when the
primary resolution fails.
*/
function embeddedPostgresStagedRootCandidates(): string[] {
const roots: string[] = [];
const envRoot = process.env.FUSION_EMBEDDED_PG_RUNTIME_DIR;
if (envRoot) roots.push(envRoot);
try {
const execDir = dirname(process.execPath);
// build.ts stages per-target dirs named after the bun target suffix. For
// darwin/linux that equals `${process.platform}-${process.arch}`; Windows
// cross-compiled release assets use "windows-x64" while process.platform is
// "win32", so probe both spellings.
roots.push(join(execDir, "runtime", `${process.platform}-${process.arch}`, "embedded-postgres"));
if (process.platform === "win32") {
roots.push(join(execDir, "runtime", `windows-${process.arch}`, "embedded-postgres"));
}
} catch {
// process.execPath is always defined in practice; keep the fallback silent.
}
return roots;
}
let stagedEmbeddedPgBundleCache: string | null | undefined;
/** Absolute path of the staged self-contained embedded-postgres bundle, or null. */
function getStagedEmbeddedPgBundlePath(): string | null {
if (stagedEmbeddedPgBundleCache !== undefined) return stagedEmbeddedPgBundleCache;
stagedEmbeddedPgBundleCache = null;
for (const root of embeddedPostgresStagedRootCandidates()) {
try {
const bundle = join(root, "dist", "index.cjs");
if (existsSync(bundle)) {
stagedEmbeddedPgBundleCache = bundle;
break;
}
} catch {
// Keep probing the remaining candidates.
}
}
return stagedEmbeddedPgBundleCache;
}
/**
* require("embedded-postgres") with the staged-standalone fallback (loaded by
* absolute path — the only require form a compiled bun binary can resolve
* outside its own bundle). Primary resolution always wins.
*/
function requireEmbeddedPostgresModule(): unknown {
try {
return require("embedded-postgres");
} catch (primaryError) {
const bundle = getStagedEmbeddedPgBundlePath();
if (bundle) {
try {
return require(bundle);
} catch {
// Fall through and surface the primary (normal-install) error.
}
}
throw primaryError;
}
}
/**
* require.resolve() for embedded-postgres-family specifiers with the
* staged-standalone fallback. The staged bundle inlines the platform package,
* so every family specifier resolves to the bundle path — which preserves the
* layout contract callers rely on: join(dirname(entrypoint), "..", "native")
* lands on the staged native/ tree.
*/
function resolveEmbeddedPostgresSpecifier(specifier: string): string {
try {
return require.resolve(specifier);
} catch (primaryError) {
const bundle = getStagedEmbeddedPgBundlePath();
if (bundle && (specifier === "embedded-postgres" || specifier.startsWith("@embedded-postgres/"))) {
return bundle;
}
throw primaryError;
}
}
const EMBEDDED_PG_BIN_NAMES = new Set([
"postgres",
"initdb",
@@ -505,7 +606,10 @@ function getEmbeddedPostgresCtor(): EmbeddedPostgresCtor {
installElectronAsarNativePathPatch();
// Use require() so the bundler leaves this as a runtime resolution (esbuild
// keeps createRequire'd specifiers out of the static import graph).
const mod = require("embedded-postgres") as { default: EmbeddedPostgresCtor };
// FNXC:StandaloneExeEmbeddedPg 2026-07-17-14:25:
// requireEmbeddedPostgresModule adds the execPath-relative staged-bundle
// fallback used by the bun standalone binary; normal installs resolve as before.
const mod = requireEmbeddedPostgresModule() as { default: EmbeddedPostgresCtor };
embeddedPostgresCtorCache = mod.default ?? (mod as unknown as EmbeddedPostgresCtor);
return embeddedPostgresCtorCache;
}
@@ -684,7 +788,7 @@ function findPnpmVirtualStore(start: string): string | null {
function resolvePnpmPlatformPackageNativeRoot(packageName: string): string | null {
try {
const embeddedEntrypoint = require.resolve("embedded-postgres");
const embeddedEntrypoint = resolveEmbeddedPostgresSpecifier("embedded-postgres");
const virtualStore = findPnpmVirtualStore(dirname(embeddedEntrypoint));
if (!virtualStore) return null;
const encodedName = packageName.replace("/", "+");
@@ -722,7 +826,10 @@ function resolveGenericEmbeddedPostgresNativeRoot(): string | null {
const packageName = embeddedPostgresPlatformPackageName();
if (!packageName) return null;
try {
const entrypoint = require.resolve(packageName);
// FNXC:StandaloneExeEmbeddedPg 2026-07-17-13:35:
// Staged-standalone fallback included so the native initdb/pg_ctl/postgres
// payload resolves from the runtime dir shipped next to the fn binary.
const entrypoint = resolveEmbeddedPostgresSpecifier(packageName);
return resolveElectronAsarUnpackedPath(join(dirname(entrypoint), "..", "native"));
} catch {
return resolvePnpmPlatformPackageNativeRoot(packageName);

View File

@@ -20,6 +20,7 @@
*/
import { readFile } from "node:fs/promises";
import { existsSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
@@ -107,94 +108,107 @@ export const BULK_COMPLETION_REFUSAL_AT_VERSION = "0018";
export const MIGRATION_BOOKKEEPING_TABLE = "fusion_schema_migrations";
const __dirname = dirname(fileURLToPath(import.meta.url));
const BASELINE_MIGRATION_PATH = join(__dirname, "migrations", "0000_initial.sql");
/*
FNXC:StandaloneExeMigrations 2026-07-17-13:30:
The bun-compiled standalone `fn` binary runs its bundled code from the virtual
/$bunfs/root filesystem, so the historical `join(__dirname, "migrations")`
resolution points at a path that does not exist on disk (bun --compile does not
embed readFile assets) and every DATABASE_URL boot died with
ENOENT /$bunfs/root/migrations/0000_initial.sql. Resolution order:
1. FUSION_MIGRATIONS_DIR env override — always wins when set (operator escape hatch).
2. join(__dirname, "migrations") — the npm/tsup and desktop layout; kept first
among the probes so nothing changes for existing installs.
3. join(dirname(process.execPath), "migrations") — the standalone-exe layout,
where build.ts / the release tarball stage migrations/ next to the binary.
The existsSync probe (not a runtime-detection heuristic) picks between (2) and
(3): inside the compiled binary the module-relative dir simply does not exist,
while for node-based installs it always does, so npm/desktop behavior is untouched.
*/
function resolveMigrationsDir(): string {
const envDir = process.env.FUSION_MIGRATIONS_DIR;
if (envDir) return envDir;
const moduleDir = join(__dirname, "migrations");
if (existsSync(join(moduleDir, "0000_initial.sql"))) return moduleDir;
const execDir = join(dirname(process.execPath), "migrations");
if (existsSync(join(execDir, "0000_initial.sql"))) return execDir;
// Preserve the historical default (and its historical error message) when
// neither location exists — the readFile ENOENT remains the diagnostic.
return moduleDir;
}
const MIGRATIONS_DIR = resolveMigrationsDir();
const BASELINE_MIGRATION_PATH = join(MIGRATIONS_DIR, "0000_initial.sql");
const AUTOMATION_ISOLATION_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0001_automation_project_isolation.sql",
);
const ANALYTICS_ISOLATION_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0002_analytics_project_isolation.sql",
);
const MONITOR_APPROVAL_ISOLATION_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0003_monitor_approval_project_isolation.sql",
);
const LEGACY_CUTOVER_PRESERVATION_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0004_legacy_cutover_preservation.sql",
);
const MULTI_PROJECT_CUTOVER_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0005_multi_project_cutover.sql",
);
const PROJECT_OWNERSHIP_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0006_project_ownership.sql",
);
const SQLITE_SCHEMA_PARITY_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0007_sqlite_schema_parity.sql",
);
const SESSION_ADVISOR_ENABLED_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0008_session_advisor_enabled.sql",
);
const MISSION_FIX_IDEMPOTENCY_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0009_mission_fix_idempotency.sql",
);
const IMPORT_TRANSLATION_CACHE_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0010_import_translation_cache.sql",
);
const IMPORT_TRANSLATION_CACHE_SCOPE_FIX_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0016_import_translation_cache_scope_fix.sql",
);
const IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0019_import_translation_cache_legacy_partition_backfill.sql",
);
const OWNER_PROJECT_ID_SPLIT_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0011_owner_project_id.sql",
);
const CHAT_SESSION_PINS_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0012_chat_session_pins.sql",
);
const EXECUTOR_TOOL_FAILURE_RETRY_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0013_executor_tool_failure_retry.sql",
);
const EXECUTOR_ESCALATION_ATTEMPT_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0014_executor_escalation_attempt.sql",
);
const GLOBAL_ROUTINES_MIGRATION_PATH = join(
__dirname,
"migrations",
MIGRATIONS_DIR,
"0015_global_routines.sql",
);
const TASK_MERGER_MODEL_LANE_MIGRATION_PATH = join(__dirname, "migrations", "0017_task_merger_model_lane.sql");
const BULK_COMPLETION_REFUSAL_AT_MIGRATION_PATH = join(__dirname, "migrations", "0018_bulk_completion_refusal_at.sql");
const TASK_MERGER_MODEL_LANE_MIGRATION_PATH = join(MIGRATIONS_DIR, "0017_task_merger_model_lane.sql");
const BULK_COMPLETION_REFUSAL_AT_MIGRATION_PATH = join(MIGRATIONS_DIR, "0018_bulk_completion_refusal_at.sql");
/**
* Ensure the migration bookkeeping table exists. Lives in the public schema so