feat: claude one-shot port from nanobot python codebase (v0.1.4.post4)

This commit is contained in:
Joe Fleming
2026-03-13 08:58:43 -06:00
parent 37c66a1bbf
commit f0252e663e
52 changed files with 5002 additions and 7 deletions

65
src/config/loader.ts Normal file
View File

@@ -0,0 +1,65 @@
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { homedir } from 'node:os';
import { dirname, resolve } from 'node:path';
import { type Config, ConfigSchema } from './types.ts';
const DEFAULT_CONFIG_PATH = resolve(homedir(), '.nanobot', 'config.json');
export function getConfigPath(override?: string): string {
return override ?? process.env['NANOBOT_CONFIG'] ?? DEFAULT_CONFIG_PATH;
}
export function loadConfig(configPath?: string): Config {
const path = getConfigPath(configPath);
if (!existsSync(path)) {
return ConfigSchema.parse({});
}
const raw = readFileSync(path, 'utf8');
let json: unknown;
try {
json = JSON.parse(raw);
} catch {
console.error(`Failed to parse config at ${path}`);
return ConfigSchema.parse({});
}
// Apply NANOBOT_ env var overrides before validation
const merged = applyEnvOverrides(json as Record<string, unknown>);
return ConfigSchema.parse(merged);
}
export function saveConfig(config: Config, configPath?: string): void {
const path = getConfigPath(configPath);
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, JSON.stringify(config, null, 2), 'utf8');
}
/** Resolve `~` in workspace path to the real home directory. */
export function resolveWorkspacePath(raw: string): string {
if (raw.startsWith('~/') || raw === '~') {
return resolve(homedir(), raw.slice(2));
}
return resolve(raw);
}
function applyEnvOverrides(json: Record<string, unknown>): Record<string, unknown> {
const out = structuredClone(json);
const model = process.env['NANOBOT_MODEL'];
if (model) {
const agent = (out['agent'] as Record<string, unknown> | undefined) ?? {};
agent['model'] = model;
out['agent'] = agent;
}
const workspace = process.env['NANOBOT_WORKSPACE'];
if (workspace) {
const agent = (out['agent'] as Record<string, unknown> | undefined) ?? {};
agent['workspacePath'] = workspace;
out['agent'] = agent;
}
return out;
}