# workspace_files.dspy - 目录文件列表(拖拽上传区 + 可选中文件行) import os import json as _json from urllib.parse import unquote def _decode_id(s): for _ in range(4): if '%' not in s: break s2 = unquote(s) if s2 == s: break s = s2 return s folder_id = _decode_id((params_kw or {}).get('id', '').strip()) uid = await get_user() if not uid: uid = 'user-01' session_id = (params_kw or {}).get('session_id', '') or '' pipeline_id = (params_kw or {}).get('pipeline_id', '') or '' dbname = get_module_dbname('pipeline-sdlc') async with DBPools().sqlorContext(dbname) as sor: # 产线隔离:跨产线项目视为无项目(与弹窗入口一致,防绕过) project_dir, _ = await get_project_dir_pl(sor, uid, session_id, pipeline_id) space_dir, _ = await get_space_dir(sor, uid, session_id) # 通用会话(pipeline_id=_generic):锁用户专属目录 _general/{uid}, # 不进入任何项目的工作空间(2026-09-08 用户要求) if pipeline_id == '_generic': _gd = generic_workspace_dir(uid) os.makedirs(_gd, exist_ok=True) project_dir = _gd space_dir = _gd full_dir = resolve_workspace_path(project_dir, space_dir, folder_id) if not os.path.isdir(full_dir): return {"widgettype": "Text", "options": {"otext": "目录不可用: ${p0}", "text": "目录不可用: ${p0}", "i18n": True, "i18n_params": {"p0": str(folder_id)}, "cfontsize": 0.9}} # 上传端点必须与列表端点同参数:session_id + pipeline_id 都要透传。 # 只传 session_id 的旧代码在通用助手(pipeline_id=_generic、无 session_id)下 # 让 workspace_upload.dspy 走不到 _generic 分支,解析到别的项目目录 → # 上传报「目标目录不存在」(2026-09-17 实测根因)。 _uq = [] if session_id: _uq.append("session_id=" + session_id) if pipeline_id: _uq.append("pipeline_id=" + pipeline_id) upload_url = entire_url("/pipeline-sdlc/api/workspace_upload.dspy") + (("?" + "&".join(_uq)) if _uq else "") # 拖拽上传脚本:读取文件 → base64 → form-encoded POST → 刷新浏览器 upload_script = ( "var fs=event.params.files;" "if(!fs||!fs.length)return;" "for(var i=0;i