# workspace_browse.dspy - 浏览项目工作空间目录树 uid = await get_user() if not uid: uid = 'user-01' dbname = get_module_dbname('pipeline-sdlc') workspace_base = '/d/pipeline/workspaces' import os async with DBPools().sqlorContext(dbname) as sor: recs = await sor.sqlExe( "SELECT current_project_id FROM pipeline_agent_settings WHERE user_id=${u}$", {"u": uid}) pid = getattr(recs[0], 'current_project_id', '') if recs else '' ws_dir = '' if pid: proj = await sor.sqlExe( "SELECT name, org_id, workspace_dir FROM sd_projects WHERE id=${p}$", {"p": pid}) if proj: pname = getattr(proj[0], 'name', '') org_id = getattr(proj[0], 'org_id', '0') or '0' ws = getattr(proj[0], 'workspace_dir', '') or '' if ws and ws.startswith('/'): ws_dir = ws else: ws_dir = workspace_base + '/' + org_id + '/' + pname if not ws_dir or not os.path.isdir(ws_dir): return json.dumps({"error": "工作空间不存在: " + ws_dir, "tree": {"name": "", "children": [], "files": []}}, ensure_ascii=False) def build_tree(path): result = {"name": os.path.basename(path) or path, "path": path, "children": [], "files": []} try: items = sorted(os.listdir(path)) except Exception: return result for item in items: full = os.path.join(path, item) if os.path.isdir(full) and not item.startswith('.') and item != '__pycache__': result["children"].append(build_tree(full)) elif os.path.isfile(full) and not item.startswith('.'): size = os.path.getsize(full) ext = os.path.splitext(item)[1].lower() result["files"].append({ "name": item, "path": full, "size": size, "ext": ext, "is_text": ext in ['.md','.py','.js','.html','.css','.json','.txt', '.xml','.yaml','.yml','.toml','.cfg','.sh','.sql', '.dspy','.ui','.java','.go','.rs','.ts','.tsx','.vue'], "is_media": ext in ['.png','.jpg','.jpeg','.gif','.svg','.mp4','.webm','.mp3','.wav'], }) return result tree = build_tree(ws_dir) return json.dumps({ "workspace": ws_dir, "project_id": pid, "tree": tree, }, ensure_ascii=False, default=str)