From 1e2f76a79ff417d32ddc14cfb1d38a8bd8c68ae3 Mon Sep 17 00:00:00 2001 From: 134 Date: Tue, 20 Oct 2026 17:43:00 +0800 Subject: [PATCH] fix(canvas-agent): preserve server disk reserve for sandbox tasks --- services/zcode-agent/runner.mjs | 10 ++++++++-- services/zcode-agent/sandbox.mjs | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/services/zcode-agent/runner.mjs b/services/zcode-agent/runner.mjs index d2cb47eb8..2b901e1e3 100644 --- a/services/zcode-agent/runner.mjs +++ b/services/zcode-agent/runner.mjs @@ -1,5 +1,5 @@ import { randomBytes, randomInt, createHash } from 'node:crypto'; -import { mkdir, readFile, writeFile, chmod, chown, readdir, lstat } from 'node:fs/promises'; +import { mkdir, readFile, writeFile, chmod, chown, readdir, lstat, statfs } from 'node:fs/promises'; import { join, resolve } from 'node:path'; import { spawn } from 'node:child_process'; import { createInterface } from 'node:readline'; @@ -35,6 +35,9 @@ export async function runNativeZcode(options) { if (!/^[a-zA-Z0-9-]{1,80}$/.test(options.runId)) throw new Error('无效的 Agent 任务标识'); const manifest = await verifyRuntime(options.runtimeDir); const base = resolve(options.stateDir); await mkdir(base, { recursive: true, mode: 0o711 }); await chmod(base, 0o711); + const disk = await statfs(base); + if (Number(disk.bavail) * Number(disk.bsize) < 512 * 1024 * 1024) + throw new Error('服务器可用磁盘空间不足,未启动 Agent 任务'); const root = join(base, options.runId); await ensurePrivate(root); const home = join(root, 'home'); const workspace = join(root, 'workspace'); const temp = join(root, 'tmp'); for (const path of [home, workspace, temp, join(home, '.zcode/v2'), join(home, '.zcode/cli')]) await ensurePrivate(path); @@ -143,10 +146,13 @@ export async function runNativeZcode(options) { const deadline = setTimeout(() => stop('timeout'), options.timeoutMs || 20 * 60_000); const diskCheck = setInterval(async () => { try { + const disk = await statfs(base); + if (Number(disk.bavail) * Number(disk.bsize) < 256 * 1024 * 1024) throw new Error('disk reserve'); let bytes = 0; async function scan(directory) { for (const entry of await readdir(directory, { withFileTypes: true })) { - const path = join(directory, entry.name); const info = await lstat(path); + const path = join(directory, entry.name); let info; + try { info = await lstat(path); } catch (error) { if (error.code === 'ENOENT') continue; throw error; } if (info.isSymbolicLink()) continue; if (info.isDirectory()) await scan(path); else bytes += info.size; if (bytes > 512 * 1024 * 1024) throw new Error('disk budget'); diff --git a/services/zcode-agent/sandbox.mjs b/services/zcode-agent/sandbox.mjs index 029dc46b5..e9d6ab756 100644 --- a/services/zcode-agent/sandbox.mjs +++ b/services/zcode-agent/sandbox.mjs @@ -14,7 +14,7 @@ export async function sandboxCommand(options) { for (const file of ['/usr/bin/bwrap', '/usr/bin/setpriv', '/usr/bin/systemd-run']) if (!existsSync(file)) throw new Error('缺少 Linux Agent 沙箱组件,禁止宿主直接执行'); const args = ['--quiet', '--scope', '--collect', `--unit=yedir-canvas-${options.runId}`, - '--property=MemoryMax=1073741824', '--property=TasksMax=128', '--property=CPUQuota=200%', + '--property=LimitCORE=0', '--property=MemoryMax=1073741824', '--property=TasksMax=128', '--property=CPUQuota=200%', '/usr/bin/setpriv', `--reuid=${uid}`, `--regid=${uid}`, '--clear-groups', '/usr/bin/bwrap', '--unshare-all', '--new-session', '--die-with-parent', '--cap-drop', 'ALL', '--clearenv', '--ro-bind', '/usr', '/usr', '--ro-bind', '/lib64', '/lib64', '--symlink', 'usr/bin', '/bin', '--symlink', 'usr/lib', '/lib',