myagent-ai 1.5.7 → 1.5.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.5.7",
3
+ "version": "1.5.9",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {
package/web/api_server.py CHANGED
@@ -1570,26 +1570,15 @@ class ApiServer:
1570
1570
  pm = self.core.permission_manager
1571
1571
  if not pm:
1572
1572
  return web.json_response({"error": "权限管理器未初始化"}, status=500)
1573
- from core.permissions import PERMISSION_LABELS, ALL_PERMISSIONS
1574
- defaults = pm.get_permissions("__defaults__") if hasattr(pm, 'get_permissions') else {}
1575
- # 直接读取默认配置
1576
- default_perms = {}
1577
1573
  try:
1578
- all_perms = pm.get_all_permissions()
1579
- default_perms = all_perms.get("defaults", {})
1580
- except Exception:
1581
- default_perms = {p: True for p in ALL_PERMISSIONS}
1582
- agents_perms = {}
1583
- try:
1584
- agents_perms = all_perms.get("agents", {})
1585
- except Exception:
1586
- pass
1587
- return web.json_response({
1588
- "defaults": default_perms,
1589
- "agents": agents_perms,
1590
- "labels": PERMISSION_LABELS,
1591
- "all_permissions": ALL_PERMISSIONS,
1592
- })
1574
+ result = pm.get_config_summary()
1575
+ # 前端期望 labels 键名,映射 permission_labels
1576
+ if "permission_labels" in result and "labels" not in result:
1577
+ result["labels"] = result["permission_labels"]
1578
+ except Exception as e:
1579
+ logger.error(f"获取权限配置失败: {e}")
1580
+ return web.json_response({"error": str(e)}, status=500)
1581
+ return web.json_response(result)
1593
1582
 
1594
1583
  async def handle_set_default_permissions(self, request):
1595
1584
  """PUT /api/permissions/defaults - 更新全局默认权限"""
@@ -1601,7 +1590,7 @@ class ApiServer:
1601
1590
  # data 格式: {"execution": true, "file_read": false, ...}
1602
1591
  if isinstance(data, dict):
1603
1592
  for perm, value in data.items():
1604
- if isinstance(value, bool) and perm in PermissionManager.ALL_PERMISSIONS:
1593
+ if isinstance(value, bool) and perm in pm.ALL_PERMISSIONS:
1605
1594
  pm.set_default_permission(perm, value)
1606
1595
  pm.save()
1607
1596
  return web.json_response({"ok": True})
@@ -1616,7 +1605,7 @@ class ApiServer:
1616
1605
  agent = request.match_info["agent"]
1617
1606
  try:
1618
1607
  perms = pm.get_permissions(agent)
1619
- return web.json_response({"agent": agent, "permissions": perms})
1608
+ return web.json_response({"agent": agent, "permissions": perms.to_dict()})
1620
1609
  except Exception as e:
1621
1610
  return web.json_response({"error": str(e)}, status=500)
1622
1611
 
package/web/ui/index.html CHANGED
@@ -417,10 +417,7 @@ async function openEditAgentModal(path){
417
417
  <div class="form-group"><label>头像 Emoji</label><input id="eaEmoji" value="${escHtml(a.avatar_emoji||'')}" ${isSys?'disabled':''}></div>
418
418
  <div class="form-group"><label>头像颜色</label><div class="flex gap-8 items-center"><input id="eaColor" value="${escHtml(a.avatar_color||'#6366f1')}" type="color" style="width:48px;height:34px;padding:2px" ${isSys?'disabled':''}><input id="eaColorText" value="${escHtml(a.avatar_color||'#6366f1')}" style="flex:1" oninput="$('eaColor').value=this.value" ${isSys?'disabled':''}></div></div>
419
419
  </div>
420
- <div class="form-row">
421
- <div class="form-group"><label>执行模式</label><select id="eaExecMode" ${isSys?'disabled':''}><option value="sandbox" ${a.execution_mode==='sandbox'?'selected':''}>沙盒 (Docker)</option><option value="local" ${a.execution_mode==='local'?'selected':''}>本机</option></select></div>
422
- <div class="form-group"><label>绑定模型</label><select id="eaModelId"><option value="">使用全局默认</option>${modelOpts}</select></div>
423
- </div>
420
+ <div class="form-group"><label>绑定模型</label><select id="eaModelId"><option value="">使用全局默认</option>${modelOpts}</select></div>
424
421
  <div class="form-group"><label>备用模型</label><select id="eaBackupModels" multiple style="min-height:60px">${backupOpts}</select><div style="font-size:11px;color:var(--text2);margin-top:4px">按住 Ctrl/Cmd 多选</div></div>
425
422
  <div class="form-row">
426
423
  <div class="form-group"><label>Agent ID <span style="color:var(--text2)">(只读)</span></label><input id="eaId" value="${escHtml(a.id||'-')}" disabled></div>
@@ -485,7 +482,7 @@ function agentTabSwitch(el,tabId){
485
482
  async function doSaveAgent(path){
486
483
  const r=await api(`/api/agents/${encodeURIComponent(path)}`,{method:'PUT',body:JSON.stringify({
487
484
  description:$('eaDesc').value,avatar_emoji:$('eaEmoji').value,avatar_color:$('eaColorText').value,
488
- execution_mode:$('eaExecMode').value,model_id:$('eaModelId').value,
485
+ model_id:$('eaModelId').value,
489
486
  backup_model_ids:Array.from($('eaBackupModels').selectedOptions).map(o=>o.value),
490
487
  work_dir:$('eaWorkDir').value,system_prompt:$('eaPrompt').value
491
488
  })});
@@ -572,53 +569,91 @@ async function viewSessionMsgs(sid){
572
569
 
573
570
  async function loadAgentPerms(){
574
571
  const path=window._currentEditAgentPath;if(!path)return;
575
- const [agentPerms,globalPerms]=await Promise.all([
572
+ const [agentPerms,globalPerms,agentInfo]=await Promise.all([
576
573
  api('/api/permissions/'+encodeURIComponent(path)),
577
- api('/api/permissions')
574
+ api('/api/permissions'),
575
+ api(`/api/agents/${encodeURIComponent(path)}`)
578
576
  ]);
579
577
  const perms=globalPerms.all_permissions||[];
580
578
  const labels=globalPerms.labels||{};
581
579
  const defaults=globalPerms.defaults||{};
582
580
  const ap=agentPerms.permissions||{};
583
- let html='<h4 style="font-size:14px;color:var(--text2);margin-bottom:12px">Agent 权限配置</h4>';
584
- html+='<p style="color:var(--text2);font-size:12px;margin-bottom:16px">为该 Agent 单独配置权限。未设置的项目将使用全局默认值。</p>';
581
+ const execMode=agentInfo.execution_mode||'sandbox';
582
+ const isSys=!!agentInfo.system;
583
+
584
+ // ── 执行模式卡片 ──
585
+ let html='<div class="card" style="margin-bottom:16px">';
586
+ html+='<h3 style="font-size:14px;color:var(--text2);margin-bottom:8px">⚡ 执行环境</h3>';
587
+ html+='<p style="color:var(--text2);font-size:12px;margin-bottom:12px">选择 Agent 代码执行的运行环境。沙盒模式隔离更安全,本机模式可使用全部本地资源。</p>';
588
+ html+='<div style="display:flex;gap:12px">';
589
+ html+=`<label style="flex:1;display:flex;align-items:center;gap:10px;padding:14px 16px;border-radius:var(--radius);border:2px solid ${execMode==='sandbox'?'var(--primary)':'var(--border)'};cursor:pointer;background:${execMode==='sandbox'?'var(--accent-light)':'transparent'};transition:all .15s" onclick="document.getElementById('permExecSandbox').checked=true;updateExecModeUI()">`;
590
+ html+=`<input type="radio" name="permExecMode" id="permExecSandbox" value="sandbox" ${execMode==='sandbox'?'checked':''} style="display:none">`;
591
+ html+=`<span style="font-size:28px">🐳</span><div><div style="font-weight:600;font-size:14px">沙盒模式</div><div style="font-size:11px;color:var(--text2);margin-top:2px">Docker 容器隔离,安全受限</div></div></label>`;
592
+ html+=`<label style="flex:1;display:flex;align-items:center;gap:10px;padding:14px 16px;border-radius:var(--radius);border:2px solid ${execMode==='local'?'var(--primary)':'var(--border)'};cursor:pointer;background:${execMode==='local'?'var(--accent-light)':'transparent'};transition:all .15s" onclick="document.getElementById('permExecLocal').checked=true;updateExecModeUI()">`;
593
+ html+=`<input type="radio" name="permExecMode" id="permExecLocal" value="local" ${execMode==='local'?'checked':''} style="display:none">`;
594
+ html+=`<span style="font-size:28px">💻</span><div><div style="font-weight:600;font-size:14px">本机模式</div><div style="font-size:11px;color:var(--text2);margin-top:2px">直接在本机运行,完整权限</div></div></label>`;
595
+ html+='</div></div>';
596
+
597
+ // ── 功能权限卡片 ──
598
+ html+='<div class="card" style="margin-bottom:16px">';
599
+ html+='<h3 style="font-size:14px;color:var(--text2);margin-bottom:8px">🔑 功能权限</h3>';
600
+ html+='<p style="color:var(--text2);font-size:12px;margin-bottom:12px">精细控制 Agent 的各项能力。未设置的项目将使用全局默认值。</p>';
585
601
  html+='<div class="grid" style="grid-template-columns:repeat(3,1fr);gap:12px">';
586
602
  for(const p of perms){
587
603
  const label=labels[p]||p;
588
604
  const defVal=defaults[p]!==false;
589
605
  const curVal=ap[p]!==undefined?ap[p]:defVal;
590
606
  const checked=curVal?'checked':'';
591
- const isDefault=ap[p]===undefined?'(默认)':'';
592
- html+=`<div class="form-group" style="display:flex;align-items:center;gap:10px"><label style="flex:1;font-weight:500">${label} <span style="color:var(--text3);font-size:11px">${isDefault}</span></label><input type="checkbox" id="agent_perm_${p}" ${checked} style="width:18px;height:18px;cursor:pointer"></div>`;
607
+ const isDefault=ap[p]===undefined?'<span style=\"color:var(--text3);font-size:11px;margin-left:4px\">(默认)</span>':'';
608
+ html+=`<div class="form-group" style="display:flex;align-items:center;gap:10px"><label style="flex:1;font-weight:500">${label}${isDefault}</label><input type="checkbox" id="agent_perm_${p}" ${checked} style="width:18px;height:18px;cursor:pointer"></div>`;
593
609
  }
610
+ html+='</div></div>';
611
+
612
+ // ── 操作按钮 ──
613
+ html+='<div class="flex gap-8">';
614
+ html+='<button class="btn btn-primary" onclick="saveAgentPermsFromTab()">💾 保存全部</button>';
615
+ html+='<button class="btn btn-ghost" onclick="resetAgentPermsFromTab()">🔄 重置功能权限为默认</button>';
594
616
  html+='</div>';
595
- html+='<div class="flex gap-8 mt-16"><button class="btn btn-primary" onclick="saveAgentPermsFromTab()">保存权限</button>';
596
- html+='<button class="btn btn-ghost" onclick="resetAgentPermsFromTab()">重置为默认</button></div>';
597
617
  $('atPermsContent').innerHTML=html;
598
618
  }
619
+ function updateExecModeUI(){
620
+ const sandbox=document.getElementById('permExecSandbox');
621
+ const local=document.getElementById('permExecLocal');
622
+ if(!sandbox||!local)return;
623
+ const isLocal=local.checked;
624
+ [sandbox.parentElement,local.parentElement].forEach(el=>{
625
+ const radio=el.querySelector('input[type=radio]');
626
+ if(radio.checked){el.style.borderColor='var(--primary)';el.style.background='var(--accent-light)'}
627
+ else{el.style.borderColor='var(--border)';el.style.background='transparent'}
628
+ });
629
+ }
599
630
  async function saveAgentPermsFromTab(){
600
631
  const path=window._currentEditAgentPath;if(!path)return;
632
+ // 1. 保存执行模式
633
+ const execSandbox=document.getElementById('permExecSandbox');
634
+ const execLocal=document.getElementById('permExecLocal');
635
+ const newExecMode=(execSandbox&&execSandbox.checked)?'sandbox':((execLocal&&execLocal.checked)?'local':'sandbox');
636
+ const execR=await api(`/api/agents/${encodeURIComponent(path)}`,{method:'PUT',body:JSON.stringify({execution_mode:newExecMode})});
637
+ if(execR.error){showToast('执行模式保存失败: '+execR.error,'danger');return}
638
+
639
+ // 2. 保存功能权限
601
640
  const [agentPerms,globalPerms]=await Promise.all([
602
641
  api('/api/permissions/'+encodeURIComponent(path)),
603
642
  api('/api/permissions')
604
643
  ]);
605
644
  const perms=globalPerms.all_permissions||[];
606
645
  const defaults=globalPerms.defaults||{};
607
- const ap=agentPerms.permissions||{};
608
646
  const data={};
609
647
  for(const p of perms){
610
648
  const el=document.getElementById('agent_perm_'+p);
611
649
  if(!el)continue;
612
650
  const defVal=defaults[p]!==false;
613
651
  const curVal=el.checked;
614
- // Only include if different from default
615
- if(curVal!==defVal){
616
- data[p]=curVal;
617
- }
652
+ if(curVal!==defVal){data[p]=curVal}
618
653
  }
619
654
  const r=await api('/api/permissions/'+encodeURIComponent(path),{method:'PUT',body:JSON.stringify(data)});
620
- if(r.error){showToast(r.error,'danger');return}
621
- showToast('Agent 权限已保存','success');
655
+ if(r.error){showToast('权限保存失败: '+r.error,'danger');return}
656
+ showToast('权限配置已保存','success');
622
657
  }
623
658
  async function resetAgentPermsFromTab(){
624
659
  const path=window._currentEditAgentPath;if(!path)return;
@@ -821,14 +856,34 @@ async function editAgentPerms(name){
821
856
  const labels=_permsCache.labels||{};
822
857
  const perms=_permsCache.all_permissions||[];
823
858
  const defaults=_permsCache.defaults||{};
824
- // 获取当前 agent 权限
859
+ // 获取当前 agent 权限和 agent 信息(含执行模式)
825
860
  let agentPerms={};
861
+ let agentExecMode='sandbox';
826
862
  try{
827
- const r=await api('/api/permissions/'+encodeURIComponent(name));
863
+ const [r,ai]=await Promise.all([
864
+ api('/api/permissions/'+encodeURIComponent(name)),
865
+ api('/api/agents/'+encodeURIComponent(name))
866
+ ]);
828
867
  agentPerms=r.permissions||{};
868
+ agentExecMode=ai.execution_mode||'sandbox';
829
869
  }catch(e){agentPerms={...defaults}}
830
870
 
831
- let html='<h3>编辑 Agent 权限: '+escHtml(name)+'</h3><div class="grid" style="grid-template-columns:repeat(3,1fr);gap:12px">';
871
+ let html='<h3>编辑 Agent 权限: '+escHtml(name)+'</h3>';
872
+
873
+ // 执行模式选择
874
+ html+='<div style="margin-bottom:16px"><div style="font-size:13px;color:var(--text2);margin-bottom:8px;font-weight:600">⚡ 执行环境</div>';
875
+ html+='<div style="display:flex;gap:12px">';
876
+ html+=`<label id="modalExecSandboxLabel" style="flex:1;display:flex;align-items:center;gap:10px;padding:12px 14px;border-radius:var(--radius);border:2px solid ${agentExecMode==='sandbox'?'var(--primary)':'var(--border)'};cursor:pointer;background:${agentExecMode==='sandbox'?'var(--accent-light)':'transparent'};transition:all .15s" onclick="document.getElementById('modalExecSandbox').checked=true;updateModalExecUI()">`;
877
+ html+=`<input type="radio" name="modalExecMode" id="modalExecSandbox" value="sandbox" ${agentExecMode==='sandbox'?'checked':''} style="display:none">`;
878
+ html+=`<span style="font-size:24px">🐳</span><div><div style="font-weight:600;font-size:13px">沙盒模式</div><div style="font-size:11px;color:var(--text2)">Docker 容器隔离</div></div></label>`;
879
+ html+=`<label id="modalExecLocalLabel" style="flex:1;display:flex;align-items:center;gap:10px;padding:12px 14px;border-radius:var(--radius);border:2px solid ${agentExecMode==='local'?'var(--primary)':'var(--border)'};cursor:pointer;background:${agentExecMode==='local'?'var(--accent-light)':'transparent'};transition:all .15s" onclick="document.getElementById('modalExecLocal').checked=true;updateModalExecUI()">`;
880
+ html+=`<input type="radio" name="modalExecMode" id="modalExecLocal" value="local" ${agentExecMode==='local'?'checked':''} style="display:none">`;
881
+ html+=`<span style="font-size:24px">💻</span><div><div style="font-weight:600;font-size:13px">本机模式</div><div style="font-size:11px;color:var(--text2)">完整本地权限</div></div></label>`;
882
+ html+='</div></div>';
883
+
884
+ // 功能权限
885
+ html+='<div style="font-size:13px;color:var(--text2);margin-bottom:8px;font-weight:600">🔑 功能权限</div>';
886
+ html+='<div class="grid" style="grid-template-columns:repeat(3,1fr);gap:12px">';
832
887
  for(const p of perms){
833
888
  const label=labels[p]||p;
834
889
  const defVal=defaults[p]!==false;
@@ -836,12 +891,33 @@ async function editAgentPerms(name){
836
891
  const checked=curVal?'checked':'';
837
892
  html+=`<div class="form-group" style="display:flex;align-items:center;gap:10px"><label style="flex:1;font-weight:500">${label}</label><input type="checkbox" id="perm_agent_${p}" ${checked} style="width:18px;height:18px;cursor:pointer"></div>`;
838
893
  }
839
- html+='</div><div class="flex gap-8 mt-16"><button class="btn btn-primary" onclick="saveAgentPerms(\''+escHtml(name)+'\')">保存</button><button class="btn btn-ghost" onclick="renderPermissions()">取消</button></div>';
894
+ html+='</div><div class="flex gap-8 mt-16"><button class="btn btn-primary" onclick="saveAgentPerms(\''+escHtml(name)+'\')">💾 保存</button><button class="btn btn-ghost" onclick="renderPermissions()">取消</button></div>';
840
895
  $('modalContainer').innerHTML='<div class="modal-overlay" onclick="closeModal()"><div class="modal" onclick="event.stopPropagation()">'+html+'</div></div>';
841
896
  }
842
897
 
898
+ function updateModalExecUI(){
899
+ const sandbox=document.getElementById('modalExecSandbox');
900
+ const local=document.getElementById('modalExecLocal');
901
+ if(!sandbox||!local)return;
902
+ [sandbox.parentElement,local.parentElement].forEach(el=>{
903
+ const radio=el.querySelector('input[type=radio]');
904
+ if(radio.checked){el.style.borderColor='var(--primary)';el.style.background='var(--accent-light)'}
905
+ else{el.style.borderColor='var(--border)';el.style.background='transparent'}
906
+ });
907
+ }
908
+
843
909
  async function saveAgentPerms(name){
844
910
  if(!_permsCache)return;
911
+ // 1. 保存执行模式
912
+ const modalSandbox=document.getElementById('modalExecSandbox');
913
+ const modalLocal=document.getElementById('modalExecLocal');
914
+ if(modalSandbox||modalLocal){
915
+ const newMode=(modalSandbox&&modalSandbox.checked)?'sandbox':((modalLocal&&modalLocal.checked)?'local':null);
916
+ if(newMode){
917
+ await api('/api/agents/'+encodeURIComponent(name),{method:'PUT',body:JSON.stringify({execution_mode:newMode})});
918
+ }
919
+ }
920
+ // 2. 保存功能权限
845
921
  const perms=_permsCache.all_permissions||[];
846
922
  const data={};
847
923
  for(const p of perms){