koishi-plugin-new-auth 0.4.0 → 0.4.4

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/README.md CHANGED
@@ -48,6 +48,10 @@ newauth.role.create <id> <name> [scopeType]
48
48
  newauth.member.add <roleId> <uid> [scope]
49
49
  newauth.member.remove <roleId> <uid> [scope]
50
50
  newauth.role.copy <sourceRoleId> <targetRoleId> [scope]
51
+ newauth.guild.commands [query]
52
+ newauth.guild.allow <roleId> <command>
53
+ newauth.guild.deny <roleId> <command>
54
+ newauth.guild.inherit <roleId> <command>
51
55
  newauth.plugin.assign <plugin> <target> [scope]
52
56
  ```
53
57
 
@@ -62,6 +66,8 @@ newauth.plugin.assign <plugin> <target> [scope]
62
66
 
63
67
  `newauth.assign` 与待配置 WebUI 的角色勾选行为一致:列出的角色写为允许,其他角色在该作用域下写为关闭,并把待配置指令变为已配置。
64
68
 
69
+ `newauth.guild.*` 是当前群的自治入口,只能由 Bot 管理员或当前群群主使用,只能修改当前群、已配置且允许群内自治的指令,不能修改全局权限或 Bot 管理员角色。
70
+
65
71
  ## 内置角色
66
72
 
67
73
  ```txt
@@ -83,7 +89,7 @@ guest
83
89
  - 以角色为入口的指令权限编辑;
84
90
  - 全局默认和单群作用域切换;
85
91
  - 待配置指令审查和角色勾选分配;
86
- - 旧 `authority` 建议与 AI 风格自动分配建议;
92
+ - 旧 `authority` 建议与基于 ChatLuna 默认模型的 AI 分配审查表;
87
93
  - 指令状态和群内自治开关;
88
94
  - 自定义角色创建、显式成员管理;
89
95
  - 从其他角色复制权限;
@@ -91,4 +97,4 @@ guest
91
97
 
92
98
  WebUI 是 Koishi 实例管理员界面,Console 入口和所有事件都要求 `authority: 4`。群主自治只应影响允许自治的单群指令,不等于进入实例管理面板。
93
99
 
94
- 自动分配策略默认保守:实例级或高危操作只给 `bot-admin`,群管理操作给 `guild-admin` `guild-owner`,普通用户功能给 `guild-member` 及以上,无法判断时回退到旧 `authority` 建议。
100
+ AI 自动分配不会按关键词硬编码推断。安装并启用 ChatLuna 且设置默认模型后,newauth 会把指令元数据和角色列表交给 ChatLuna 默认模型,让模型返回允许的角色;WebUI 会先显示审查表并填入勾选项,只有管理员点击保存后才写入策略。模型不可用或返回无效时回退到旧 `authority` 建议。
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{defineComponent as _e,ref as d,computed as c,watch as W,openBlock as s,createElementBlock as u,createElementVNode as t,toDisplayString as o,withDirectives as p,Fragment as k,renderList as y,vModelSelect as C,normalizeClass as w,createCommentVNode as L,withModifiers as ee,vModelText as T,vModelCheckbox as Ce,createTextVNode as F}from"vue";import{store as le,send as b}from"@koishijs/client";const we={class:"new-auth-page"},Ie={class:"topbar"},$e={class:"top-actions"},Se=["value"],Pe=["disabled"],Re=["disabled"],Ve={class:"tabs"},Ae={key:0,class:"error"},Ue={key:1,class:"role-layout"},xe={class:"role-list"},Me=["onClick"],Te=["disabled"],Oe={key:0,class:"detail"},Ge={class:"detail-head"},Ne={class:"detail-tools"},Be=["value"],De=["disabled"],Ee={class:"check"},Le={class:"member-panel"},Fe={class:"member-list"},Ke=["disabled","onClick"],qe=["value"],ze=["disabled"],je={class:"filters"},He={class:"command-list"},Je={class:"command-main"},Qe={class:"state-buttons"},We=["onClick"],Xe=["onClick"],Ye=["onClick"],Ze={key:2,class:"pending-grid"},el={class:"role-picker"},ll=["checked","onChange"],tl={class:"row-actions"},nl=["disabled","onClick"],ol=["disabled","onClick"],sl=["disabled","onClick"],ul={key:0,class:"empty"},il={key:3,class:"commands-table"},al={class:"filters"},dl={class:"plugin-bulk"},rl=["value"],vl=["disabled"],cl=["disabled"],pl=["disabled"],gl=["disabled"],bl={class:"command-main"},fl={class:"command-controls"},kl={class:"check"},yl=["checked","onChange"],hl=["value","onChange"],ml=_e({__name:"new-auth",setup(A){const f=d("roles"),a=d("global"),v=d(""),h=d(""),I=d("all"),K=d(false),r=d(false),O=d(""),$=d(""),U=d(""),q=d("guild"),x=d(""),G=d("global"),N=d(""),m=d(""),_=d({}),S=c(()=>le.newauth||{roles:[],commands:[],policies:[],members:[],scopes:[{id:"global",name:"全局默认"}],pendingCount:0}),P=c(()=>S.value.roles),B=c(()=>S.value.commands),z=c(()=>S.value.policies),te=c(()=>S.value.members),ne=c(()=>{var n;return(n=S.value.scopes)!=null&&n.length?S.value.scopes:[{id:"global",name:"全局默认"}]}),R=c(()=>B.value.filter(n=>n.status==="pending")),V=c(()=>P.value.find(n=>n.id===v.value)),X=c(()=>te.value.filter(n=>n.roleId===v.value)),oe=c(()=>P.value.filter(n=>n.id!==v.value)),se=c(()=>[...new Set(B.value.map(n=>n.plugin))].sort());W(P,n=>{!v.value&&n.length&&(v.value=n[0].id)},{immediate:true}),W(a,n=>{G.value=n==="global"?"global":n}),W([R,z,a],()=>{for(const n of R.value)D(n)},{immediate:true});const Y=c(()=>{const n=h.value.toLowerCase();return B.value.filter(l=>I.value!=="all"&&l.status!==I.value?false:n?[l.id,l.name,l.commandPath,l.plugin,l.description].some(e=>e==null?void 0:e.toLowerCase().includes(n)):true)}),ue=c(()=>V.value?Y.value.filter(n=>{var l,e;return K.value||((l=V.value)==null?void 0:l.id)==="bot-admin"||((e=V.value)==null?void 0:e.scopeType)==="global"?true:n.allowGuildOverride}):[]);function j(n){const l=z.value.find(e=>e.scope===a.value&&e.roleId===v.value&&e.commandId===n);return(l==null?void 0:l.state)||"inherit"}async function g(n){r.value=true,O.value="";try{await n}catch(l){O.value=l instanceof Error?l.message:String(l)}finally{r.value=false}}async function ie(){return g((async()=>{const n=await b("newauth/getData");le.newauth=n})())}function H(n,l){return g(b("newauth/setPolicy",{scope:a.value,roleId:v.value,commandId:n,state:l}))}function M(n){return`${a.value}:${n}`}function ae(n){const l=z.value.filter(e=>e.scope===a.value&&e.commandId===n.id&&e.state==="allow").map(e=>e.roleId);return l.length?l:[...n.autoAssign.roles]}function D(n){const l=M(n.id);_.value[l]||(_.value[l]=ae(n))}function de(n,l){return D(n),_.value[M(n.id)].includes(l)}function re(n,l,e){D(n);const i=M(n.id),J=e.target.checked,Q=new Set(_.value[i]);J?Q.add(l):Q.delete(l),_.value[i]=[...Q]}function Z(n,l){_.value[M(n.id)]=[...l==="legacy"?n.suggestion.roles:n.autoAssign.roles]}function ve(n){return D(n),g(b("newauth/configureCommandRoles",{commandId:n.id,scope:a.value,roleIds:_.value[M(n.id)]}))}function ce(){return g(b("newauth/autoAssignPending",{scope:a.value,mode:"advisor"}))}function pe(n,l){const e=l.target;return g(b("newauth/setGuildOverride",{commandId:n.id,allowGuildOverride:e.checked}))}function ge(n,l){const e=l.target;return g(b("newauth/setCommandStatus",{commandId:n.id,status:e.value}))}function be(){return g((async()=>{await b("newauth/createRole",{id:$.value,name:U.value,scopeType:q.value}),v.value=$.value,$.value="",U.value=""})())}function fe(){return g((async()=>{await b("newauth/addMember",{roleId:v.value,uid:x.value,scope:G.value}),x.value=""})())}function ke(n){return g(b("newauth/removeMember",{roleId:n.roleId,uid:`${n.platform}:${n.userId}`,scope:n.scope}))}function ye(){return g(b("newauth/copyRolePolicies",{sourceRoleId:N.value,targetRoleId:v.value,scope:a.value}))}function he(n){return`${n.roleId}:${n.platform}:${n.userId}:${n.scope}`}function E(n){return g(b("newauth/assignPluginCommands",{plugin:m.value,scope:a.value,target:n}))}function me(n){return n==="global"?"全局":"群内"}return(n,l)=>(s(),u("main",we,[t("header",Ie,[t("div",null,[l[20]||(l[20]=t("h1",null,"新权限",-1)),t("p",null,o(P.value.length)+" 个角色 / "+o(B.value.length)+" 个指令 / "+o(R.value.length)+" 个待配置",1)]),t("div",$e,[p(t("select",{"onUpdate:modelValue":l[0]||(l[0]=e=>a.value=e),class:"scope-select"},[(s(true),u(k,null,y(ne.value,e=>(s(),u("option",{key:e.id,value:e.id},o(e.name),9,Se))),128))],512),[[C,a.value]]),t("button",{class:"primary",disabled:r.value||!R.value.length,onClick:ce}," AI 自动分配待配置 ",8,Pe),t("button",{disabled:r.value,onClick:ie},"刷新",8,Re)])]),t("nav",Ve,[t("button",{class:w({active:f.value==="roles"}),onClick:l[1]||(l[1]=e=>f.value="roles")},"角色",2),t("button",{class:w({active:f.value==="pending"}),onClick:l[2]||(l[2]=e=>f.value="pending")},"待配置",2),t("button",{class:w({active:f.value==="commands"}),onClick:l[3]||(l[3]=e=>f.value="commands")},"指令",2)]),O.value?(s(),u("p",Ae,o(O.value),1)):L("v-if",true),f.value==="roles"?(s(),u("section",Ue,[t("aside",xe,[(s(true),u(k,null,y(P.value,e=>(s(),u("button",{key:e.id,class:w(["role-item",{active:e.id===v.value}]),onClick:i=>v.value=e.id},[t("span",null,[t("strong",null,o(e.name),1),t("small",null,o(e.builtin?"内置角色":"自定义角色")+" / "+o(me(e.scopeType)),1)]),t("em",null,o(e.allowCount),1)],10,Me))),128)),t("form",{class:"role-create",onSubmit:ee(be,["prevent"])},[p(t("input",{"onUpdate:modelValue":l[4]||(l[4]=e=>$.value=e),placeholder:"custom:music-admin"},null,512),[[T,$.value,void 0,{trim:true}]]),p(t("input",{"onUpdate:modelValue":l[5]||(l[5]=e=>U.value=e),placeholder:"角色名称"},null,512),[[T,U.value,void 0,{trim:true}]]),p(t("select",{"onUpdate:modelValue":l[6]||(l[6]=e=>q.value=e)},[...l[21]||(l[21]=[t("option",{value:"guild"},"群内角色",-1),t("option",{value:"global"},"全局角色",-1)])],512),[[C,q.value]]),t("button",{class:"primary",disabled:r.value||!$.value||!U.value},"创建角色",8,Te)],32)]),V.value?(s(),u("section",Oe,[t("div",Ge,[t("div",null,[t("h2",null,o(V.value.name),1),t("p",null,o(V.value.id),1)]),t("div",Ne,[p(t("select",{"onUpdate:modelValue":l[7]||(l[7]=e=>N.value=e)},[l[22]||(l[22]=t("option",{disabled:"",value:""},"复制来源",-1)),(s(true),u(k,null,y(oe.value,e=>(s(),u("option",{key:e.id,value:e.id},o(e.name),9,Be))),128))],512),[[C,N.value]]),t("button",{disabled:r.value||!N.value,onClick:ye},"复制权限",8,De),t("label",Ee,[p(t("input",{type:"checkbox","onUpdate:modelValue":l[8]||(l[8]=e=>K.value=e)},null,512),[[Ce,K.value]]),l[23]||(l[23]=F(" 审计模式 ",-1))])])]),t("section",Le,[t("div",null,[l[24]||(l[24]=t("strong",null,"成员",-1)),t("small",null,o(X.value.length)+" 个显式成员",1)]),t("div",Fe,[(s(true),u(k,null,y(X.value,e=>(s(),u("span",{key:he(e),class:"member-chip"},[F(o(e.platform)+":"+o(e.userId)+" / "+o(e.scope)+" ",1),t("button",{disabled:r.value,onClick:i=>ke(e)},"移除",8,Ke)]))),128))]),t("form",{class:"member-add",onSubmit:ee(fe,["prevent"])},[p(t("input",{"onUpdate:modelValue":l[9]||(l[9]=e=>x.value=e),placeholder:"platform:userId"},null,512),[[T,x.value,void 0,{trim:true}]]),p(t("select",{"onUpdate:modelValue":l[10]||(l[10]=e=>G.value=e)},[l[25]||(l[25]=t("option",{value:"global"},"global",-1)),a.value!=="global"?(s(),u("option",{key:0,value:a.value},o(a.value),9,qe)):L("v-if",true)],512),[[C,G.value]]),t("button",{class:"primary",disabled:r.value||!x.value},"添加成员",8,ze)],32)]),t("div",je,[p(t("input",{"onUpdate:modelValue":l[11]||(l[11]=e=>h.value=e),placeholder:"搜索指令、插件、描述"},null,512),[[T,h.value,void 0,{trim:true}]]),p(t("select",{"onUpdate:modelValue":l[12]||(l[12]=e=>I.value=e)},[...l[26]||(l[26]=[t("option",{value:"all"},"全部状态",-1),t("option",{value:"pending"},"待配置",-1),t("option",{value:"configured"},"已配置",-1),t("option",{value:"disabled"},"已禁用",-1)])],512),[[C,I.value]])]),t("div",He,[(s(true),u(k,null,y(ue.value,e=>(s(),u("article",{key:e.id,class:"command-row"},[t("div",Je,[t("strong",null,o(e.name),1),t("span",null,o(e.commandPath),1),t("small",null,o(e.plugin)+" / legacy "+o(e.legacyAuthority)+" / "+o(e.suggestion.label),1)]),t("div",Qe,[t("button",{class:w({active:j(e.id)==="inherit"}),onClick:i=>H(e.id,"inherit")}," 继承 ",10,We),t("button",{class:w({active:j(e.id)==="allow"}),onClick:i=>H(e.id,"allow")}," 开 ",10,Xe),t("button",{class:w({active:j(e.id)==="deny"}),onClick:i=>H(e.id,"deny")}," 关 ",10,Ye)])]))),128))])])):L("v-if",true)])):f.value==="pending"?(s(),u("section",Ze,[(s(true),u(k,null,y(R.value,e=>(s(),u("article",{key:e.id,class:"pending-item"},[t("div",null,[t("strong",null,o(e.name),1),t("span",null,o(e.commandPath),1),t("small",null,o(e.plugin)+" / legacy "+o(e.legacyAuthority),1)]),t("dl",null,[l[27]||(l[27]=t("dt",null,"旧建议",-1)),t("dd",null,o(e.suggestion.label),1),l[28]||(l[28]=t("dt",null,"AI 建议",-1)),t("dd",null,o(e.autoAssign.label)+":"+o(e.autoAssign.reason),1)]),t("div",el,[(s(true),u(k,null,y(P.value,i=>(s(),u("label",{key:`${e.id}:${i.id}`,class:"check"},[t("input",{type:"checkbox",checked:de(e,i.id),onChange:J=>re(e,i.id,J)},null,40,ll),F(" "+o(i.name),1)]))),128))]),t("div",tl,[t("button",{disabled:r.value,onClick:i=>Z(e,"legacy")},"填入旧建议",8,nl),t("button",{disabled:r.value,onClick:i=>Z(e,"advisor")},"填入 AI 建议",8,ol),t("button",{class:"primary",disabled:r.value,onClick:i=>ve(e)},"保存分配",8,sl)])]))),128)),R.value.length?L("v-if",true):(s(),u("p",ul,"没有待配置指令。"))])):(s(),u("section",il,[t("div",al,[p(t("input",{"onUpdate:modelValue":l[13]||(l[13]=e=>h.value=e),placeholder:"搜索指令、插件、描述"},null,512),[[T,h.value,void 0,{trim:true}]]),p(t("select",{"onUpdate:modelValue":l[14]||(l[14]=e=>I.value=e)},[...l[29]||(l[29]=[t("option",{value:"all"},"全部状态",-1),t("option",{value:"pending"},"待配置",-1),t("option",{value:"configured"},"已配置",-1),t("option",{value:"disabled"},"已禁用",-1)])],512),[[C,I.value]])]),t("div",dl,[p(t("select",{"onUpdate:modelValue":l[15]||(l[15]=e=>m.value=e)},[l[30]||(l[30]=t("option",{disabled:"",value:""},"选择插件",-1)),(s(true),u(k,null,y(se.value,e=>(s(),u("option",{key:e,value:e},o(e),9,rl))),128))],512),[[C,m.value]]),t("button",{disabled:r.value||!m.value,onClick:l[16]||(l[16]=e=>E("legacy"))},"采用插件建议",8,vl),t("button",{disabled:r.value||!m.value,onClick:l[17]||(l[17]=e=>E("guild-member"))},"给群成员",8,cl),t("button",{disabled:r.value||!m.value,onClick:l[18]||(l[18]=e=>E("guild-admin"))},"给群管理员",8,pl),t("button",{disabled:r.value||!m.value,onClick:l[19]||(l[19]=e=>E("bot-admin"))},"仅 Bot 管理员",8,gl)]),(s(true),u(k,null,y(Y.value,e=>(s(),u("article",{key:e.id,class:"command-row"},[t("div",bl,[t("strong",null,o(e.name),1),t("span",null,o(e.id),1),t("small",null,o(e.plugin)+" / "+o(e.commandPath)+" / "+o(e.autoAssign.label),1)]),t("div",fl,[t("label",kl,[t("input",{type:"checkbox",checked:e.allowGuildOverride,onChange:i=>pe(e,i)},null,40,yl),l[31]||(l[31]=F(" 群内自治 ",-1))]),t("select",{value:e.status,onChange:i=>ge(e,i)},[...l[32]||(l[32]=[t("option",{value:"pending"},"待配置",-1),t("option",{value:"configured"},"已配置",-1),t("option",{value:"disabled"},"已禁用",-1)])],40,hl)])]))),128))]))]))}}),_l=(A,f)=>{const a=A.__vccOpts||A;for(const[v,h]of f)a[v]=h;return a},Cl=_l(ml,[["__scopeId","data-v-6014eba6"]]),$l=A=>{A.page({name:"新权限",path:"/new-auth",icon:"⌗",fields:["newauth"],authority:4,component:Cl})};export{$l as default};
1
+ import{defineComponent as Ae,ref as d,computed as c,watch as Y,openBlock as s,createElementBlock as u,createElementVNode as t,toDisplayString as o,withDirectives as p,Fragment as h,renderList as k,vModelSelect as I,normalizeClass as $,createCommentVNode as T,withModifiers as ne,vModelText as N,vModelCheckbox as Re,createTextVNode as K}from"vue";import{store as oe,send as b}from"@koishijs/client";const Pe={class:"new-auth-page"},Ve={class:"topbar"},Ue={class:"top-actions"},xe=["value"],Me=["disabled"],Te=["disabled"],Ne={class:"tabs"},Oe={key:0,class:"error"},Ge={key:1,class:"role-layout"},Be={class:"role-list"},De=["onClick"],Ee=["disabled"],Le={key:0,class:"detail"},Fe={class:"detail-head"},Ke={class:"detail-tools"},je=["value"],qe=["disabled"],ze={class:"check"},He={class:"member-panel"},Je={class:"member-list"},Qe=["disabled","onClick"],We=["value"],Xe=["disabled"],Ye={class:"filters"},Ze={class:"command-list"},el={class:"command-main"},ll={class:"state-buttons"},tl=["onClick"],nl=["onClick"],ol=["onClick"],sl={key:2,class:"pending-panel"},ul={key:0,class:"review-table"},il={class:"role-picker"},al=["checked","onChange"],dl={class:"row-actions"},rl=["disabled","onClick"],vl=["disabled","onClick"],cl=["disabled","onClick"],pl={key:1,class:"empty"},gl={key:3,class:"commands-table"},bl={class:"filters"},fl={class:"plugin-bulk"},hl=["value"],kl=["disabled"],yl=["disabled"],ml=["disabled"],wl=["disabled"],_l={class:"command-main"},Cl={class:"command-controls"},Sl={class:"check"},Il=["checked","onChange"],$l=["value","onChange"],Al=Ae({__name:"new-auth",setup(U){const f=d("roles"),a=d("global"),v=d(""),_=d(""),A=d("all"),j=d(false),r=d(false),O=d(""),R=d(""),x=d(""),q=d("guild"),M=d(""),G=d("global"),B=d(""),C=d(""),y=d({}),z=d({}),m=c(()=>oe.newauth||{roles:[],commands:[],policies:[],members:[],scopes:[{id:"global",name:"全局默认"}],pendingCount:0,autoAssignAvailable:false}),S=c(()=>m.value.roles),D=c(()=>m.value.commands),H=c(()=>m.value.policies),se=c(()=>m.value.members),ue=c(()=>{var n;return(n=m.value.scopes)!=null&&n.length?m.value.scopes:[{id:"global",name:"全局默认"}]}),w=c(()=>D.value.filter(n=>n.status==="pending")),Z=c(()=>w.value.filter(n=>z.value[n.id])),P=c(()=>S.value.find(n=>n.id===v.value)),ee=c(()=>se.value.filter(n=>n.roleId===v.value)),ie=c(()=>S.value.filter(n=>n.id!==v.value)),ae=c(()=>[...new Set(D.value.map(n=>n.plugin))].sort());Y(S,n=>{!v.value&&n.length&&(v.value=n[0].id)},{immediate:true}),Y(a,n=>{G.value=n==="global"?"global":n}),Y([w,H,a],()=>{for(const n of w.value)E(n)},{immediate:true});const le=c(()=>{const n=_.value.toLowerCase();return D.value.filter(l=>A.value!=="all"&&l.status!==A.value?false:n?[l.id,l.name,l.commandPath,l.plugin,l.description].some(e=>e==null?void 0:e.toLowerCase().includes(n)):true)}),de=c(()=>P.value?le.value.filter(n=>{var l,e;return j.value||((l=P.value)==null?void 0:l.id)==="bot-admin"||((e=P.value)==null?void 0:e.scopeType)==="global"?true:n.allowGuildOverride}):[]);function J(n){const l=H.value.find(e=>e.scope===a.value&&e.roleId===v.value&&e.commandId===n);return(l==null?void 0:l.state)||"inherit"}async function g(n){r.value=true,O.value="";try{await n}catch(l){O.value=l instanceof Error?l.message:String(l)}finally{r.value=false}}async function re(){return g((async()=>{const n=await b("newauth/getData");oe.newauth=n})())}function Q(n,l){return g(b("newauth/setPolicy",{scope:a.value,roleId:v.value,commandId:n,state:l}))}function V(n){return`${a.value}:${n}`}function ve(n){const l=H.value.filter(e=>e.scope===a.value&&e.commandId===n.id&&e.state==="allow").map(e=>e.roleId);return l.length?l:[...n.suggestion.roles]}function E(n){const l=V(n.id);y.value[l]||(y.value[l]=ve(n))}function ce(n,l){return E(n),y.value[V(n.id)].includes(l)}function pe(n,l,e){E(n);const i=V(n.id),W=e.target.checked,X=new Set(y.value[i]);W?X.add(l):X.delete(l),y.value[i]=[...X]}function ge(n,l){y.value[V(n.id)]=[...n.suggestion.roles]}function L(n){return z.value[n.id]||n.autoAssign}function be(n){return L(n).roles.map(l=>{var e;return((e=S.value.find(i=>i.id===l))==null?void 0:e.name)||l}).join("、")||"无"}function te(n,l){z.value[n.id]=l,y.value[V(n.id)]=[...l.roles]}function fe(n){return g((async()=>{const l=await b("newauth/generateAiSuggestion",{commandId:n.id});te(n,l)})())}function he(){return g((async()=>{const n=await b("newauth/generateAiSuggestions");for(const l of w.value){const e=n.suggestions[l.id];e&&te(l,e)}})())}function ke(n){return E(n),g(b("newauth/configureCommandRoles",{commandId:n.id,scope:a.value,roleIds:y.value[V(n.id)]}))}function ye(n,l){const e=l.target;return g(b("newauth/setGuildOverride",{commandId:n.id,allowGuildOverride:e.checked}))}function me(n,l){const e=l.target;return g(b("newauth/setCommandStatus",{commandId:n.id,status:e.value}))}function we(){return g((async()=>{await b("newauth/createRole",{id:R.value,name:x.value,scopeType:q.value}),v.value=R.value,R.value="",x.value=""})())}function _e(){return g((async()=>{await b("newauth/addMember",{roleId:v.value,uid:M.value,scope:G.value}),M.value=""})())}function Ce(n){return g(b("newauth/removeMember",{roleId:n.roleId,uid:`${n.platform}:${n.userId}`,scope:n.scope}))}function Se(){return g(b("newauth/copyRolePolicies",{sourceRoleId:B.value,targetRoleId:v.value,scope:a.value}))}function Ie(n){return`${n.roleId}:${n.platform}:${n.userId}:${n.scope}`}function F(n){return g(b("newauth/assignPluginCommands",{plugin:C.value,scope:a.value,target:n}))}function $e(n){return n==="global"?"全局":"群内"}return(n,l)=>(s(),u("main",Pe,[t("header",Ve,[t("div",null,[l[20]||(l[20]=t("h1",null,"新权限",-1)),t("p",null,o(S.value.length)+" 个角色 / "+o(D.value.length)+" 个指令 / "+o(w.value.length)+" 个待配置",1)]),t("div",Ue,[p(t("select",{"onUpdate:modelValue":l[0]||(l[0]=e=>a.value=e),class:"scope-select"},[(s(true),u(h,null,k(ue.value,e=>(s(),u("option",{key:e.id,value:e.id},o(e.name),9,xe))),128))],512),[[I,a.value]]),t("button",{class:"primary",disabled:r.value||!w.value.length||!m.value.autoAssignAvailable,onClick:he}," AI 生成审查表 ",8,Me),t("button",{disabled:r.value,onClick:re},"刷新",8,Te)])]),t("nav",Ne,[t("button",{class:$({active:f.value==="roles"}),onClick:l[1]||(l[1]=e=>f.value="roles")},"角色",2),t("button",{class:$({active:f.value==="pending"}),onClick:l[2]||(l[2]=e=>f.value="pending")},"待配置",2),t("button",{class:$({active:f.value==="commands"}),onClick:l[3]||(l[3]=e=>f.value="commands")},"指令",2)]),O.value?(s(),u("p",Oe,o(O.value),1)):T("v-if",true),f.value==="roles"?(s(),u("section",Ge,[t("aside",Be,[(s(true),u(h,null,k(S.value,e=>(s(),u("button",{key:e.id,class:$(["role-item",{active:e.id===v.value}]),onClick:i=>v.value=e.id},[t("span",null,[t("strong",null,o(e.name),1),t("small",null,o(e.builtin?"内置角色":"自定义角色")+" / "+o($e(e.scopeType)),1)]),t("em",null,o(e.allowCount),1)],10,De))),128)),t("form",{class:"role-create",onSubmit:ne(we,["prevent"])},[p(t("input",{"onUpdate:modelValue":l[4]||(l[4]=e=>R.value=e),placeholder:"custom:music-admin"},null,512),[[N,R.value,void 0,{trim:true}]]),p(t("input",{"onUpdate:modelValue":l[5]||(l[5]=e=>x.value=e),placeholder:"角色名称"},null,512),[[N,x.value,void 0,{trim:true}]]),p(t("select",{"onUpdate:modelValue":l[6]||(l[6]=e=>q.value=e)},[...l[21]||(l[21]=[t("option",{value:"guild"},"群内角色",-1),t("option",{value:"global"},"全局角色",-1)])],512),[[I,q.value]]),t("button",{class:"primary",disabled:r.value||!R.value||!x.value},"创建角色",8,Ee)],32)]),P.value?(s(),u("section",Le,[t("div",Fe,[t("div",null,[t("h2",null,o(P.value.name),1),t("p",null,o(P.value.id),1)]),t("div",Ke,[p(t("select",{"onUpdate:modelValue":l[7]||(l[7]=e=>B.value=e)},[l[22]||(l[22]=t("option",{disabled:"",value:""},"复制来源",-1)),(s(true),u(h,null,k(ie.value,e=>(s(),u("option",{key:e.id,value:e.id},o(e.name),9,je))),128))],512),[[I,B.value]]),t("button",{disabled:r.value||!B.value,onClick:Se},"复制权限",8,qe),t("label",ze,[p(t("input",{type:"checkbox","onUpdate:modelValue":l[8]||(l[8]=e=>j.value=e)},null,512),[[Re,j.value]]),l[23]||(l[23]=K(" 审计模式 ",-1))])])]),t("section",He,[t("div",null,[l[24]||(l[24]=t("strong",null,"成员",-1)),t("small",null,o(ee.value.length)+" 个显式成员",1)]),t("div",Je,[(s(true),u(h,null,k(ee.value,e=>(s(),u("span",{key:Ie(e),class:"member-chip"},[K(o(e.platform)+":"+o(e.userId)+" / "+o(e.scope)+" ",1),t("button",{disabled:r.value,onClick:i=>Ce(e)},"移除",8,Qe)]))),128))]),t("form",{class:"member-add",onSubmit:ne(_e,["prevent"])},[p(t("input",{"onUpdate:modelValue":l[9]||(l[9]=e=>M.value=e),placeholder:"platform:userId"},null,512),[[N,M.value,void 0,{trim:true}]]),p(t("select",{"onUpdate:modelValue":l[10]||(l[10]=e=>G.value=e)},[l[25]||(l[25]=t("option",{value:"global"},"global",-1)),a.value!=="global"?(s(),u("option",{key:0,value:a.value},o(a.value),9,We)):T("v-if",true)],512),[[I,G.value]]),t("button",{class:"primary",disabled:r.value||!M.value},"添加成员",8,Xe)],32)]),t("div",Ye,[p(t("input",{"onUpdate:modelValue":l[11]||(l[11]=e=>_.value=e),placeholder:"搜索指令、插件、描述"},null,512),[[N,_.value,void 0,{trim:true}]]),p(t("select",{"onUpdate:modelValue":l[12]||(l[12]=e=>A.value=e)},[...l[26]||(l[26]=[t("option",{value:"all"},"全部状态",-1),t("option",{value:"pending"},"待配置",-1),t("option",{value:"configured"},"已配置",-1),t("option",{value:"disabled"},"已禁用",-1)])],512),[[I,A.value]])]),t("div",Ze,[(s(true),u(h,null,k(de.value,e=>(s(),u("article",{key:e.id,class:"command-row"},[t("div",el,[t("strong",null,o(e.name),1),t("span",null,o(e.commandPath),1),t("small",null,o(e.plugin)+" / legacy "+o(e.legacyAuthority)+" / "+o(e.suggestion.label),1)]),t("div",ll,[t("button",{class:$({active:J(e.id)==="inherit"}),onClick:i=>Q(e.id,"inherit")}," 继承 ",10,tl),t("button",{class:$({active:J(e.id)==="allow"}),onClick:i=>Q(e.id,"allow")}," 开 ",10,nl),t("button",{class:$({active:J(e.id)==="deny"}),onClick:i=>Q(e.id,"deny")}," 关 ",10,ol)])]))),128))])])):T("v-if",true)])):f.value==="pending"?(s(),u("section",sl,[Z.value.length?(s(),u("table",ul,[l[27]||(l[27]=t("thead",null,[t("tr",null,[t("th",null,"指令"),t("th",null,"建议角色"),t("th",null,"理由")])],-1)),t("tbody",null,[(s(true),u(h,null,k(Z.value,e=>(s(),u("tr",{key:`review:${e.id}`},[t("td",null,o(e.commandPath),1),t("td",null,o(be(e)),1),t("td",null,o(L(e).reason),1)]))),128))])])):T("v-if",true),(s(true),u(h,null,k(w.value,e=>(s(),u("article",{key:e.id,class:"pending-item"},[t("div",null,[t("strong",null,o(e.name),1),t("span",null,o(e.commandPath),1),t("small",null,o(e.plugin)+" / legacy "+o(e.legacyAuthority),1)]),t("dl",null,[l[28]||(l[28]=t("dt",null,"旧建议",-1)),t("dd",null,o(e.suggestion.label),1),l[29]||(l[29]=t("dt",null,"AI 状态",-1)),t("dd",null,o(L(e).label)+":"+o(L(e).reason),1)]),t("div",il,[(s(true),u(h,null,k(S.value,i=>(s(),u("label",{key:`${e.id}:${i.id}`,class:"check"},[t("input",{type:"checkbox",checked:ce(e,i.id),onChange:W=>pe(e,i.id,W)},null,40,al),K(" "+o(i.name),1)]))),128))]),t("div",dl,[t("button",{disabled:r.value,onClick:i=>ge(e)},"填入旧建议",8,rl),t("button",{disabled:r.value||!m.value.autoAssignAvailable,onClick:i=>fe(e)},"AI 生成建议",8,vl),t("button",{class:"primary",disabled:r.value,onClick:i=>ke(e)},"保存分配",8,cl)])]))),128)),w.value.length?T("v-if",true):(s(),u("p",pl,"没有待配置指令。"))])):(s(),u("section",gl,[t("div",bl,[p(t("input",{"onUpdate:modelValue":l[13]||(l[13]=e=>_.value=e),placeholder:"搜索指令、插件、描述"},null,512),[[N,_.value,void 0,{trim:true}]]),p(t("select",{"onUpdate:modelValue":l[14]||(l[14]=e=>A.value=e)},[...l[30]||(l[30]=[t("option",{value:"all"},"全部状态",-1),t("option",{value:"pending"},"待配置",-1),t("option",{value:"configured"},"已配置",-1),t("option",{value:"disabled"},"已禁用",-1)])],512),[[I,A.value]])]),t("div",fl,[p(t("select",{"onUpdate:modelValue":l[15]||(l[15]=e=>C.value=e)},[l[31]||(l[31]=t("option",{disabled:"",value:""},"选择插件",-1)),(s(true),u(h,null,k(ae.value,e=>(s(),u("option",{key:e,value:e},o(e),9,hl))),128))],512),[[I,C.value]]),t("button",{disabled:r.value||!C.value,onClick:l[16]||(l[16]=e=>F("legacy"))},"采用插件建议",8,kl),t("button",{disabled:r.value||!C.value,onClick:l[17]||(l[17]=e=>F("guild-member"))},"给群成员",8,yl),t("button",{disabled:r.value||!C.value,onClick:l[18]||(l[18]=e=>F("guild-admin"))},"给群管理员",8,ml),t("button",{disabled:r.value||!C.value,onClick:l[19]||(l[19]=e=>F("bot-admin"))},"仅 Bot 管理员",8,wl)]),(s(true),u(h,null,k(le.value,e=>(s(),u("article",{key:e.id,class:"command-row"},[t("div",_l,[t("strong",null,o(e.name),1),t("span",null,o(e.id),1),t("small",null,o(e.plugin)+" / "+o(e.commandPath)+" / "+o(e.autoAssign.label),1)]),t("div",Cl,[t("label",Sl,[t("input",{type:"checkbox",checked:e.allowGuildOverride,onChange:i=>ye(e,i)},null,40,Il),l[32]||(l[32]=K(" 群内自治 ",-1))]),t("select",{value:e.status,onChange:i=>me(e,i)},[...l[33]||(l[33]=[t("option",{value:"pending"},"待配置",-1),t("option",{value:"configured"},"已配置",-1),t("option",{value:"disabled"},"已禁用",-1)])],40,$l)])]))),128))]))]))}}),Rl=(U,f)=>{const a=U.__vccOpts||U;for(const[v,_]of f)a[v]=_;return a},Pl=Rl(Al,[["__scopeId","data-v-b9691780"]]),xl=U=>{U.page({name:"新权限",path:"/new-auth",icon:"⌗",fields:["newauth"],authority:4,component:Pl})};export{xl as default};
package/dist/style.css CHANGED
@@ -1 +1 @@
1
- .new-auth-page[data-v-6014eba6]{min-height:100vh;background:#eef2f6;color:#18222d;padding:20px}.topbar[data-v-6014eba6],.detail-head[data-v-6014eba6],.filters[data-v-6014eba6],.row-actions[data-v-6014eba6],.command-controls[data-v-6014eba6],.top-actions[data-v-6014eba6],.detail-tools[data-v-6014eba6],.member-add[data-v-6014eba6],.plugin-bulk[data-v-6014eba6]{display:flex;align-items:center;gap:12px}.topbar[data-v-6014eba6]{justify-content:space-between;border:1px solid #d7dee8;border-radius:8px;background:#fff;box-shadow:0 8px 24px #20304814;padding:16px}h1[data-v-6014eba6],h2[data-v-6014eba6],p[data-v-6014eba6]{margin:0}h1[data-v-6014eba6]{font-size:26px;line-height:1.2}h2[data-v-6014eba6]{font-size:20px}p[data-v-6014eba6],small[data-v-6014eba6],span[data-v-6014eba6],dd[data-v-6014eba6],dt[data-v-6014eba6]{color:#607083}button[data-v-6014eba6],select[data-v-6014eba6],input[data-v-6014eba6]{border:1px solid #c9d3df;border-radius:6px;background:#fff;color:#18222d;min-height:34px;padding:0 10px;font:inherit}button[data-v-6014eba6]{cursor:pointer;transition:border-color .16s ease,box-shadow .16s ease,background .16s ease}button[data-v-6014eba6]:not(:disabled):hover,select[data-v-6014eba6]:focus,input[data-v-6014eba6]:focus{border-color:#2d6cdf;box-shadow:0 0 0 3px #2d6cdf1f;outline:none}button[data-v-6014eba6]:disabled{cursor:not-allowed;opacity:.55}.primary[data-v-6014eba6]{background:#1d6f68;border-color:#1d6f68;color:#fff}.tabs[data-v-6014eba6]{display:flex;gap:6px;margin:16px 0}.tabs button[data-v-6014eba6]{background:#f8fafc}.tabs button.active[data-v-6014eba6],.state-buttons button.active[data-v-6014eba6]{background:#17202b;border-color:#17202b;color:#fff}.role-layout[data-v-6014eba6]{display:grid;grid-template-columns:minmax(220px,280px) 1fr;gap:16px}.role-list[data-v-6014eba6]{display:grid;gap:8px;align-content:start}.role-create[data-v-6014eba6]{display:grid;gap:8px;border:1px solid #d7dee8;border-radius:8px;background:#fff;padding:10px;box-shadow:0 4px 14px #2030480d}.role-item[data-v-6014eba6]{display:flex;justify-content:space-between;align-items:center;min-height:58px;text-align:left}.role-item span[data-v-6014eba6],.command-main[data-v-6014eba6]{display:grid;gap:3px;min-width:0}.role-item.active[data-v-6014eba6]{border-color:#2d6cdf;box-shadow:inset 3px 0 #2d6cdf,0 4px 14px #2d6cdf1f}.role-item em[data-v-6014eba6]{font-style:normal;color:#9a5a00}.detail[data-v-6014eba6]{min-width:0}.detail-head[data-v-6014eba6]{justify-content:space-between;margin-bottom:12px}.detail-tools[data-v-6014eba6]{flex-wrap:wrap;justify-content:flex-end}.member-panel[data-v-6014eba6]{display:grid;gap:10px;border:1px solid #d7dee8;border-radius:8px;background:#fff;padding:12px;margin-bottom:12px;box-shadow:0 4px 14px #2030480d}.member-panel>div[data-v-6014eba6]:first-child{display:flex;justify-content:space-between;gap:12px}.member-list[data-v-6014eba6]{display:flex;flex-wrap:wrap;gap:6px;min-height:28px}.member-chip[data-v-6014eba6]{display:inline-flex;align-items:center;gap:6px;border:1px solid #c9d3df;border-radius:999px;background:#f8fafc;padding:3px 4px 3px 10px}.member-chip button[data-v-6014eba6]{min-height:24px;padding:0 8px;border-radius:999px}.filters[data-v-6014eba6]{margin-bottom:12px}.plugin-bulk[data-v-6014eba6]{flex-wrap:wrap;border:1px solid #d7dee8;border-radius:8px;background:#fff;padding:10px;margin-bottom:12px;box-shadow:0 4px 14px #2030480d}.filters input[data-v-6014eba6]{flex:1;min-width:180px}.command-list[data-v-6014eba6],.pending-grid[data-v-6014eba6],.commands-table[data-v-6014eba6]{display:grid;gap:8px}.command-row[data-v-6014eba6],.pending-item[data-v-6014eba6]{background:#fff;border:1px solid #d7dee8;border-radius:8px;padding:12px;box-shadow:0 4px 14px #2030480d}.command-row[data-v-6014eba6]{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;align-items:center}.command-main strong[data-v-6014eba6],.command-main span[data-v-6014eba6],.command-main small[data-v-6014eba6]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.state-buttons[data-v-6014eba6]{display:grid;grid-template-columns:repeat(3,54px);gap:6px}.pending-grid[data-v-6014eba6]{grid-template-columns:repeat(auto-fill,minmax(300px,1fr))}.pending-item[data-v-6014eba6]{display:grid;gap:10px}.role-picker[data-v-6014eba6]{display:grid;grid-template-columns:repeat(auto-fill,minmax(128px,1fr));gap:8px}.role-picker .check[data-v-6014eba6]{border:1px solid #d7dee8;border-radius:6px;background:#f8fafc;min-height:32px;padding:0 8px}dl[data-v-6014eba6]{display:grid;grid-template-columns:58px 1fr;gap:4px 8px;margin:0}dd[data-v-6014eba6]{margin:0}.check[data-v-6014eba6]{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}.check input[data-v-6014eba6]{min-height:0}.error[data-v-6014eba6]{border:1px solid #e2a15d;background:#fff6e9;color:#85510d;border-radius:6px;padding:10px;margin-bottom:12px}.empty[data-v-6014eba6]{padding:24px 0}@media (max-width: 760px){.new-auth-page[data-v-6014eba6]{padding:12px}.topbar[data-v-6014eba6],.detail-head[data-v-6014eba6],.command-row[data-v-6014eba6]{grid-template-columns:1fr;display:grid}.top-actions[data-v-6014eba6],.filters[data-v-6014eba6],.command-controls[data-v-6014eba6],.detail-tools[data-v-6014eba6],.member-add[data-v-6014eba6],.plugin-bulk[data-v-6014eba6]{flex-wrap:wrap}.role-layout[data-v-6014eba6]{grid-template-columns:1fr}.state-buttons[data-v-6014eba6]{grid-template-columns:repeat(3,minmax(0,1fr))}}
1
+ .new-auth-page[data-v-b9691780]{min-height:100vh;background:#eef2f6;color:#18222d;padding:20px}.topbar[data-v-b9691780],.detail-head[data-v-b9691780],.filters[data-v-b9691780],.row-actions[data-v-b9691780],.command-controls[data-v-b9691780],.top-actions[data-v-b9691780],.detail-tools[data-v-b9691780],.member-add[data-v-b9691780],.plugin-bulk[data-v-b9691780]{display:flex;align-items:center;gap:12px}.topbar[data-v-b9691780]{justify-content:space-between;border:1px solid #d7dee8;border-radius:8px;background:#fff;box-shadow:0 8px 24px #20304814;padding:16px}h1[data-v-b9691780],h2[data-v-b9691780],p[data-v-b9691780]{margin:0}h1[data-v-b9691780]{font-size:26px;line-height:1.2}h2[data-v-b9691780]{font-size:20px}p[data-v-b9691780],small[data-v-b9691780],span[data-v-b9691780],dd[data-v-b9691780],dt[data-v-b9691780]{color:#607083}button[data-v-b9691780],select[data-v-b9691780],input[data-v-b9691780]{border:1px solid #c9d3df;border-radius:6px;background:#fff;color:#18222d;min-height:34px;padding:0 10px;font:inherit}button[data-v-b9691780]{cursor:pointer;transition:border-color .16s ease,box-shadow .16s ease,background .16s ease}button[data-v-b9691780]:not(:disabled):hover,select[data-v-b9691780]:focus,input[data-v-b9691780]:focus{border-color:#2d6cdf;box-shadow:0 0 0 3px #2d6cdf1f;outline:none}button[data-v-b9691780]:disabled{cursor:not-allowed;opacity:.55}.primary[data-v-b9691780]{background:#1d6f68;border-color:#1d6f68;color:#fff}.tabs[data-v-b9691780]{display:flex;gap:6px;margin:16px 0}.tabs button[data-v-b9691780]{background:#f8fafc}.tabs button.active[data-v-b9691780],.state-buttons button.active[data-v-b9691780]{background:#17202b;border-color:#17202b;color:#fff}.role-layout[data-v-b9691780]{display:grid;grid-template-columns:minmax(220px,280px) 1fr;gap:16px}.role-list[data-v-b9691780]{display:grid;gap:8px;align-content:start}.role-create[data-v-b9691780]{display:grid;gap:8px;border:1px solid #d7dee8;border-radius:8px;background:#fff;padding:10px;box-shadow:0 4px 14px #2030480d}.role-item[data-v-b9691780]{display:flex;justify-content:space-between;align-items:center;min-height:58px;text-align:left}.role-item span[data-v-b9691780],.command-main[data-v-b9691780]{display:grid;gap:3px;min-width:0}.role-item.active[data-v-b9691780]{border-color:#2d6cdf;box-shadow:inset 3px 0 #2d6cdf,0 4px 14px #2d6cdf1f}.role-item em[data-v-b9691780]{font-style:normal;color:#9a5a00}.detail[data-v-b9691780]{min-width:0}.detail-head[data-v-b9691780]{justify-content:space-between;margin-bottom:12px}.detail-tools[data-v-b9691780]{flex-wrap:wrap;justify-content:flex-end}.member-panel[data-v-b9691780]{display:grid;gap:10px;border:1px solid #d7dee8;border-radius:8px;background:#fff;padding:12px;margin-bottom:12px;box-shadow:0 4px 14px #2030480d}.member-panel>div[data-v-b9691780]:first-child{display:flex;justify-content:space-between;gap:12px}.member-list[data-v-b9691780]{display:flex;flex-wrap:wrap;gap:6px;min-height:28px}.member-chip[data-v-b9691780]{display:inline-flex;align-items:center;gap:6px;border:1px solid #c9d3df;border-radius:999px;background:#f8fafc;padding:3px 4px 3px 10px}.member-chip button[data-v-b9691780]{min-height:24px;padding:0 8px;border-radius:999px}.filters[data-v-b9691780]{margin-bottom:12px}.plugin-bulk[data-v-b9691780]{flex-wrap:wrap;border:1px solid #d7dee8;border-radius:8px;background:#fff;padding:10px;margin-bottom:12px;box-shadow:0 4px 14px #2030480d}.filters input[data-v-b9691780]{flex:1;min-width:180px}.command-list[data-v-b9691780],.pending-panel[data-v-b9691780],.commands-table[data-v-b9691780]{display:grid;gap:8px}.command-row[data-v-b9691780],.pending-item[data-v-b9691780]{background:#fff;border:1px solid #d7dee8;border-radius:8px;padding:12px;box-shadow:0 4px 14px #2030480d}.command-row[data-v-b9691780]{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;align-items:center}.command-main strong[data-v-b9691780],.command-main span[data-v-b9691780],.command-main small[data-v-b9691780]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.state-buttons[data-v-b9691780]{display:grid;grid-template-columns:repeat(3,54px);gap:6px}.pending-panel[data-v-b9691780]{grid-template-columns:repeat(auto-fill,minmax(300px,1fr))}.review-table[data-v-b9691780]{grid-column:1 / -1;width:100%;border-collapse:collapse;overflow:hidden;border:1px solid #d7dee8;border-radius:8px;background:#fff;box-shadow:0 4px 14px #2030480d}.review-table th[data-v-b9691780],.review-table td[data-v-b9691780]{border-bottom:1px solid #e5ebf1;padding:10px;text-align:left;vertical-align:top}.review-table th[data-v-b9691780]{background:#f8fafc;color:#314155;font-weight:600}.review-table tr:last-child td[data-v-b9691780]{border-bottom:0}.pending-item[data-v-b9691780]{display:grid;gap:10px}.role-picker[data-v-b9691780]{display:grid;grid-template-columns:repeat(auto-fill,minmax(128px,1fr));gap:8px}.role-picker .check[data-v-b9691780]{border:1px solid #d7dee8;border-radius:6px;background:#f8fafc;min-height:32px;padding:0 8px}dl[data-v-b9691780]{display:grid;grid-template-columns:58px 1fr;gap:4px 8px;margin:0}dd[data-v-b9691780]{margin:0}.check[data-v-b9691780]{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}.check input[data-v-b9691780]{min-height:0}.error[data-v-b9691780]{border:1px solid #e2a15d;background:#fff6e9;color:#85510d;border-radius:6px;padding:10px;margin-bottom:12px}.empty[data-v-b9691780]{padding:24px 0}@media (max-width: 760px){.new-auth-page[data-v-b9691780]{padding:12px}.topbar[data-v-b9691780],.detail-head[data-v-b9691780],.command-row[data-v-b9691780]{grid-template-columns:1fr;display:grid}.top-actions[data-v-b9691780],.filters[data-v-b9691780],.command-controls[data-v-b9691780],.detail-tools[data-v-b9691780],.member-add[data-v-b9691780],.plugin-bulk[data-v-b9691780]{flex-wrap:wrap}.role-layout[data-v-b9691780]{grid-template-columns:1fr}.state-buttons[data-v-b9691780]{grid-template-columns:repeat(3,minmax(0,1fr))}}
package/lib/index.d.ts CHANGED
@@ -9,7 +9,7 @@ type RoleType = 'builtin' | 'custom';
9
9
  type ScopeType = 'global' | 'guild';
10
10
  type CommandStatus = 'pending' | 'configured' | 'disabled';
11
11
  type PolicyState = 'inherit' | 'allow' | 'deny';
12
- type AutoAssignKind = 'public' | 'moderation' | 'owner' | 'admin' | 'legacy';
12
+ type AutoAssignKind = 'ai' | 'legacy';
13
13
  type PluginAssignTarget = 'legacy' | 'bot-admin' | 'guild-owner' | 'guild-admin' | 'guild-member';
14
14
  export interface NewAuthCommandRecord {
15
15
  id: string;
@@ -82,6 +82,10 @@ declare module '@koishijs/plugin-console' {
82
82
  success: true;
83
83
  count: number;
84
84
  }>;
85
+ 'newauth/generateAiSuggestion'(payload: AiSuggestionPayload): Promise<AutoAssignSuggestion>;
86
+ 'newauth/generateAiSuggestions'(payload?: AutoAssignPayload): Promise<{
87
+ suggestions: Record<string, AutoAssignSuggestion>;
88
+ }>;
85
89
  'newauth/configureCommandRoles'(payload: ConfigureCommandRolesPayload): Promise<{
86
90
  success: true;
87
91
  }>;
@@ -156,6 +160,9 @@ interface AutoAssignPayload {
156
160
  scope?: string;
157
161
  mode?: 'legacy' | 'advisor';
158
162
  }
163
+ interface AiSuggestionPayload {
164
+ commandId: string;
165
+ }
159
166
  interface ConfigureCommandRolesPayload {
160
167
  commandId: string;
161
168
  scope: string;
@@ -192,12 +199,16 @@ export declare function apply(ctx: Context, config: Config): void;
192
199
  export declare class NewAuthService extends Service {
193
200
  config: Config;
194
201
  private commandCache;
202
+ private commandLocks;
195
203
  private adminSet;
196
204
  private ownerRoleNames;
197
205
  private adminRoleNames;
198
206
  constructor(ctx: Context, config: Config);
199
207
  start(): Promise<void>;
200
208
  registerCommand(command: Command): Promise<string | undefined>;
209
+ private writeCommandRecord;
210
+ private ensureSelfCommandPolicies;
211
+ private cleanupSelfCommandDuplicates;
201
212
  intercept(argv: Argv): Promise<string | undefined>;
202
213
  canExecute(session: Session, commandId: string): Promise<{
203
214
  allowed: boolean;
@@ -265,6 +276,16 @@ export declare class NewAuthService extends Service {
265
276
  setCommandPolicy(scope: string, roleId: string, input: string, state: PolicyState): Promise<NewAuthCommandRecord>;
266
277
  applySuggestedPolicy(input: string, scope?: string, mode?: 'legacy' | 'advisor'): Promise<NewAuthCommandRecord>;
267
278
  configureCommandRoles(input: string, scope?: string, roleIds?: string[]): Promise<NewAuthCommandRecord>;
279
+ generateAiSuggestion(input: string): Promise<AutoAssignSuggestion>;
280
+ generateAiSuggestionsForPending(): Promise<Record<string, AutoAssignSuggestion>>;
281
+ listGuildManageableCommands(session: Session, query?: string): Promise<{
282
+ command: NewAuthCommandRecord;
283
+ states: string[];
284
+ }[]>;
285
+ setGuildManagedPolicy(session: Session, roleId: string, input: string, state: PolicyState): Promise<{
286
+ command: NewAuthCommandRecord;
287
+ scope: string;
288
+ }>;
268
289
  autoAssignPending(scope?: string, mode?: 'legacy' | 'advisor'): Promise<number>;
269
290
  assignPluginCommands(plugin: string, scope?: string, target?: PluginAssignTarget): Promise<number>;
270
291
  getCommand(input: string): Promise<NewAuthCommandRecord>;
@@ -275,7 +296,10 @@ export declare class NewAuthService extends Service {
275
296
  private resolveCommandInput;
276
297
  private setPolicy;
277
298
  private getEffectivePolicy;
299
+ private ensureGuildManager;
278
300
  private getAutoAssignSuggestion;
301
+ private hasAiAdvisor;
302
+ private getAiAssignSuggestion;
279
303
  private ensureRoleMember;
280
304
  private ensureRoleExists;
281
305
  private hasPlatformRole;
package/lib/index.js CHANGED
@@ -5,7 +5,7 @@ exports.apply = apply;
5
5
  const koishi_1 = require("koishi");
6
6
  const path_1 = require("path");
7
7
  exports.name = 'new-auth';
8
- exports.inject = { required: ['database'], optional: ['console'] };
8
+ exports.inject = { required: ['database'], optional: ['console', 'chatluna'] };
9
9
  const BUILTIN_ROLES = [
10
10
  {
11
11
  id: 'bot-admin',
@@ -40,7 +40,7 @@ const BUILTIN_ROLES = [
40
40
  ];
41
41
  const OWNER_ROLE_NAMES = ['owner', 'guild-owner'];
42
42
  const ADMIN_ROLE_NAMES = ['admin', 'administrator', 'guild-admin', 'moderator'];
43
- const DEFAULT_ALLOW_GUILD_OVERRIDE_AUTHORITY_MAX = 3;
43
+ const DEFAULT_ALLOW_GUILD_OVERRIDE_AUTHORITY_MAX = 2;
44
44
  const GRANT_RUNTIME_COMMAND_PERMISSION = true;
45
45
  const RAISE_LEGACY_AUTHORITY = false;
46
46
  exports.Config = koishi_1.Schema.object({
@@ -120,6 +120,7 @@ class NewAuthService extends koishi_1.Service {
120
120
  constructor(ctx, config) {
121
121
  super(ctx, 'newauth', true);
122
122
  this.commandCache = new Map();
123
+ this.commandLocks = new Map();
123
124
  this.config = config;
124
125
  this.adminSet = new Set(config.botAdmins.map(normalizeKey));
125
126
  this.ownerRoleNames = new Set(OWNER_ROLE_NAMES.map(normalizeKey));
@@ -136,6 +137,17 @@ class NewAuthService extends koishi_1.Service {
136
137
  return;
137
138
  const now = new Date();
138
139
  const record = this.createCommandRecord(command, now);
140
+ const previous = this.commandLocks.get(record.id) || Promise.resolve(undefined);
141
+ const next = previous.then(() => this.writeCommandRecord(command, record, now));
142
+ this.commandLocks.set(record.id, next);
143
+ next.finally(() => {
144
+ if (this.commandLocks.get(record.id) === next)
145
+ this.commandLocks.delete(record.id);
146
+ });
147
+ return next;
148
+ }
149
+ async writeCommandRecord(command, record, now) {
150
+ await this.cleanupSelfCommandDuplicates(record);
139
151
  const [existing] = await this.ctx.database.get('new_auth_command', { id: record.id });
140
152
  if (existing) {
141
153
  const next = {
@@ -147,17 +159,41 @@ class NewAuthService extends koishi_1.Service {
147
159
  legacyAuthority: record.legacyAuthority,
148
160
  updatedAt: now,
149
161
  };
162
+ if (this.isSelfCommand(command)) {
163
+ next.status = 'configured';
164
+ }
150
165
  await this.ctx.database.set('new_auth_command', record.id, next);
151
166
  this.commandCache.set(record.id, { ...existing, ...next });
167
+ await this.ensureSelfCommandPolicies(record);
152
168
  return record.id;
153
169
  }
154
170
  await this.ctx.database.create('new_auth_command', record);
155
171
  this.commandCache.set(record.id, record);
156
- if (this.isSelfCommand(command)) {
157
- await this.setPolicy('global', 'bot-admin', record.id, 'allow');
158
- }
172
+ await this.ensureSelfCommandPolicies(record);
159
173
  return record.id;
160
174
  }
175
+ async ensureSelfCommandPolicies(record) {
176
+ if (!isSelfCommandPath(record.commandPath))
177
+ return;
178
+ await this.setPolicy('global', 'bot-admin', record.id, 'allow');
179
+ if (isGuildAutonomyCommandPath(record.commandPath)) {
180
+ await this.setPolicy('global', 'guild-owner', record.id, 'allow');
181
+ }
182
+ }
183
+ async cleanupSelfCommandDuplicates(record) {
184
+ if (!isSelfCommandPath(record.commandPath))
185
+ return;
186
+ const staleRecords = await this.ctx.database.get('new_auth_command', {
187
+ commandPath: record.commandPath,
188
+ });
189
+ for (const stale of staleRecords) {
190
+ if (stale.id === record.id)
191
+ continue;
192
+ await this.ctx.database.remove('new_auth_policy', { commandId: stale.id });
193
+ await this.ctx.database.remove('new_auth_command', { id: stale.id });
194
+ this.commandCache.delete(stale.id);
195
+ }
196
+ }
161
197
  async intercept(argv) {
162
198
  const { command, session, options } = argv;
163
199
  if (!command || !session)
@@ -333,7 +369,7 @@ class NewAuthService extends koishi_1.Service {
333
369
  members,
334
370
  scopes,
335
371
  pendingCount: commands.filter(command => command.status === 'pending').length,
336
- autoAssignAvailable: true,
372
+ autoAssignAvailable: this.hasAiAdvisor(),
337
373
  };
338
374
  }
339
375
  async addBotAdmin(uid) {
@@ -429,7 +465,7 @@ class NewAuthService extends koishi_1.Service {
429
465
  async applySuggestedPolicy(input, scope = 'global', mode = 'legacy') {
430
466
  const command = await this.resolveCommandInput(input);
431
467
  const suggestion = mode === 'advisor'
432
- ? this.getAutoAssignSuggestion(command)
468
+ ? await this.getAiAssignSuggestion(command)
433
469
  : getLegacySuggestion(command.legacyAuthority);
434
470
  await this.applyRolesToCommand(scope, command.id, suggestion.roles);
435
471
  if (command.status === 'pending') {
@@ -445,6 +481,52 @@ class NewAuthService extends koishi_1.Service {
445
481
  }
446
482
  return command;
447
483
  }
484
+ async generateAiSuggestion(input) {
485
+ const command = await this.resolveCommandInput(input);
486
+ return this.getAiAssignSuggestion(command);
487
+ }
488
+ async generateAiSuggestionsForPending() {
489
+ const commands = await this.listCommands({ pending: true });
490
+ const suggestions = {};
491
+ for (const command of commands) {
492
+ suggestions[command.id] = await this.getAiAssignSuggestion(command);
493
+ }
494
+ return suggestions;
495
+ }
496
+ async listGuildManageableCommands(session, query) {
497
+ const scope = await this.ensureGuildManager(session);
498
+ const commands = (await this.listCommands({ all: true, query }))
499
+ .filter(command => command.status === 'configured' && command.allowGuildOverride);
500
+ const roles = ['guild-member', 'guild-admin', 'guild-owner'];
501
+ const rows = [];
502
+ for (const command of commands) {
503
+ const states = [];
504
+ for (const roleId of roles) {
505
+ states.push(`${roleId}:${await this.getEffectivePolicy(scope, roleId, command.id)}`);
506
+ }
507
+ rows.push({ command, states });
508
+ }
509
+ return rows;
510
+ }
511
+ async setGuildManagedPolicy(session, roleId, input, state) {
512
+ const scope = await this.ensureGuildManager(session);
513
+ const role = await this.ensureRoleExists(roleId);
514
+ if (role.scopeType !== 'guild') {
515
+ throw new Error('群内自治只能配置群内角色');
516
+ }
517
+ if (role.id === 'bot-admin') {
518
+ throw new Error('群内自治不能配置 Bot 管理员');
519
+ }
520
+ const command = await this.resolveCommandInput(input);
521
+ if (command.status !== 'configured') {
522
+ throw new Error('该指令尚未由 Koishi 管理员配置');
523
+ }
524
+ if (!command.allowGuildOverride) {
525
+ throw new Error('该指令不允许群内自治');
526
+ }
527
+ await this.setPolicy(scope, role.id, command.id, state);
528
+ return { command, scope };
529
+ }
448
530
  async autoAssignPending(scope = 'global', mode = 'advisor') {
449
531
  const commands = await this.listCommands({ pending: true });
450
532
  for (const command of commands) {
@@ -526,15 +608,16 @@ class NewAuthService extends koishi_1.Service {
526
608
  createdAt: now,
527
609
  updatedAt: now,
528
610
  };
529
- if (this.getAutoAssignSuggestion(record).kind === 'admin') {
530
- record.allowGuildOverride = false;
531
- }
532
611
  return record;
533
612
  }
534
613
  getDescription(command) {
535
614
  const key = `commands.${command.name}.description`;
536
615
  const value = this.ctx.i18n.get(key);
537
- return Array.isArray(value) ? value[0] ?? '' : value ?? '';
616
+ if (typeof value === 'string')
617
+ return value;
618
+ if (Array.isArray(value))
619
+ return stringifyI18nValue(value[0]);
620
+ return stringifyI18nValue(value);
538
621
  }
539
622
  async resolveCommandInput(input) {
540
623
  const normalized = input.trim();
@@ -573,69 +656,79 @@ class NewAuthService extends koishi_1.Service {
573
656
  });
574
657
  return global?.state ?? 'inherit';
575
658
  }
576
- getAutoAssignSuggestion(command) {
577
- const text = normalizeKey([
578
- command.commandPath,
579
- command.name,
580
- command.plugin,
581
- command.description,
582
- command.aliases.join(' '),
583
- ].join(' '));
584
- const dangerous = [
585
- 'plugin', 'market', 'install', 'uninstall', 'reload', 'restart',
586
- 'config', 'database', 'db', 'file', 'delete', 'remove', 'exec',
587
- 'eval', 'shell', 'token', 'env', 'broadcast',
588
- '插件', '市场', '安装', '卸载', '重载', '重启', '配置', '数据库',
589
- '文件', '删除', '执行', '脚本', '令牌', '环境变量', '广播',
590
- ];
591
- if (dangerous.some(keyword => text.includes(keyword))) {
592
- return {
593
- kind: 'admin',
594
- roles: ['bot-admin'],
595
- label: '仅 Bot 管理员',
596
- reason: '命中实例级或高危操作关键词。',
597
- };
659
+ async ensureGuildManager(session) {
660
+ if (!session?.platform || !session.guildId || session.isDirect) {
661
+ throw new Error('只能在群聊中管理群内自治');
598
662
  }
599
- const moderation = [
600
- 'ban', 'mute', 'kick', 'warn', 'recall', 'approve', 'blacklist',
601
- '禁言', '踢', '封禁', '警告', '撤回', '审核', '黑名单',
602
- ];
603
- if (moderation.some(keyword => text.includes(keyword))) {
663
+ const roles = await this.resolveRoles(session);
664
+ if (!roles.includes('bot-admin') && !roles.includes('guild-owner')) {
665
+ throw new Error('只有 Bot 管理员或当前群群主可以管理群内自治');
666
+ }
667
+ return getScope(session);
668
+ }
669
+ getAutoAssignSuggestion(command) {
670
+ const fallback = getLegacySuggestion(command.legacyAuthority);
671
+ return {
672
+ kind: 'legacy',
673
+ roles: fallback.roles,
674
+ label: '等待 AI 分配',
675
+ reason: '未调用 AI 时只显示旧 authority 建议,不做关键词推断。',
676
+ };
677
+ }
678
+ hasAiAdvisor() {
679
+ const chatluna = this.ctx.chatluna;
680
+ const defaultModel = chatluna?.currentConfig?.defaultModel ?? chatluna?.config?.defaultModel;
681
+ return typeof chatluna?.createChatModel === 'function'
682
+ && typeof defaultModel === 'string'
683
+ && !!defaultModel
684
+ && defaultModel !== '无';
685
+ }
686
+ async getAiAssignSuggestion(command) {
687
+ const fallback = getLegacySuggestion(command.legacyAuthority);
688
+ if (!this.hasAiAdvisor()) {
604
689
  return {
605
- kind: 'moderation',
606
- roles: ['guild-admin', 'guild-owner'],
607
- label: '群管理员和群主',
608
- reason: '命中群管理操作关键词。',
690
+ kind: 'legacy',
691
+ roles: fallback.roles,
692
+ label: fallback.label,
693
+ reason: 'ChatLuna 默认模型不可用,已回退到旧 authority 建议。',
609
694
  };
610
695
  }
611
- const ownerOnly = ['welcome', 'setting', 'notice', 'announce', '欢迎', '公告', '群设置'];
612
- if (ownerOnly.some(keyword => text.includes(keyword))) {
696
+ try {
697
+ const roles = await this.listRoles();
698
+ const chatluna = this.ctx.chatluna;
699
+ const defaultModel = chatluna.currentConfig?.defaultModel ?? chatluna.config?.defaultModel;
700
+ const modelRef = await chatluna.createChatModel(defaultModel);
701
+ const model = modelRef?.value;
702
+ if (typeof model?.invoke !== 'function')
703
+ throw new Error('ChatLuna 模型未就绪');
704
+ const output = await model.invoke(buildAiAdvisorPrompt(command, roles), {
705
+ maxTokens: 512,
706
+ signal: AbortSignal.timeout(60000),
707
+ configurable: {
708
+ conversationId: 'newauth-permission-advisor',
709
+ userId: 'newauth',
710
+ },
711
+ });
712
+ const parsed = parseAiAdvisorOutput(output);
713
+ const knownRoles = new Set(roles.map(role => role.id));
714
+ const selected = [...new Set(parsed.roles)].filter(roleId => knownRoles.has(roleId));
715
+ if (!selected.length)
716
+ throw new Error('AI 没有返回有效角色');
613
717
  return {
614
- kind: 'owner',
615
- roles: ['guild-owner'],
616
- label: '群主',
617
- reason: '更适合群主进行群内自治配置。',
718
+ kind: 'ai',
719
+ roles: selected,
720
+ label: selected.map(roleId => roles.find(role => role.id === roleId)?.name ?? roleId).join(''),
721
+ reason: parsed.reason || '由 ChatLuna 默认模型生成。',
618
722
  };
619
723
  }
620
- const publicUse = [
621
- 'help', 'sign', 'rank', 'weather', 'search', 'music', 'play', 'query',
622
- '帮助', '签到', '排行', '天气', '搜索', '点歌', '播放', '查询',
623
- ];
624
- if (publicUse.some(keyword => text.includes(keyword))) {
724
+ catch (error) {
625
725
  return {
626
- kind: 'public',
627
- roles: ['guild-member', 'guild-admin', 'guild-owner'],
628
- label: '群成员及以上',
629
- reason: '命中普通用户功能关键词。',
726
+ kind: 'legacy',
727
+ roles: fallback.roles,
728
+ label: fallback.label,
729
+ reason: `AI 分配失败,已回退到旧 authority 建议:${error instanceof Error ? error.message : String(error)}`,
630
730
  };
631
731
  }
632
- const fallback = getLegacySuggestion(command.legacyAuthority);
633
- return {
634
- kind: 'legacy',
635
- roles: fallback.roles,
636
- label: fallback.label,
637
- reason: '未命中明确语义,回退到旧 authority 建议。',
638
- };
639
732
  }
640
733
  async ensureRoleMember(roleId, platform, userId, scope) {
641
734
  await this.ensureRoleExists(roleId);
@@ -728,6 +821,13 @@ function registerConsole(ctx, service) {
728
821
  ctx.console.refresh('newauth');
729
822
  return { success: true, count };
730
823
  }, { authority: 4 });
824
+ ctx.console.addListener('newauth/generateAiSuggestion', async (payload) => {
825
+ return service.generateAiSuggestion(payload.commandId);
826
+ }, { authority: 4 });
827
+ ctx.console.addListener('newauth/generateAiSuggestions', async () => {
828
+ const suggestions = await service.generateAiSuggestionsForPending();
829
+ return { suggestions };
830
+ }, { authority: 4 });
731
831
  ctx.console.addListener('newauth/configureCommandRoles', async (payload) => {
732
832
  return ok(service.configureCommandRoles(payload.commandId, payload.scope, payload.roleIds));
733
833
  }, { authority: 4 });
@@ -854,6 +954,38 @@ function registerManagementCommands(ctx, config, service) {
854
954
  const count = await service.copyRolePolicies(sourceRoleId, targetRoleId, scope);
855
955
  return `已复制 ${count} 条策略:${sourceRoleId} -> ${targetRoleId} (${scope})`;
856
956
  });
957
+ ctx.command('newauth.guild', '管理当前群的新权限自治', { authority: 0 })
958
+ .action(async ({ session }) => {
959
+ const rows = await service.listGuildManageableCommands(session);
960
+ return [
961
+ `当前群可自治指令:${rows.length}`,
962
+ '使用 newauth.guild.commands 查看列表。',
963
+ ].join('\n');
964
+ });
965
+ ctx.command('newauth.guild.commands [query:text]', '列出当前群可自治指令', { authority: 0 })
966
+ .action(async ({ session }, query) => {
967
+ const rows = await service.listGuildManageableCommands(session, query);
968
+ if (!rows.length)
969
+ return '当前群没有可自治指令。';
970
+ return rows.map(({ command, states }) => {
971
+ return `${command.commandPath} (${command.id}) ${states.join(' ')}`;
972
+ }).join('\n');
973
+ });
974
+ ctx.command('newauth.guild.allow <roleId> <command>', '允许当前群角色使用可自治指令', { authority: 0 })
975
+ .action(async ({ session }, roleId, command) => {
976
+ const result = await service.setGuildManagedPolicy(session, roleId, command, 'allow');
977
+ return `已允许:${result.scope} ${roleId} -> ${result.command.commandPath}`;
978
+ });
979
+ ctx.command('newauth.guild.deny <roleId> <command>', '关闭当前群角色使用可自治指令', { authority: 0 })
980
+ .action(async ({ session }, roleId, command) => {
981
+ const result = await service.setGuildManagedPolicy(session, roleId, command, 'deny');
982
+ return `已关闭:${result.scope} ${roleId} -> ${result.command.commandPath}`;
983
+ });
984
+ ctx.command('newauth.guild.inherit <roleId> <command>', '恢复当前群角色的继承策略', { authority: 0 })
985
+ .action(async ({ session }, roleId, command) => {
986
+ const result = await service.setGuildManagedPolicy(session, roleId, command, 'inherit');
987
+ return `已恢复继承:${result.scope} ${roleId} -> ${result.command.commandPath}`;
988
+ });
857
989
  ctx.command('newauth.plugin.assign <plugin> <target> [scope]', '批量配置插件指令', { authority })
858
990
  .action(async (_, plugin, target, scope = 'global') => {
859
991
  if (!['legacy', 'bot-admin', 'guild-owner', 'guild-admin', 'guild-member'].includes(target)) {
@@ -912,18 +1044,96 @@ function getAssignTargetRoles(target) {
912
1044
  return ['guild-owner'];
913
1045
  return ['bot-admin'];
914
1046
  }
1047
+ function buildAiAdvisorPrompt(command, roles) {
1048
+ const roleLines = roles.map(role => {
1049
+ return `- ${role.id}: ${role.name},${role.scopeType === 'global' ? '全局' : '群内'},${role.builtin ? '内置' : '自定义'}`;
1050
+ }).join('\n');
1051
+ return [
1052
+ '你是 Koishi Bot 的指令权限分配助手。请根据指令元数据判断此指令应该分配给哪些角色。',
1053
+ '必须遵守:群主不是 Bot 管理员;实例级、配置级、文件/数据库/插件/执行类能力应保守;不确定时选择 bot-admin。',
1054
+ '只允许返回 JSON,不要 Markdown,不要解释 JSON 之外的内容。',
1055
+ 'JSON 格式:{"roles":["role-id"],"reason":"一句中文理由"}',
1056
+ '',
1057
+ '可选角色:',
1058
+ roleLines,
1059
+ '',
1060
+ '指令:',
1061
+ JSON.stringify({
1062
+ id: command.id,
1063
+ name: command.name,
1064
+ commandPath: command.commandPath,
1065
+ aliases: command.aliases,
1066
+ plugin: command.plugin,
1067
+ description: command.description,
1068
+ legacyAuthority: command.legacyAuthority,
1069
+ }, null, 2),
1070
+ ].join('\n');
1071
+ }
1072
+ function parseAiAdvisorOutput(output) {
1073
+ const text = extractMessageText(output);
1074
+ const fenced = /```(?:json)?\s*([\s\S]*?)```/.exec(text);
1075
+ const raw = fenced?.[1] ?? /\{[\s\S]*\}/.exec(text)?.[0] ?? text;
1076
+ const parsed = JSON.parse(raw);
1077
+ if (!Array.isArray(parsed.roles))
1078
+ throw new Error('AI 返回格式缺少 roles 数组');
1079
+ return {
1080
+ roles: parsed.roles.filter(role => typeof role === 'string'),
1081
+ reason: typeof parsed.reason === 'string' ? parsed.reason : '',
1082
+ };
1083
+ }
1084
+ function extractMessageText(value) {
1085
+ if (value == null)
1086
+ return '';
1087
+ if (typeof value === 'string')
1088
+ return value;
1089
+ if (typeof value === 'number' || typeof value === 'boolean')
1090
+ return String(value);
1091
+ if (Array.isArray(value))
1092
+ return value.map(extractMessageText).join('');
1093
+ if (typeof value === 'object') {
1094
+ const record = value;
1095
+ if ('content' in record)
1096
+ return extractMessageText(record.content);
1097
+ if ('text' in record)
1098
+ return extractMessageText(record.text);
1099
+ }
1100
+ return String(value);
1101
+ }
915
1102
  function omitPrimary(record, key) {
916
1103
  const { [key]: _value, ...rest } = record;
917
1104
  return rest;
918
1105
  }
919
1106
  function isSelfCommandPath(commandPath) {
920
- return commandPath === 'newauth' || commandPath.startsWith('newauth/');
1107
+ return commandPath === 'newauth'
1108
+ || commandPath.startsWith('newauth/')
1109
+ || commandPath.startsWith('newauth.');
1110
+ }
1111
+ function isGuildAutonomyCommandPath(commandPath) {
1112
+ return commandPath === 'newauth.guild'
1113
+ || commandPath.startsWith('newauth.guild.');
921
1114
  }
922
1115
  function inferPlugin(command) {
1116
+ if (isSelfCommandPath(command.name))
1117
+ return 'new-auth';
923
1118
  const source = command.caller?.scope || command.ctx?.scope;
924
1119
  const plugin = source?.plugin?.name || source?.uid || source?.id || 'unknown';
925
1120
  return String(plugin).replace(/^koishi-plugin-/, '') || 'unknown';
926
1121
  }
1122
+ function stringifyI18nValue(value) {
1123
+ if (value == null)
1124
+ return '';
1125
+ if (typeof value === 'string')
1126
+ return value;
1127
+ if (typeof value === 'number' || typeof value === 'boolean')
1128
+ return String(value);
1129
+ if (Array.isArray(value))
1130
+ return value.map(stringifyI18nValue).filter(Boolean).join('\n');
1131
+ if (typeof value === 'object') {
1132
+ const record = value;
1133
+ return stringifyI18nValue(record['zh-CN'] ?? record.zh ?? record[''] ?? Object.values(record)[0]);
1134
+ }
1135
+ return '';
1136
+ }
927
1137
  function makeCommandId(plugin, commandPath) {
928
1138
  return `command:${normalizeSegment(plugin)}:${normalizeSegment(commandPath)}`;
929
1139
  }
package/newauth.md CHANGED
@@ -1455,4 +1455,4 @@ authority 只做建议,不直接授权;
1455
1455
  ---
1456
1456
 
1457
1457
  KOISHI侧边栏的图标不使用默认的 使用“⌗”
1458
- 同时根据F:\chatluna-1.4.0-alpha.17 为指令权限限制制作一个AI自动分配
1458
+ 同时根据F:\chatluna-1.4.0-alpha.17 为指令权限限制制作一个AI自动分配(即:让AI来配置节点应该给哪个组)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koishi-plugin-new-auth",
3
- "version": "0.4.0",
3
+ "version": "0.4.4",
4
4
  "description": "Koishi 的角色与作用域指令权限系统。",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -49,6 +49,10 @@
49
49
  "service": {
50
50
  "required": [
51
51
  "database"
52
+ ],
53
+ "optional": [
54
+ "console",
55
+ "chatluna"
52
56
  ]
53
57
  }
54
58
  }