From 5003dc0377a3fb0c461097f3f62970489535f7d4 Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 4 Oct 2026 11:31:44 +0200 Subject: [PATCH] claude-code writes its managed files from a staged file, not /dev/stdin Node hands a child its input over a socket, which /dev/stdin cannot open (ENXIO): on the first assignment nothing under /etc/claude-code was written. --- modules/claude-code/tools/index.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/modules/claude-code/tools/index.ts b/modules/claude-code/tools/index.ts index e6a4224..1bd234e 100644 --- a/modules/claude-code/tools/index.ts +++ b/modules/claude-code/tools/index.ts @@ -8,7 +8,8 @@ // module's `servers` state — every node's MCP server registrations (novox/hq ADR 0201). node.ts holds the // logic. -import { readFileSync, watchFile } from "node:fs"; +import { mkdtempSync, readFileSync, rmSync, watchFile, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; import { spawnSync } from "node:child_process"; import { join } from "node:path"; import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools"; @@ -41,9 +42,15 @@ const writeManaged: WriteManaged = (name, content) => { } catch { /* absent */ } + // From a file, never /dev/stdin: Node hands a child its input over a socket, which /dev/stdin cannot + // open (ENXIO) — found on the first assignment, where nothing under /etc/claude-code was ever written. + const staged = mkdtempSync(join(tmpdir(), "claude-code-")); + const source = join(staged, name); + writeFileSync(source, content, { mode: 0o644 }); const asRoot = process.getuid?.() === 0; - const cmd = asRoot ? ["install", "-D", "-m", "0644", "/dev/stdin", path] : ["sudo", "-n", "install", "-D", "-m", "0644", "/dev/stdin", path]; - const r = spawnSync(cmd[0], cmd.slice(1), { input: content, encoding: "utf8" }); + const cmd = asRoot ? ["install", "-D", "-m", "0644", source, path] : ["sudo", "-n", "install", "-D", "-m", "0644", source, path]; + const r = spawnSync(cmd[0], cmd.slice(1), { encoding: "utf8" }); + rmSync(staged, { recursive: true, force: true }); if (r.status !== 0) { throw new Error(`${name}: could not be written to ${MANAGED_DIR} (${(r.stderr || r.error?.message || "").trim()}); ` + `the module writes there through the operator account's passwordless sudo`);