clawdie-ai/setup/zfs-metadata.ts
Operator & Codex 975f37f895 feat(install): add versioned setup and system contracts
---
Build: pass | Tests: pass — Tests  2000 passed (2000)
2026-04-27 10:06:44 +02:00

65 lines
1.8 KiB
TypeScript

import { execFileSync } from 'child_process';
export const ZFS_METADATA_PREFIX = 'org.clawdie';
interface ZfsMetadataDeps {
execFileSync: typeof execFileSync;
}
const DEFAULT_DEPS: ZfsMetadataDeps = {
execFileSync,
};
function propertyName(key: string): string {
const normalized = key.trim().toLowerCase();
if (!/^[a-z0-9][a-z0-9-]*$/u.test(normalized)) {
throw new Error(`invalid ZFS metadata key: ${key}`);
}
return `${ZFS_METADATA_PREFIX}:${normalized}`;
}
function normalizeValue(value: string | number | boolean): string {
return String(value).trim();
}
export function setZfsMetadata(
dataset: string,
props: Record<string, string | number | boolean | null | undefined>,
deps: Partial<ZfsMetadataDeps> = {},
): void {
const resolvedDeps = { ...DEFAULT_DEPS, ...deps };
for (const [key, raw] of Object.entries(props)) {
if (raw === null || raw === undefined) continue;
const value = normalizeValue(raw);
if (!value) continue;
resolvedDeps.execFileSync(
'zfs',
['set', `${propertyName(key)}=${value}`, dataset],
{ stdio: ['ignore', 'ignore', 'pipe'] },
);
}
}
export function getZfsMetadata(
dataset: string,
keys: string[],
deps: Partial<ZfsMetadataDeps> = {},
): Record<string, string | null> {
const resolvedDeps = { ...DEFAULT_DEPS, ...deps };
const result: Record<string, string | null> = {};
for (const key of keys) {
const prop = propertyName(key);
try {
const out = resolvedDeps.execFileSync(
'zfs',
['get', '-H', '-o', 'value', prop, dataset],
{ encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] },
);
const value = out.trim();
result[key] = !value || value === '-' ? null : value;
} catch {
result[key] = null;
}
}
return result;
}