/*
RFC-0565: dht.status command handler. Reports local DHT node status including
config, cache entries, and peer connectivity. Local-only query — no network
I/O except for checking if the DHT node is reachable.
- Do not start a DHT node — status is read from config and cache files only.
- Status is read-only — it reports config, cache, and peer state without mutation.
- RFC-0565: initial implementation — dht.status handler.
- RFC-1097: step 6 — compass.migrate codemod run
Mechanical v1 to v2 header migration across the workspace: 942 files rewritten — CHANGE_SUMMARY windows collapsed into history, forbidden v1 blocks stripped, KEY_DECISIONS seeded from @ai-invariant comments (5 files) or TODO placeholders (103 files), blocks reordered to canonical order.
- RFC-1097: sweep — werkstatt-engine clean
Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics.
- RFC-1254: drop unused io-migration leftovers
*/
import { join } from "node:path";
import type {
KernelCommandInput,
KernelCommandResult,
KernelRuntimeContext,
} from "@warpgogol/werkstatt-shared/kernel";
import { loadDhtConfig } from "./config.ts";
import { loadCache } from "./cache.ts";
const IDENTITY_FILENAME = "werkstatt.identity.json";
const SWIM_FILENAME = "werkstatt.swim.json";
interface DhtStatusResult {
initialized: boolean;
configPath: string | null;
bindAddr: string | null;
bootstrapNodes: string[];
replicationFactor: number | null;
cacheEntries: number;
cachePath: string;
identityBootstrapped: boolean;
swimConfigured: boolean;
diagnostics?: string[];
}
export async function runDhtStatus(
_input: KernelCommandInput,
context: KernelRuntimeContext,
): Promise> {
const { workspaceRoot } = context;
// Check DHT config
let config = null;
let configPath: string | null = null;
try {
config = await loadDhtConfig(workspaceRoot);
configPath = join(workspaceRoot, "werkstatt.dht.json");
} catch {
// Not initialized
}
// Check identity
let identityBootstrapped = false;
try {
await context.io.readFile(join(workspaceRoot, IDENTITY_FILENAME));
identityBootstrapped = true;
} catch {
// Not bootstrapped
}
// Check SWIM
let swimConfigured = false;
try {
await context.io.readFile(join(workspaceRoot, SWIM_FILENAME));
swimConfigured = true;
} catch {
// Not configured
}
// Load cache
const cache = await loadCache(workspaceRoot);
const cachePath = join(workspaceRoot, "werkstatt.dht.cache.json");
const cacheEntries = Object.keys(cache.entries).length;
if (!config) {
return {
data: {
initialized: false,
configPath: null,
bindAddr: null,
bootstrapNodes: [],
replicationFactor: null,
cacheEntries,
cachePath,
identityBootstrapped,
swimConfigured,
diagnostics: [
`dht.status: DHT not initialized — run dht.node.init first`,
...(!identityBootstrapped
? [`dht.status: identity not bootstrapped — run identity.bootstrap first (RFC-0558)`]
: []),
],
},
exitCode: 0,
summary: `[dht.status] not initialized`,
};
}
return {
data: {
initialized: true,
configPath,
bindAddr: config.bindAddr,
bootstrapNodes: config.bootstrapNodes,
replicationFactor: config.replicationFactor,
cacheEntries,
cachePath,
identityBootstrapped,
swimConfigured,
},
exitCode: 0,
summary: `[dht.status] initialized, bind=${config.bindAddr}, bootstrap=${config.bootstrapNodes.length} nodes, cache=${cacheEntries} entries`,
};
}