clawdie-ai/setup/hosts.ts
Operator & Claude Code 0fcac57e42 Use RUNTIME_ID for setup-side label interpolation
Follow-up to a99f971: covers the remaining ${TENANT_ID} interpolation
sites that produced leading-hyphen / empty-path values on root installs.

- setup/ollama.ts, setup/llama-cpp.ts: preferred jail names
- setup/sanoid.ts: tenant-era home candidate
- setup/hosts.ts: jail-name discovery filter (+ test mock)
- src/telegram-commands.ts: status identity line, suppress empty
  tenant clause on root installs

Root-detection sites that key off TENANT_ID === '' are intentionally
left untouched; the invariant is preserved.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---
Build: FAIL | Tests: FAIL — 15 failed
2026-05-04 06:31:21 +02:00

147 lines
3.7 KiB
TypeScript

import fs from 'fs';
import path from 'path';
import {
AGENT_INTERNAL_DOMAIN,
RUNTIME_ID,
} from '../src/config.js';
import {
renderLocalHostsBlock,
upsertLocalHostsBlock,
} from '../src/local-hosts.js';
import { logger } from '../src/logger.js';
import { getPlatform } from './platform.js';
import { emitStatus } from './status.js';
const HOSTS_FILE = '/etc/hosts';
const BASTILLE_ROOT = '/usr/local/bastille';
function ensureHostsBase(content: string): string {
if (content.trim()) {
return content;
}
return [
'127.0.0.1 localhost',
'::1 localhost',
].join('\n') + '\n';
}
function syncHostsFile(filePath: string): void {
const current = fs.existsSync(filePath)
? fs.readFileSync(filePath, 'utf-8')
: '';
const next = upsertLocalHostsBlock(ensureHostsBase(current));
fs.writeFileSync(filePath, next);
}
function syncJailHosts(jailName: string): boolean {
const hostsFile = path.join(BASTILLE_ROOT, 'jails', jailName, 'root', 'etc', 'hosts');
if (!fs.existsSync(path.dirname(hostsFile))) {
return false;
}
syncHostsFile(hostsFile);
return true;
}
function discoverAgentJails(): string[] {
const jailsRoot = path.join(BASTILLE_ROOT, 'jails');
if (!fs.existsSync(jailsRoot)) {
return [];
}
const normalizedRuntimeId = RUNTIME_ID.replace(/[-_]/g, '');
return fs.readdirSync(jailsRoot, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name)
.filter(
(name) =>
name === RUNTIME_ID ||
name.startsWith(`${RUNTIME_ID}-`) ||
name.startsWith(`${RUNTIME_ID}_`) ||
name.startsWith(`${normalizedRuntimeId}_`),
)
.sort();
}
export function syncLocalHosts(): {
hostFile: string;
syncedTargets: string[];
skippedTargets: string[];
} {
const syncedTargets: string[] = [];
const skippedTargets: string[] = [];
syncHostsFile(HOSTS_FILE);
syncedTargets.push(HOSTS_FILE);
for (const target of discoverAgentJails()) {
if (syncJailHosts(target)) {
syncedTargets.push(target);
} else {
skippedTargets.push(target);
}
}
return {
hostFile: HOSTS_FILE,
syncedTargets,
skippedTargets,
};
}
export async function run(_args: string[]): Promise<void> {
if (getPlatform() !== 'freebsd') {
emitStatus('SETUP_HOSTS', {
STATUS: 'failed',
ERROR: 'unsupported_platform',
LOG: 'logs/setup.log',
});
process.exit(1);
}
try {
const result = syncLocalHosts();
logger.info(
{
internalDomain: AGENT_INTERNAL_DOMAIN,
syncedTargets: result.syncedTargets,
skippedTargets: result.skippedTargets,
},
'Local hosts sync complete',
);
emitStatus('SETUP_HOSTS', {
INTERNAL_DOMAIN: AGENT_INTERNAL_DOMAIN,
HOSTS_FILE: result.hostFile,
SYNCED: result.syncedTargets.join(',') || 'none',
SKIPPED: result.skippedTargets.join(',') || 'none',
STATUS: 'success',
LOG: 'logs/setup.log',
});
console.log('');
console.log('─'.repeat(52));
console.log(` local hosts synced for ${AGENT_INTERNAL_DOMAIN}`);
console.log(` Host file : ${HOSTS_FILE}`);
console.log(` Targets : ${result.syncedTargets.join(', ')}`);
if (result.skippedTargets.length > 0) {
console.log(` Skipped : ${result.skippedTargets.join(', ')}`);
}
console.log('');
console.log(' Managed block preview:');
console.log(renderLocalHostsBlock().trimEnd());
console.log('─'.repeat(52));
console.log('');
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
emitStatus('SETUP_HOSTS', {
INTERNAL_DOMAIN: AGENT_INTERNAL_DOMAIN,
STATUS: 'failed',
ERROR: message,
LOG: 'logs/setup.log',
});
throw error;
}
}