diff --git a/pipeline_service/init.py b/pipeline_service/init.py index 147affc..79667b4 100644 --- a/pipeline_service/init.py +++ b/pipeline_service/init.py @@ -375,15 +375,25 @@ def pipeline_unregister_step_type(step_type): # ── Git / Shell 操作(v3.2.1: 主agent devops 能力)── _SHELL_BASE_DIR = '/d/pipeline/workspaces' +_WORKDIR_FALLBACK = '/tmp/pipeline_workspaces' + +def _resolve_workdir(): + """选择可写的工作目录根。""" + if os.path.isdir(_SHELL_BASE_DIR) or os.access(os.path.dirname(_SHELL_BASE_DIR), os.W_OK): + return _SHELL_BASE_DIR + os.makedirs(_WORKDIR_FALLBACK, exist_ok=True) + return _WORKDIR_FALLBACK async def shell_exec(command: str, workdir: str = None, timeout: int = 60): """安全外壳执行。限制在 workspace 目录下,超时自动终止。 Returns: {"rc": exit_code, "stdout": "...", "stderr": "..."} """ - cwd = workdir or _SHELL_BASE_DIR + cwd = workdir or _resolve_workdir() cwd = os.path.abspath(cwd) - if not cwd.startswith(os.path.abspath(_SHELL_BASE_DIR)): + base = os.path.abspath(_SHELL_BASE_DIR) + fallback = os.path.abspath(_WORKDIR_FALLBACK) + if not (cwd.startswith(base) or cwd.startswith(fallback)): return {"rc": -1, "stdout": "", "stderr": f"安全限制:工作目录必须在 {_SHELL_BASE_DIR} 下"} try: proc = await asyncio.create_subprocess_shell( @@ -409,10 +419,10 @@ async def skill_import_git(repo_url: str, skills_dir: str = 'skills'): """ from appPublic.uniqueID import getID repo_name = repo_url.rstrip('/').split('/')[-1].replace('.git', '') or f"repo_{getID()[:8]}" - target = os.path.join(_SHELL_BASE_DIR, repo_name) + target = os.path.join(_resolve_workdir(), repo_name) if not os.path.isdir(target): - r = await shell_exec(f'git clone {repo_url} {target}', workdir=_SHELL_BASE_DIR) + r = await shell_exec(f'git clone {repo_url} {target}', workdir=_resolve_workdir()) if r['rc'] != 0: return {"success": False, "error": f"clone 失败: {r['stderr'][:500]}"}