import { existsSync } from 'node:fs'; import { createRequire } from 'node:module'; import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; /** Installed application resources and writable project data have different roots. */ export const APPLICATION_ROOT = fileURLToPath(new URL('../../', import.meta.url)); export function resolveVeoRuntime(options: { env?: NodeJS.ProcessEnv; workspaceRoot?: string; flowEntry?: string; applicationRoot?: string; } = {}): { cliRoot: string; flowEntry: string } { const env = options.env ?? process.env; const explicitEntry = options.flowEntry?.trim() || env.VCLAW_VEO_FLOW_ENTRY?.trim(); if (explicitEntry) { const flowEntry = resolve(explicitEntry); return { cliRoot: dirname(flowEntry), flowEntry }; } const explicitRoot = env.VCLAW_VEO_CLI_ROOT?.trim(); // Project storage is data, never an implicit source of executable code. // Legacy sidecars remain supported through an explicit root/entry override. const cliRoot = resolve(explicitRoot || join(options.applicationRoot ?? APPLICATION_ROOT, 'vclaw-cli')); return { cliRoot, flowEntry: join(cliRoot, 'flow.ts') }; } /** Offline prerequisite check: this does not authenticate or contact a provider. */ export function veoRuntimeIssues(runtime: { cliRoot: string; flowEntry: string }): string[] { if (!existsSync(runtime.flowEntry)) { return [`Veo runtime could not find flow.ts at ${runtime.flowEntry}. Set VCLAW_VEO_CLI_ROOT to a bootstrapped vclaw-cli directory.`]; } const require = createRequire(runtime.flowEntry); const missing = ['ora', 'image-size', 'puppeteer-real-browser'].filter((name) => { try { require.resolve(name); return false; } catch { return true; } }); return missing.length ? [ `Missing Veo sidecar dependencies: ${missing.join(', ')}. Run bun install --frozen-lockfile in ${runtime.cliRoot}. ` + 'If the installed package is read-only, copy its vclaw-cli directory to a writable location, install there, and set VCLAW_VEO_CLI_ROOT to that directory.', ] : []; }