From b011af5ec3be0b6693a22997fbf7f460505dd77c Mon Sep 17 00:00:00 2001 From: Joseph Yaksich Date: Sat, 1 Aug 2026 07:50:09 +0000 Subject: [PATCH] fix: restart Apple container networking via supported API Signed-off-by: Joseph Yaksich --- src/server/channel-computers.ts | 10 ++++++---- test/channel-computers.mjs | 3 ++- test/fake-container.mjs | 1 + 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/server/channel-computers.ts b/src/server/channel-computers.ts index 87942b1..66c59eb 100644 --- a/src/server/channel-computers.ts +++ b/src/server/channel-computers.ts @@ -592,12 +592,14 @@ async function repairAppleGuestNetwork(computer: ChannelComputer): Promise // Avoid repeatedly bouncing shared vmnet while several machines detect // the same fleet-wide outage during one reconciliation pass. if (now() - appleNetworkRepairAt > 30_000) { - if (platform() === "darwin") { - const label = `gui/${process.getuid?.() ?? 501}/com.apple.container.network.container-network-vmnet.default`; - const kicked = await spawnCollected("/bin/launchctl", ["kickstart", "-k", label], { timeoutMs: 30_000 }); - if (kicked.code !== 0) throw new Error(kicked.stderr.toString("utf8").trim() || "Apple shared VM network service could not restart"); + const activeCommands = Number(q1("SELECT COUNT(*) n FROM channel_computer_obligations WHERE kind='command' AND status='active'")?.n || 0); + const activeTurns = Number(q1("SELECT COUNT(*) n FROM agent_turns WHERE state='running'")?.n || 0); + if (terminalSessions.size > 0 || activeCommands > 1 || activeTurns > 1) { + throw new Error("resident computer network is unavailable; automatic repair is waiting for concurrent work to finish"); } + const stopped = await apple(["system", "stop"], { timeoutMs: 90_000 }); const started = await apple(["system", "start"], { timeoutMs: 90_000 }); + if (stopped.code !== 0) throw new Error(stopped.stderr.toString("utf8").trim() || "Apple container services could not stop for network recovery"); if (started.code !== 0) throw new Error(started.stderr.toString("utf8").trim() || "Apple container services could not restart"); appleNetworkRepairAt = now(); } diff --git a/test/channel-computers.mjs b/test/channel-computers.mjs index b5ff9aa..e50442e 100644 --- a/test/channel-computers.mjs +++ b/test/channel-computers.mjs @@ -154,7 +154,8 @@ test("Apple channel-computer contract preserves isolation, files, wakes, archive assert.ok(calls.some((call) => call.includes("-w") && call.includes("/workspace") && call.some((word) => word.includes("/bin/bash")) && call.some((word) => word.includes("-lc"))), "resident commands execute in the correct VM workspace"); assert.equal(db.q1("SELECT disk_bytes FROM channel_computers WHERE channel_id=?", beta.channelId).disk_bytes, computers.MANAGED_CHANNEL_DISK_BYTES, "reported storage is the managed writable allocation, not Apple's host-backed virtual capacity"); const backend = await readFile(join(root, "src", "server", "channel-computers.ts"), "utf8"); - assert.match(backend, /com\.apple\.container\.network\.container-network-vmnet\.default/, "network recovery restarts Apple's installed vmnet launch service"); + assert.match(backend, /apple\(\["system", "stop"\]/, "network recovery uses Apple's supported service stop operation"); + assert.match(backend, /apple\(\["system", "start"\]/, "network recovery uses Apple's supported service start operation"); assert.match(backend, /terminal \? \["-it"\] : pipeInput \? \["-i"\]/, "Apple terminal and streamed-stdin invocations request the exact interactive mode they need"); assert.match(backend, /isolatedInvocation\(\["\/bin\/bash", "-l"\][\s\S]*true\)/, "interactive isolated terminals request an explicit guest login shell"); assert.match(backend, /args: \[\.\.\.words, \.\.\.guestWords\(\.\.\.args\)\]/, "Apple guest argv remains quoted for the runtime's documented second shell parse"); diff --git a/test/fake-container.mjs b/test/fake-container.mjs index 3975e3a..27b03fc 100644 --- a/test/fake-container.mjs +++ b/test/fake-container.mjs @@ -31,6 +31,7 @@ if (args[0] === "system" && args[1] === "status") { process.stdout.write(JSON.stringify({ status: "running" })); process.exit(0); } +if (args[0] === "system" && args[1] === "stop") process.exit(0); if (args[0] === "system" && args[1] === "start") { for (const entry of readdirSync(join(stateRoot, "machines"))) rmSync(join(stateRoot, "machines", entry, ".network-down"), { force: true }); process.exit(0);