clawdie-ai/setup/ollama.ts
Operator & Codex f1dc7ea6df Drop stale jail and agent migration paths (Codex)
Remove completed controlplane agent-id migration, simplify jail-name resolution to current canonical names, and drop SUDO_UID ownership fallback from service setup.

---
Build: pass | Tests: pass — 2370 passed (704 files)
2026-05-10 21:30:17 +02:00

132 lines
3.5 KiB
TypeScript

/**
* setup/ollama.ts — Provision the Ollama jail for local inference.
*
* Creates the jail if missing, installs ollama, enables the service,
* and starts it. Fully idempotent — safe to rerun.
*/
import { execSync, spawnSync } from 'child_process';
import {
FEATURE_OLLAMA,
FEATURE_OLLAMA_HPP,
LOCAL_LLM_PROVIDER,
OLLAMA_INTERNAL_DOMAIN,
OLLAMA_JAIL_IP,
RUNTIME_ID,
SUBNET_BASE,
} from '../src/config.js';
import { logger } from '../src/logger.js';
import { loadPackageList, mountPkgCacheInJail } from './packages.js';
import { getPlatform } from './platform.js';
import { emitStatus } from './status.js';
import { maybeEnableTailscaleInJail } from './tailscale.js';
import {
bastille,
jailExists,
detectFreeBSDRelease,
} from './bastille-helpers.js';
const LOG = 'logs/setup.log';
export async function run(_args: string[]): Promise<void> {
const explicitJailName = (process.env.OLLAMA_JAIL_NAME || '').trim();
const hostname = OLLAMA_INTERNAL_DOMAIN;
const runBastille = (args: string[]) => bastille(...args);
if (!FEATURE_OLLAMA && LOCAL_LLM_PROVIDER !== 'ollama') {
emitStatus('SETUP_OLLAMA', {
STATUS: 'skipped',
REASON: 'feature_disabled',
LOG,
});
logger.info(
'Ollama skipped — FEATURE_OLLAMA disabled and provider not ollama',
);
return;
}
if (getPlatform() !== 'freebsd') {
emitStatus('SETUP_OLLAMA', {
STATUS: 'failed',
ERROR: 'unsupported_platform',
LOG,
});
process.exit(1);
}
try {
const jailName = explicitJailName || `${RUNTIME_ID}-ollama`;
const exists = jailExists(jailName);
if (!exists) {
const release = detectFreeBSDRelease();
const gateway = process.env.WARDEN_GATEWAY || `${SUBNET_BASE}.1`;
const bridge = process.env.WARDEN_BRIDGE || 'warden0';
logger.info(
{ jailName, ip: OLLAMA_JAIL_IP, release },
'Creating Ollama jail',
);
const create = bastille(
'create',
// thin jail (no -T): model assets live outside the base userland.
'-B',
'-g',
gateway,
jailName,
release,
`${OLLAMA_JAIL_IP}/24`,
bridge,
);
if (!create.ok) {
throw new Error(`bastille create failed: ${create.output}`);
}
bastille('config', jailName, 'set', 'host.hostname', hostname);
bastille('restart', jailName);
} else {
logger.info(
{ jailName },
'Ollama jail already exists, skipping creation',
);
}
mountPkgCacheInJail(jailName);
const packages = loadPackageList('ollama-jail.txt');
if (FEATURE_OLLAMA_HPP) packages.push('ollama-hpp');
const pkg = bastille('pkg', jailName, 'install', '-y', ...packages);
if (!pkg.ok) {
logger.warn(
{ output: pkg.output },
'Ollama package install had warnings',
);
}
maybeEnableTailscaleInJail(runBastille, jailName, jailName);
bastille('sysrc', jailName, 'ollama_enable=YES');
const start = bastille('service', jailName, 'ollama', 'restart');
if (!start.ok) {
logger.warn(
{ output: start.output },
'Ollama service start had warnings',
);
}
emitStatus('SETUP_OLLAMA', {
STATUS: 'success',
JAIL_NAME: jailName,
JAIL_IP: OLLAMA_JAIL_IP,
OLLAMA_HPP: FEATURE_OLLAMA_HPP ? 'YES' : 'NO',
LOG,
});
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
emitStatus('SETUP_OLLAMA', {
STATUS: 'failed',
ERROR: message,
LOG,
});
throw error;
}
}