{
"widgettype": "VBox",
"options": {
"width": "100%",
"height": "100%",
"padding": "24px",
"gap": "16px"
},
"subwidgets": [
{
"widgettype": "Html",
"options": {
"html": "",
"height": "0",
"width": "0"
}
},
{
"widgettype": "Title3",
"options": {
"otext": "机构远程空间配置", "text": "机构远程空间配置", "i18n": true
}
},
{
"widgettype": "Text",
"options": {
"otext": "设置机构的远程工作空间。远程模式下,机构所有用户的工作目录与沙箱命令都在远程主机执行(远程主机需部署 bwrap)。", "text": "设置机构的远程工作空间。远程模式下,机构所有用户的工作目录与沙箱命令都在远程主机执行(远程主机需部署 bwrap)。", "i18n": true,
"cfontsize": 0.9,
"color": "#64748b"
}
},
{
"widgettype": "VBox",
"options": {
"bgcolor": "#fff",
"border": "1px solid #e2e8f0",
"borderRadius": "12px",
"padding": "20px",
"gap": "12px"
},
"subwidgets": [
{
"widgettype": "Form",
"id": "remote_form",
"options": {
"name": "remote_form",
"cols": 2,
"fields": [
{
"name": "mode",
"uitype": "code",
"label": "工作模式",
"cwidth": 12,
"data": [
{
"value": "local",
"text": "本地(bwrap 沙箱)"
},
{
"value": "remote",
"text": "远程(SSH + bwrap 沙箱)"
}
],
"valueField": "value",
"textField": "text"
},
{
"name": "remote_host",
"uitype": "str",
"label": "远程主机",
"cwidth": 24,
"placeholder": "例如 192.168.1.10 或 remote.example.com"
},
{
"name": "remote_port",
"uitype": "str",
"label": "远程端口",
"cwidth": 8,
"value": "22"
},
{
"name": "remote_user",
"uitype": "str",
"label": "远程用户",
"cwidth": 12
},
{
"name": "remote_dir",
"uitype": "str",
"label": "远程目录",
"cwidth": 24,
"placeholder": "相对路径,从远程用户 home 目录开始,例如 work/pipeline"
}
]
}
},
{
"widgettype": "HBox",
"options": {
"gap": "12px"
},
"subwidgets": [
{
"widgettype": "Button",
"id": "save_btn",
"options": {
"label": "保存配置"
},
"binds": [
{
"wid": "self",
"event": "click",
"actiontype": "script",
"target": "self",
"script": "var f=bricks.getWidgetById('remote_form',bricks.app);var v=f?f._getValue():{};var body=new URLSearchParams();body.append('action','set');body.append('owner_type','org');body.append('mode',v.mode||'local');body.append('remote_host',v.remote_host||'');body.append('remote_port',v.remote_port||'22');body.append('remote_user',v.remote_user||'');body.append('remote_dir',v.remote_dir||'');fetch('/pipeline-sdlc/api/work_env.dspy',{method:'POST',body:body}).then(function(r){return r.json()}).then(function(d){if(d.ok){bricks.show_message({title:'成功',message:'远程空间配置已保存'});}else{bricks.show_error({title:'失败',message:d.error||'未知错误'});}});"
}
]
},
{
"widgettype": "Button",
"id": "test_btn",
"options": {
"label": "测试远程连接"
},
"binds": [
{
"wid": "self",
"event": "click",
"actiontype": "script",
"target": "self",
"script": "var f=bricks.getWidgetById('remote_form',bricks.app);var v=f?f._getValue():{};var body=new URLSearchParams();body.append('action','ensure_remote_bwrap');body.append('remote_host',v.remote_host||'');body.append('remote_port',v.remote_port||'22');body.append('remote_user',v.remote_user||'');fetch('/pipeline-sdlc/api/work_env.dspy',{method:'POST',body:body}).then(function(r){return r.json()}).then(function(d){if(d.ok){bricks.show_message({title:'连接成功',message:'远程 bwrap 位置: '+(d.location||'unknown')});}else{bricks.show_error({title:'连接失败',message:d.error||'未知错误'});}});"
}
]
},
{
"widgettype": "Button",
"id": "pubkey_btn",
"options": {
"label": "获取机构公钥"
},
"binds": [
{
"wid": "self",
"event": "click",
"actiontype": "script",
"target": "self",
"script": "fetch('/pipeline-sdlc/api/work_env.dspy?action=get_org_pubkey').then(function(r){return r.json()}).then(function(d){if(d.ok){var t=bricks.getWidgetById('pubkey_text',bricks.app);if(t)t.set_text('机构公钥(复制到远程主机 ~/.ssh/authorized_keys):\\\\n'+d.pubkey);}else{bricks.show_error({title:'失败',message:d.error||'未知错误'});}});"
}
]
},
{
"widgettype": "Button",
"id": "init_btn",
"options": {
"label": "初始化远程环境",
"bgcolor": "#0ea5e9",
"color": "#fff"
},
"binds": [
{
"wid": "self",
"event": "click",
"actiontype": "script",
"target": "self",
"script": "var f=bricks.getWidgetById('remote_form',bricks.app);var v=f?f._getValue():{};if(!v.remote_host||!v.remote_dir){bricks.show_error({title:'缺少配置',message:'请先填写远程主机与远程目录并保存'});return;}var btn=this;btn.disabled=true;var body=new URLSearchParams();body.append('action','init_remote_env');body.append('remote_host',v.remote_host);body.append('remote_port',v.remote_port||'22');body.append('remote_user',v.remote_user||'');body.append('remote_dir',v.remote_dir);fetch('/pipeline-sdlc/api/work_env.dspy',{method:'POST',body:body}).then(function(r){return r.json()}).then(function(d){if(!d.ok){btn.disabled=false;bricks.show_error({title:'初始化失败',message:d.error||'未知错误'});return;}var st=d.steps||{};var sk=st.skills||{};var dp=st.deps_found||{};var dl=st.deps_launch||{};var msg='bwrap: '+((st.bwrap&&st.bwrap.ok)?'✓':'✓')+'
技能同步: '+(sk.synced?sk.synced.join(', '):'✗')+'
待装依赖: '+(dp.specs?dp.specs.length:0)+' 个包(重量级已跳过 '+(dp.heavy_skipped?dp.heavy_skipped.length:0)+' 个)
依赖安装进行中…';bricks.show_message({title:'远程环境初始化',message:msg});var tries=0;function poll(){if(tries++>60){btn.disabled=false;bricks.show_error({title:'依赖安装超时',message:'超过10分钟未返回结果,请稍后重新点击初始化查询'});return;}fetch('/pipeline-sdlc/api/work_env.dspy?action=init_remote_env_status&owner_type=org').then(function(r){return r.json()}).then(function(s){if(s.status==='running'){setTimeout(poll,10000);return;}btn.disabled=false;if(s.ok){bricks.show_message({title:'远程环境初始化完成',message:'依赖安装成功: '+(s.installed?s.installed.length:0)+' 个包
venv: '+(s.venv||'')});}else{bricks.show_error({title:'依赖安装失败',message:(s.error||'')+'
失败包: '+((s.failed||[]).join('; ')||'无')+((s.tail)?'
日志末尾:
'+s.tail.slice(-300):'')});}}).catch(function(e){btn.disabled=false;bricks.show_error({title:'查询失败',message:String(e)});});}setTimeout(poll,8000);}).catch(function(e){btn.disabled=false;bricks.show_error({title:'请求失败',message:String(e)});});"
}
]
}
]
},
{
"widgettype": "Text",
"id": "pubkey_text",
"options": {
"text": "",
"whiteSpace": "pre-wrap",
"cfontsize": 0.85,
"color": "var(--ink-2)"
}
}
]
}
]
}