koishi-plugin-new-auth 0.1.0 → 0.3.1

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
@@ -10,7 +10,8 @@ It implements the first usable version described in `newauth.md`:
10
10
  - Koishi's legacy `authority` value is recorded as a suggestion, not used as the final grant;
11
11
  - policies are evaluated by `scope + role + command`;
12
12
  - guild owner/admin/member roles are separated from Bot administrator;
13
- - custom roles and role members can be managed from Koishi commands.
13
+ - custom roles and role members can be managed from Koishi commands;
14
+ - Koishi Console WebUI is available when `@koishijs/plugin-console` is installed.
14
15
 
15
16
  ## Configuration
16
17
 
@@ -67,3 +68,23 @@ guest
67
68
  ```
68
69
 
69
70
  Custom roles do not inherit from other roles. To give a custom role access to a command, add an explicit policy with `newauth.allow`.
71
+
72
+ ## WebUI
73
+
74
+ Install Koishi Console and open the `⌗ 新权限` page.
75
+
76
+ The page provides:
77
+
78
+ - role-first command policy editing;
79
+ - global and guild scope switching;
80
+ - pending command review;
81
+ - legacy `authority` suggestions;
82
+ - AI-style automatic assignment for pending commands;
83
+ - command status and guild override toggles;
84
+ - custom role creation and explicit member management;
85
+ - copying command policies from one role to another;
86
+ - batch assigning all commands from one plugin.
87
+
88
+ The automatic assignment advisor is conservative: dangerous instance-level commands are kept for `bot-admin`, group moderation commands go to `guild-admin` and `guild-owner`, normal user commands go to `guild-member` and above, and unclear commands fall back to the legacy authority suggestion.
89
+
90
+ Custom roles do not inherit from built-in roles. Use the WebUI copy action, or `newauth.role.copy`, to clone the current explicit policies from another role.
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import{defineComponent as ve,ref as i,computed as v,watch as H,openBlock as s,createElementBlock as u,createElementVNode as l,toDisplayString as o,withDirectives as c,Fragment as f,renderList as y,vModelSelect as _,normalizeClass as w,createCommentVNode as B,withModifiers as J,vModelText as x,vModelCheckbox as ce,createTextVNode as q}from"vue";import{store as Q,send as g}from"@koishijs/client";const pe={class:"new-auth-page"},ge={class:"topbar"},be={class:"top-actions"},me=["value"],fe=["disabled"],ye=["disabled"],ke={class:"tabs"},he={key:0,class:"error"},_e={key:1,class:"role-layout"},we={class:"role-list"},Ce=["onClick"],Ie=["disabled"],$e={key:0,class:"detail"},Ve={class:"detail-head"},Se={class:"detail-tools"},Ue=["value"],Pe=["disabled"],Re={class:"check"},xe={class:"member-panel"},Ae={class:"member-list"},Me=["disabled","onClick"],Oe=["value"],Te=["disabled"],Ge={class:"filters"},Ne={class:"command-list"},Be={class:"command-main"},De={class:"state-buttons"},Ee=["onClick"],Le=["onClick"],Fe=["onClick"],qe={key:2,class:"pending-grid"},ze={class:"row-actions"},Ke=["disabled","onClick"],je=["disabled","onClick"],He={key:0,class:"empty"},Je={key:3,class:"commands-table"},Qe={class:"filters"},We={class:"plugin-bulk"},Xe=["value"],Ye=["disabled"],Ze=["disabled"],el=["disabled"],ll=["disabled"],tl={class:"command-main"},nl={class:"command-controls"},ol={class:"check"},sl=["checked","onChange"],ul=["value","onChange"],al=ve({__name:"new-auth",setup(S){const b=i("roles"),a=i("global"),d=i(""),k=i(""),C=i("all"),D=i(false),r=i(false),A=i(""),I=i(""),U=i(""),E=i("guild"),P=i(""),M=i("global"),O=i(""),h=i(""),$=v(()=>Q.newauth||{roles:[],commands:[],policies:[],members:[],scopes:[{id:"global",name:"全局默认"}],pendingCount:0}),R=v(()=>$.value.roles),T=v(()=>$.value.commands),W=v(()=>$.value.policies),X=v(()=>$.value.members),Y=v(()=>{var n;return(n=$.value.scopes)!=null&&n.length?$.value.scopes:[{id:"global",name:"全局默认"}]}),G=v(()=>T.value.filter(n=>n.status==="pending")),V=v(()=>R.value.find(n=>n.id===d.value)),z=v(()=>X.value.filter(n=>n.roleId===d.value)),Z=v(()=>R.value.filter(n=>n.id!==d.value)),ee=v(()=>[...new Set(T.value.map(n=>n.plugin))].sort());H(R,n=>{!d.value&&n.length&&(d.value=n[0].id)},{immediate:true}),H(a,n=>{M.value=n==="global"?"global":n});const K=v(()=>{const n=k.value.toLowerCase();return T.value.filter(t=>C.value!=="all"&&t.status!==C.value?false:n?[t.id,t.name,t.commandPath,t.plugin,t.description].some(e=>e==null?void 0:e.toLowerCase().includes(n)):true)}),le=v(()=>V.value?K.value.filter(n=>{var t,e;return D.value||((t=V.value)==null?void 0:t.id)==="bot-admin"||((e=V.value)==null?void 0:e.scopeType)==="global"?true:n.allowGuildOverride}):[]);function L(n){const t=W.value.find(e=>e.scope===a.value&&e.roleId===d.value&&e.commandId===n);return(t==null?void 0:t.state)||"inherit"}async function p(n){r.value=true,A.value="";try{await n}catch(t){A.value=t instanceof Error?t.message:String(t)}finally{r.value=false}}async function te(){return p((async()=>{const n=await g("newauth/getData");Q.newauth=n})())}function F(n,t){return p(g("newauth/setPolicy",{scope:a.value,roleId:d.value,commandId:n,state:t}))}function j(n,t){return p(g("newauth/applySuggestion",{commandId:n,scope:a.value,mode:t}))}function ne(){return p(g("newauth/autoAssignPending",{scope:a.value,mode:"advisor"}))}function oe(n,t){const e=t.target;return p(g("newauth/setGuildOverride",{commandId:n.id,allowGuildOverride:e.checked}))}function se(n,t){const e=t.target;return p(g("newauth/setCommandStatus",{commandId:n.id,status:e.value}))}function ue(){return p((async()=>{await g("newauth/createRole",{id:I.value,name:U.value,scopeType:E.value}),d.value=I.value,I.value="",U.value=""})())}function ae(){return p((async()=>{await g("newauth/addMember",{roleId:d.value,uid:P.value,scope:M.value}),P.value=""})())}function ie(n){return p(g("newauth/removeMember",{roleId:n.roleId,uid:`${n.platform}:${n.userId}`,scope:n.scope}))}function de(){return p(g("newauth/copyRolePolicies",{sourceRoleId:O.value,targetRoleId:d.value,scope:a.value}))}function re(n){return`${n.roleId}:${n.platform}:${n.userId}:${n.scope}`}function N(n){return p(g("newauth/assignPluginCommands",{plugin:h.value,scope:a.value,target:n}))}return(n,t)=>(s(),u("main",pe,[l("header",ge,[l("div",null,[t[20]||(t[20]=l("h1",null,"新权限",-1)),l("p",null,o(R.value.length)+" 个角色 / "+o(T.value.length)+" 个指令 / "+o(G.value.length)+" 个待配置",1)]),l("div",be,[c(l("select",{"onUpdate:modelValue":t[0]||(t[0]=e=>a.value=e),class:"scope-select"},[(s(true),u(f,null,y(Y.value,e=>(s(),u("option",{key:e.id,value:e.id},o(e.name),9,me))),128))],512),[[_,a.value]]),l("button",{class:"primary",disabled:r.value||!G.value.length,onClick:ne}," AI 自动分配待配置 ",8,fe),l("button",{disabled:r.value,onClick:te},"刷新",8,ye)])]),l("nav",ke,[l("button",{class:w({active:b.value==="roles"}),onClick:t[1]||(t[1]=e=>b.value="roles")},"角色",2),l("button",{class:w({active:b.value==="pending"}),onClick:t[2]||(t[2]=e=>b.value="pending")},"待配置",2),l("button",{class:w({active:b.value==="commands"}),onClick:t[3]||(t[3]=e=>b.value="commands")},"指令",2)]),A.value?(s(),u("p",he,o(A.value),1)):B("v-if",true),b.value==="roles"?(s(),u("section",_e,[l("aside",we,[(s(true),u(f,null,y(R.value,e=>(s(),u("button",{key:e.id,class:w(["role-item",{active:e.id===d.value}]),onClick:m=>d.value=e.id},[l("span",null,[l("strong",null,o(e.name),1),l("small",null,o(e.builtin?"内置角色":"自定义角色")+" / "+o(e.scopeType),1)]),l("em",null,o(e.allowCount),1)],10,Ce))),128)),l("form",{class:"role-create",onSubmit:J(ue,["prevent"])},[c(l("input",{"onUpdate:modelValue":t[4]||(t[4]=e=>I.value=e),placeholder:"custom:music-admin"},null,512),[[x,I.value,void 0,{trim:true}]]),c(l("input",{"onUpdate:modelValue":t[5]||(t[5]=e=>U.value=e),placeholder:"角色名称"},null,512),[[x,U.value,void 0,{trim:true}]]),c(l("select",{"onUpdate:modelValue":t[6]||(t[6]=e=>E.value=e)},[...t[21]||(t[21]=[l("option",{value:"guild"},"群内角色",-1),l("option",{value:"global"},"全局角色",-1)])],512),[[_,E.value]]),l("button",{class:"primary",disabled:r.value||!I.value||!U.value},"创建角色",8,Ie)],32)]),V.value?(s(),u("section",$e,[l("div",Ve,[l("div",null,[l("h2",null,o(V.value.name),1),l("p",null,o(V.value.id),1)]),l("div",Se,[c(l("select",{"onUpdate:modelValue":t[7]||(t[7]=e=>O.value=e)},[t[22]||(t[22]=l("option",{disabled:"",value:""},"复制来源",-1)),(s(true),u(f,null,y(Z.value,e=>(s(),u("option",{key:e.id,value:e.id},o(e.name),9,Ue))),128))],512),[[_,O.value]]),l("button",{disabled:r.value||!O.value,onClick:de},"复制权限",8,Pe),l("label",Re,[c(l("input",{type:"checkbox","onUpdate:modelValue":t[8]||(t[8]=e=>D.value=e)},null,512),[[ce,D.value]]),t[23]||(t[23]=q(" 审计模式 ",-1))])])]),l("section",xe,[l("div",null,[t[24]||(t[24]=l("strong",null,"成员",-1)),l("small",null,o(z.value.length)+" 个显式成员",1)]),l("div",Ae,[(s(true),u(f,null,y(z.value,e=>(s(),u("span",{key:re(e),class:"member-chip"},[q(o(e.platform)+":"+o(e.userId)+" / "+o(e.scope)+" ",1),l("button",{disabled:r.value,onClick:m=>ie(e)},"移除",8,Me)]))),128))]),l("form",{class:"member-add",onSubmit:J(ae,["prevent"])},[c(l("input",{"onUpdate:modelValue":t[9]||(t[9]=e=>P.value=e),placeholder:"platform:userId"},null,512),[[x,P.value,void 0,{trim:true}]]),c(l("select",{"onUpdate:modelValue":t[10]||(t[10]=e=>M.value=e)},[t[25]||(t[25]=l("option",{value:"global"},"global",-1)),a.value!=="global"?(s(),u("option",{key:0,value:a.value},o(a.value),9,Oe)):B("v-if",true)],512),[[_,M.value]]),l("button",{class:"primary",disabled:r.value||!P.value},"添加成员",8,Te)],32)]),l("div",Ge,[c(l("input",{"onUpdate:modelValue":t[11]||(t[11]=e=>k.value=e),placeholder:"搜索指令、插件、描述"},null,512),[[x,k.value,void 0,{trim:true}]]),c(l("select",{"onUpdate:modelValue":t[12]||(t[12]=e=>C.value=e)},[...t[26]||(t[26]=[l("option",{value:"all"},"全部状态",-1),l("option",{value:"pending"},"待配置",-1),l("option",{value:"configured"},"已配置",-1),l("option",{value:"disabled"},"已禁用",-1)])],512),[[_,C.value]])]),l("div",Ne,[(s(true),u(f,null,y(le.value,e=>(s(),u("article",{key:e.id,class:"command-row"},[l("div",Be,[l("strong",null,o(e.name),1),l("span",null,o(e.commandPath),1),l("small",null,o(e.plugin)+" / legacy "+o(e.legacyAuthority)+" / "+o(e.suggestion.label),1)]),l("div",De,[l("button",{class:w({active:L(e.id)==="inherit"}),onClick:m=>F(e.id,"inherit")}," 继承 ",10,Ee),l("button",{class:w({active:L(e.id)==="allow"}),onClick:m=>F(e.id,"allow")}," 开 ",10,Le),l("button",{class:w({active:L(e.id)==="deny"}),onClick:m=>F(e.id,"deny")}," 关 ",10,Fe)])]))),128))])])):B("v-if",true)])):b.value==="pending"?(s(),u("section",qe,[(s(true),u(f,null,y(G.value,e=>(s(),u("article",{key:e.id,class:"pending-item"},[l("div",null,[l("strong",null,o(e.name),1),l("span",null,o(e.commandPath),1),l("small",null,o(e.plugin)+" / legacy "+o(e.legacyAuthority),1)]),l("dl",null,[t[27]||(t[27]=l("dt",null,"旧建议",-1)),l("dd",null,o(e.suggestion.label),1),t[28]||(t[28]=l("dt",null,"AI 建议",-1)),l("dd",null,o(e.autoAssign.label)+":"+o(e.autoAssign.reason),1)]),l("div",ze,[l("button",{disabled:r.value,onClick:m=>j(e.id,"legacy")},"采用旧建议",8,Ke),l("button",{class:"primary",disabled:r.value,onClick:m=>j(e.id,"advisor")},"采用 AI 建议",8,je)])]))),128)),G.value.length?B("v-if",true):(s(),u("p",He,"没有待配置指令。"))])):(s(),u("section",Je,[l("div",Qe,[c(l("input",{"onUpdate:modelValue":t[13]||(t[13]=e=>k.value=e),placeholder:"搜索指令、插件、描述"},null,512),[[x,k.value,void 0,{trim:true}]]),c(l("select",{"onUpdate:modelValue":t[14]||(t[14]=e=>C.value=e)},[...t[29]||(t[29]=[l("option",{value:"all"},"全部状态",-1),l("option",{value:"pending"},"待配置",-1),l("option",{value:"configured"},"已配置",-1),l("option",{value:"disabled"},"已禁用",-1)])],512),[[_,C.value]])]),l("div",We,[c(l("select",{"onUpdate:modelValue":t[15]||(t[15]=e=>h.value=e)},[t[30]||(t[30]=l("option",{disabled:"",value:""},"选择插件",-1)),(s(true),u(f,null,y(ee.value,e=>(s(),u("option",{key:e,value:e},o(e),9,Xe))),128))],512),[[_,h.value]]),l("button",{disabled:r.value||!h.value,onClick:t[16]||(t[16]=e=>N("legacy"))},"采用插件建议",8,Ye),l("button",{disabled:r.value||!h.value,onClick:t[17]||(t[17]=e=>N("guild-member"))},"给群成员",8,Ze),l("button",{disabled:r.value||!h.value,onClick:t[18]||(t[18]=e=>N("guild-admin"))},"给群管理员",8,el),l("button",{disabled:r.value||!h.value,onClick:t[19]||(t[19]=e=>N("bot-admin"))},"仅 Bot 管理员",8,ll)]),(s(true),u(f,null,y(K.value,e=>(s(),u("article",{key:e.id,class:"command-row"},[l("div",tl,[l("strong",null,o(e.name),1),l("span",null,o(e.id),1),l("small",null,o(e.plugin)+" / "+o(e.commandPath)+" / "+o(e.autoAssign.label),1)]),l("div",nl,[l("label",ol,[l("input",{type:"checkbox",checked:e.allowGuildOverride,onChange:m=>oe(e,m)},null,40,sl),t[31]||(t[31]=q(" 群内自治 ",-1))]),l("select",{value:e.status,onChange:m=>se(e,m)},[...t[32]||(t[32]=[l("option",{value:"pending"},"待配置",-1),l("option",{value:"configured"},"已配置",-1),l("option",{value:"disabled"},"已禁用",-1)])],40,ul)])]))),128))]))]))}}),il=(S,b)=>{const a=S.__vccOpts||S;for(const[d,k]of b)a[d]=k;return a},dl=il(al,[["__scopeId","data-v-755ad7c8"]]),cl=S=>{S.page({name:"新权限",path:"/new-auth",icon:"⌗",fields:["newauth"],authority:4,component:dl})};export{cl as default};
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ .new-auth-page[data-v-755ad7c8]{min-height:100vh;background:#f6f7f4;color:#1f2523;padding:20px}.topbar[data-v-755ad7c8],.detail-head[data-v-755ad7c8],.filters[data-v-755ad7c8],.row-actions[data-v-755ad7c8],.command-controls[data-v-755ad7c8],.top-actions[data-v-755ad7c8],.detail-tools[data-v-755ad7c8],.member-add[data-v-755ad7c8],.plugin-bulk[data-v-755ad7c8]{display:flex;align-items:center;gap:12px}.topbar[data-v-755ad7c8]{justify-content:space-between;border-bottom:1px solid #d9ded6;padding-bottom:16px}h1[data-v-755ad7c8],h2[data-v-755ad7c8],p[data-v-755ad7c8]{margin:0}h1[data-v-755ad7c8]{font-size:26px;line-height:1.2}h2[data-v-755ad7c8]{font-size:20px}p[data-v-755ad7c8],small[data-v-755ad7c8],span[data-v-755ad7c8],dd[data-v-755ad7c8],dt[data-v-755ad7c8]{color:#63706a}button[data-v-755ad7c8],select[data-v-755ad7c8],input[data-v-755ad7c8]{border:1px solid #c7cec8;border-radius:6px;background:#fff;color:#1f2523;min-height:34px;padding:0 10px;font:inherit}button[data-v-755ad7c8]{cursor:pointer}button[data-v-755ad7c8]:disabled{cursor:not-allowed;opacity:.55}.primary[data-v-755ad7c8]{background:#285e54;border-color:#285e54;color:#fff}.tabs[data-v-755ad7c8]{display:flex;gap:6px;margin:16px 0}.tabs button.active[data-v-755ad7c8],.state-buttons button.active[data-v-755ad7c8]{background:#25312d;border-color:#25312d;color:#fff}.role-layout[data-v-755ad7c8]{display:grid;grid-template-columns:minmax(220px,280px) 1fr;gap:16px}.role-list[data-v-755ad7c8]{display:grid;gap:8px;align-content:start}.role-create[data-v-755ad7c8]{display:grid;gap:8px;border:1px solid #d9ded6;border-radius:8px;background:#fff;padding:10px}.role-item[data-v-755ad7c8]{display:flex;justify-content:space-between;align-items:center;min-height:58px;text-align:left}.role-item span[data-v-755ad7c8],.command-main[data-v-755ad7c8]{display:grid;gap:3px;min-width:0}.role-item.active[data-v-755ad7c8]{border-color:#285e54;box-shadow:inset 3px 0 #285e54}.role-item em[data-v-755ad7c8]{font-style:normal;color:#8b4a2f}.detail[data-v-755ad7c8]{min-width:0}.detail-head[data-v-755ad7c8]{justify-content:space-between;margin-bottom:12px}.detail-tools[data-v-755ad7c8]{flex-wrap:wrap;justify-content:flex-end}.member-panel[data-v-755ad7c8]{display:grid;gap:10px;border:1px solid #d9ded6;border-radius:8px;background:#fff;padding:12px;margin-bottom:12px}.member-panel>div[data-v-755ad7c8]:first-child{display:flex;justify-content:space-between;gap:12px}.member-list[data-v-755ad7c8]{display:flex;flex-wrap:wrap;gap:6px;min-height:28px}.member-chip[data-v-755ad7c8]{display:inline-flex;align-items:center;gap:6px;border:1px solid #c7cec8;border-radius:999px;background:#f6f7f4;padding:3px 4px 3px 10px}.member-chip button[data-v-755ad7c8]{min-height:24px;padding:0 8px;border-radius:999px}.filters[data-v-755ad7c8]{margin-bottom:12px}.plugin-bulk[data-v-755ad7c8]{flex-wrap:wrap;border:1px solid #d9ded6;border-radius:8px;background:#fff;padding:10px;margin-bottom:12px}.filters input[data-v-755ad7c8]{flex:1;min-width:180px}.command-list[data-v-755ad7c8],.pending-grid[data-v-755ad7c8],.commands-table[data-v-755ad7c8]{display:grid;gap:8px}.command-row[data-v-755ad7c8],.pending-item[data-v-755ad7c8]{background:#fff;border:1px solid #d9ded6;border-radius:8px;padding:12px}.command-row[data-v-755ad7c8]{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;align-items:center}.command-main strong[data-v-755ad7c8],.command-main span[data-v-755ad7c8],.command-main small[data-v-755ad7c8]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.state-buttons[data-v-755ad7c8]{display:grid;grid-template-columns:repeat(3,54px);gap:6px}.pending-grid[data-v-755ad7c8]{grid-template-columns:repeat(auto-fill,minmax(300px,1fr))}.pending-item[data-v-755ad7c8]{display:grid;gap:10px}dl[data-v-755ad7c8]{display:grid;grid-template-columns:58px 1fr;gap:4px 8px;margin:0}dd[data-v-755ad7c8]{margin:0}.check[data-v-755ad7c8]{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}.check input[data-v-755ad7c8]{min-height:0}.error[data-v-755ad7c8]{border:1px solid #d9a09a;background:#fff1ef;color:#8a2d22;border-radius:6px;padding:10px;margin-bottom:12px}.empty[data-v-755ad7c8]{padding:24px 0}@media (max-width: 760px){.new-auth-page[data-v-755ad7c8]{padding:12px}.topbar[data-v-755ad7c8],.detail-head[data-v-755ad7c8],.command-row[data-v-755ad7c8]{grid-template-columns:1fr;display:grid}.top-actions[data-v-755ad7c8],.filters[data-v-755ad7c8],.command-controls[data-v-755ad7c8],.detail-tools[data-v-755ad7c8],.member-add[data-v-755ad7c8],.plugin-bulk[data-v-755ad7c8]{flex-wrap:wrap}.role-layout[data-v-755ad7c8]{grid-template-columns:1fr}.state-buttons[data-v-755ad7c8]{grid-template-columns:repeat(3,minmax(0,1fr))}}
package/lib/index.d.ts CHANGED
@@ -1,11 +1,16 @@
1
1
  import { Command, Context, Schema, Session } from 'koishi';
2
2
  import type { Argv } from 'koishi';
3
3
  export declare const name = "new-auth";
4
- export declare const inject: string[];
4
+ export declare const inject: {
5
+ required: string[];
6
+ optional: string[];
7
+ };
5
8
  type RoleType = 'builtin' | 'custom';
6
9
  type ScopeType = 'global' | 'guild';
7
10
  type CommandStatus = 'pending' | 'configured' | 'disabled';
8
11
  type PolicyState = 'inherit' | 'allow' | 'deny';
12
+ type AutoAssignKind = 'public' | 'moderation' | 'owner' | 'admin' | 'legacy';
13
+ type PluginAssignTarget = 'legacy' | 'bot-admin' | 'guild-owner' | 'guild-admin' | 'guild-member';
9
14
  export interface NewAuthCommandRecord {
10
15
  id: string;
11
16
  name: string;
@@ -53,6 +58,121 @@ declare module 'koishi' {
53
58
  newauth: NewAuthService;
54
59
  }
55
60
  }
61
+ declare module '@koishijs/plugin-console' {
62
+ namespace Console {
63
+ interface Services {
64
+ newauth: NewAuthConsoleService;
65
+ }
66
+ }
67
+ interface Events {
68
+ 'newauth/getData'(): Promise<NewAuthConsoleData>;
69
+ 'newauth/setPolicy'(payload: SetPolicyPayload): Promise<{
70
+ success: true;
71
+ }>;
72
+ 'newauth/setCommandStatus'(payload: SetCommandStatusPayload): Promise<{
73
+ success: true;
74
+ }>;
75
+ 'newauth/setGuildOverride'(payload: SetGuildOverridePayload): Promise<{
76
+ success: true;
77
+ }>;
78
+ 'newauth/applySuggestion'(payload: ApplySuggestionPayload): Promise<{
79
+ success: true;
80
+ }>;
81
+ 'newauth/autoAssignPending'(payload?: AutoAssignPayload): Promise<{
82
+ success: true;
83
+ count: number;
84
+ }>;
85
+ 'newauth/createRole'(payload: CreateRolePayload): Promise<{
86
+ success: true;
87
+ }>;
88
+ 'newauth/addMember'(payload: MemberPayload): Promise<{
89
+ success: true;
90
+ }>;
91
+ 'newauth/removeMember'(payload: MemberPayload): Promise<{
92
+ success: true;
93
+ }>;
94
+ 'newauth/copyRolePolicies'(payload: CopyRolePoliciesPayload): Promise<{
95
+ success: true;
96
+ count: number;
97
+ }>;
98
+ 'newauth/assignPluginCommands'(payload: AssignPluginCommandsPayload): Promise<{
99
+ success: true;
100
+ count: number;
101
+ }>;
102
+ }
103
+ }
104
+ export interface NewAuthConsoleData {
105
+ roles: RoleView[];
106
+ commands: CommandView[];
107
+ policies: NewAuthPolicyRecord[];
108
+ members: NewAuthRoleMemberRecord[];
109
+ scopes: ScopeView[];
110
+ pendingCount: number;
111
+ autoAssignAvailable: boolean;
112
+ }
113
+ export interface RoleView extends NewAuthRoleRecord {
114
+ allowCount: number;
115
+ }
116
+ export interface CommandView extends NewAuthCommandRecord {
117
+ suggestion: RoleSuggestion;
118
+ autoAssign: AutoAssignSuggestion;
119
+ }
120
+ export interface ScopeView {
121
+ id: string;
122
+ name: string;
123
+ type: ScopeType;
124
+ }
125
+ export interface RoleSuggestion {
126
+ roles: string[];
127
+ label: string;
128
+ }
129
+ export interface AutoAssignSuggestion extends RoleSuggestion {
130
+ kind: AutoAssignKind;
131
+ reason: string;
132
+ }
133
+ interface SetPolicyPayload {
134
+ scope: string;
135
+ roleId: string;
136
+ commandId: string;
137
+ state: PolicyState;
138
+ }
139
+ interface SetCommandStatusPayload {
140
+ commandId: string;
141
+ status: CommandStatus;
142
+ }
143
+ interface SetGuildOverridePayload {
144
+ commandId: string;
145
+ allowGuildOverride: boolean;
146
+ }
147
+ interface ApplySuggestionPayload {
148
+ commandId: string;
149
+ scope?: string;
150
+ mode?: 'legacy' | 'advisor';
151
+ }
152
+ interface AutoAssignPayload {
153
+ scope?: string;
154
+ mode?: 'legacy' | 'advisor';
155
+ }
156
+ interface CreateRolePayload {
157
+ id: string;
158
+ name: string;
159
+ scopeType: ScopeType;
160
+ }
161
+ interface MemberPayload {
162
+ roleId: string;
163
+ uid: string;
164
+ scope: string;
165
+ }
166
+ interface CopyRolePoliciesPayload {
167
+ sourceRoleId: string;
168
+ targetRoleId: string;
169
+ scope: string;
170
+ }
171
+ interface AssignPluginCommandsPayload {
172
+ plugin: string;
173
+ scope: string;
174
+ target: PluginAssignTarget;
175
+ }
56
176
  export interface Config {
57
177
  botAdmins: string[];
58
178
  trustLegacyAuthorityAsAdmin: boolean;
@@ -128,24 +248,37 @@ export declare class NewAuthService {
128
248
  query?: string;
129
249
  }): Promise<NewAuthCommandRecord[]>;
130
250
  listRoles(): Promise<NewAuthRoleRecord[]>;
251
+ listPolicies(): Promise<NewAuthPolicyRecord[]>;
252
+ listMembers(): Promise<NewAuthRoleMemberRecord[]>;
253
+ listScopes(): Promise<ScopeView[]>;
254
+ getConsoleData(): Promise<NewAuthConsoleData>;
131
255
  addBotAdmin(uid: string): Promise<void>;
132
256
  removeBotAdmin(uid: string): Promise<void>;
133
257
  createCustomRole(id: string, name: string, scopeType?: ScopeType): Promise<void>;
134
258
  addRoleMember(roleId: string, uid: string, scope?: string): Promise<void>;
135
259
  removeRoleMember(roleId: string, uid: string, scope?: string): Promise<void>;
260
+ copyRolePolicies(sourceRoleId: string, targetRoleId: string, scope?: string): Promise<number>;
136
261
  setCommandStatus(input: string, status: CommandStatus): Promise<NewAuthCommandRecord>;
262
+ setCommandGuildOverride(input: string, allowGuildOverride: boolean): Promise<NewAuthCommandRecord>;
137
263
  setCommandPolicy(scope: string, roleId: string, input: string, state: PolicyState): Promise<NewAuthCommandRecord>;
264
+ applySuggestedPolicy(input: string, scope?: string, mode?: 'legacy' | 'advisor'): Promise<NewAuthCommandRecord>;
265
+ autoAssignPending(scope?: string, mode?: 'legacy' | 'advisor'): Promise<number>;
266
+ assignPluginCommands(plugin: string, scope?: string, target?: PluginAssignTarget): Promise<number>;
138
267
  getCommand(input: string): Promise<NewAuthCommandRecord>;
139
268
  private ensureBuiltinRoles;
269
+ private applyRolesToCommand;
140
270
  private createCommandRecord;
141
271
  private getDescription;
142
272
  private resolveCommandInput;
143
273
  private setPolicy;
144
274
  private getEffectivePolicy;
275
+ private getAutoAssignSuggestion;
145
276
  private ensureRoleMember;
277
+ private ensureRoleExists;
146
278
  private hasPlatformRole;
147
279
  private grantRuntimeCommandPermission;
148
280
  private getCommandList;
149
281
  private isSelfCommand;
150
282
  }
283
+ export type NewAuthConsoleService = any;
151
284
  export {};
package/lib/index.js CHANGED
@@ -3,8 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.NewAuthService = exports.Config = exports.inject = exports.name = void 0;
4
4
  exports.apply = apply;
5
5
  const koishi_1 = require("koishi");
6
+ const path_1 = require("path");
6
7
  exports.name = 'new-auth';
7
- exports.inject = ['database'];
8
+ exports.inject = { required: ['database'], optional: ['console'] };
8
9
  const BUILTIN_ROLES = [
9
10
  {
10
11
  id: 'bot-admin',
@@ -125,6 +126,9 @@ function apply(ctx, config) {
125
126
  return service.intercept(argv);
126
127
  });
127
128
  registerManagementCommands(ctx, config, service);
129
+ ctx.inject(['console'], (ctx) => {
130
+ registerConsole(ctx, service);
131
+ });
128
132
  }
129
133
  class NewAuthService {
130
134
  constructor(ctx, config) {
@@ -290,6 +294,62 @@ class NewAuthService {
290
294
  const records = await this.ctx.database.get('new_auth_role', {});
291
295
  return records.sort((a, b) => Number(a.builtin) - Number(b.builtin) || a.id.localeCompare(b.id));
292
296
  }
297
+ async listPolicies() {
298
+ return this.ctx.database.get('new_auth_policy', {});
299
+ }
300
+ async listMembers() {
301
+ return this.ctx.database.get('new_auth_role_member', {});
302
+ }
303
+ async listScopes() {
304
+ const scopes = [{ id: 'global', name: '全局默认', type: 'global' }];
305
+ const seen = new Set(['global']);
306
+ const channels = await this.ctx.database.get('channel', {}, ['id', 'platform', 'guildId']);
307
+ for (const channel of channels) {
308
+ const guildId = channel.guildId || channel.id;
309
+ if (!channel.platform || !guildId)
310
+ continue;
311
+ const id = `guild:${channel.platform}:${guildId}`;
312
+ if (seen.has(id))
313
+ continue;
314
+ seen.add(id);
315
+ scopes.push({
316
+ id,
317
+ name: `${channel.platform}:${guildId}`,
318
+ type: 'guild',
319
+ });
320
+ }
321
+ return scopes;
322
+ }
323
+ async getConsoleData() {
324
+ const [roles, commands, policies, members, scopes] = await Promise.all([
325
+ this.listRoles(),
326
+ this.listCommands({ all: true }),
327
+ this.listPolicies(),
328
+ this.listMembers(),
329
+ this.listScopes(),
330
+ ]);
331
+ const globalAllow = new Set(policies
332
+ .filter(policy => policy.scope === 'global' && policy.state === 'allow')
333
+ .map(policy => `${policy.roleId}:${policy.commandId}`));
334
+ return {
335
+ roles: roles.map(role => ({
336
+ ...role,
337
+ allowCount: role.id === 'bot-admin'
338
+ ? commands.filter(command => command.status !== 'disabled').length
339
+ : commands.filter(command => globalAllow.has(`${role.id}:${command.id}`)).length,
340
+ })),
341
+ commands: commands.map(command => ({
342
+ ...command,
343
+ suggestion: getLegacySuggestion(command.legacyAuthority),
344
+ autoAssign: this.getAutoAssignSuggestion(command),
345
+ })),
346
+ policies,
347
+ members,
348
+ scopes,
349
+ pendingCount: commands.filter(command => command.status === 'pending').length,
350
+ autoAssignAvailable: true,
351
+ };
352
+ }
293
353
  async addBotAdmin(uid) {
294
354
  const parsed = parseUid(uid);
295
355
  await this.ensureRoleMember('bot-admin', parsed.platform, parsed.userId, 'global');
@@ -319,7 +379,7 @@ class NewAuthService {
319
379
  updatedAt: now,
320
380
  };
321
381
  if (existing) {
322
- await this.ctx.database.set('new_auth_role', id, record);
382
+ await this.ctx.database.set('new_auth_role', id, omitPrimary(record, 'id'));
323
383
  }
324
384
  else {
325
385
  await this.ctx.database.create('new_auth_role', record);
@@ -338,6 +398,18 @@ class NewAuthService {
338
398
  scope,
339
399
  });
340
400
  }
401
+ async copyRolePolicies(sourceRoleId, targetRoleId, scope = 'global') {
402
+ await this.ensureRoleExists(sourceRoleId);
403
+ await this.ensureRoleExists(targetRoleId);
404
+ const policies = await this.ctx.database.get('new_auth_policy', {
405
+ scope,
406
+ roleId: sourceRoleId,
407
+ });
408
+ for (const policy of policies) {
409
+ await this.setPolicy(scope, targetRoleId, policy.commandId, policy.state);
410
+ }
411
+ return policies.length;
412
+ }
341
413
  async setCommandStatus(input, status) {
342
414
  const command = await this.resolveCommandInput(input);
343
415
  await this.ctx.database.set('new_auth_command', command.id, {
@@ -347,6 +419,15 @@ class NewAuthService {
347
419
  this.commandCache.delete(command.id);
348
420
  return command;
349
421
  }
422
+ async setCommandGuildOverride(input, allowGuildOverride) {
423
+ const command = await this.resolveCommandInput(input);
424
+ await this.ctx.database.set('new_auth_command', command.id, {
425
+ allowGuildOverride,
426
+ updatedAt: new Date(),
427
+ });
428
+ this.commandCache.delete(command.id);
429
+ return command;
430
+ }
350
431
  async setCommandPolicy(scope, roleId, input, state) {
351
432
  const command = await this.resolveCommandInput(input);
352
433
  await this.setPolicy(scope, roleId, command.id, state);
@@ -355,6 +436,40 @@ class NewAuthService {
355
436
  }
356
437
  return command;
357
438
  }
439
+ async applySuggestedPolicy(input, scope = 'global', mode = 'legacy') {
440
+ const command = await this.resolveCommandInput(input);
441
+ const suggestion = mode === 'advisor'
442
+ ? this.getAutoAssignSuggestion(command)
443
+ : getLegacySuggestion(command.legacyAuthority);
444
+ await this.applyRolesToCommand(scope, command.id, suggestion.roles);
445
+ if (command.status === 'pending') {
446
+ await this.setCommandStatus(command.id, 'configured');
447
+ }
448
+ return command;
449
+ }
450
+ async autoAssignPending(scope = 'global', mode = 'advisor') {
451
+ const commands = await this.listCommands({ pending: true });
452
+ for (const command of commands) {
453
+ await this.applySuggestedPolicy(command.id, scope, mode);
454
+ }
455
+ return commands.length;
456
+ }
457
+ async assignPluginCommands(plugin, scope = 'global', target = 'legacy') {
458
+ const commands = (await this.listCommands({ all: true }))
459
+ .filter(command => command.plugin === plugin);
460
+ for (const command of commands) {
461
+ if (target === 'legacy') {
462
+ await this.applySuggestedPolicy(command.id, scope, 'legacy');
463
+ }
464
+ else {
465
+ await this.applyRolesToCommand(scope, command.id, getAssignTargetRoles(target));
466
+ if (command.status === 'pending') {
467
+ await this.setCommandStatus(command.id, 'configured');
468
+ }
469
+ }
470
+ }
471
+ return commands.length;
472
+ }
358
473
  async getCommand(input) {
359
474
  const cached = this.commandCache.get(input);
360
475
  if (cached)
@@ -375,13 +490,22 @@ class NewAuthService {
375
490
  updatedAt: now,
376
491
  };
377
492
  if (existing) {
378
- await this.ctx.database.set('new_auth_role', role.id, record);
493
+ await this.ctx.database.set('new_auth_role', role.id, omitPrimary(record, 'id'));
379
494
  }
380
495
  else {
381
496
  await this.ctx.database.create('new_auth_role', record);
382
497
  }
383
498
  }
384
499
  }
500
+ async applyRolesToCommand(scope, commandId, roleIds) {
501
+ const roles = await this.listRoles();
502
+ const knownRoles = new Set(roles.map(role => role.id));
503
+ for (const roleId of roleIds) {
504
+ if (!knownRoles.has(roleId))
505
+ continue;
506
+ await this.setPolicy(scope, roleId, commandId, 'allow');
507
+ }
508
+ }
385
509
  createCommandRecord(command, now) {
386
510
  const plugin = inferPlugin(command);
387
511
  const legacyAuthority = inferLegacyAuthority(command);
@@ -444,16 +568,84 @@ class NewAuthService {
444
568
  });
445
569
  return global?.state ?? 'inherit';
446
570
  }
571
+ getAutoAssignSuggestion(command) {
572
+ const text = normalizeKey([
573
+ command.commandPath,
574
+ command.name,
575
+ command.plugin,
576
+ command.description,
577
+ command.aliases.join(' '),
578
+ ].join(' '));
579
+ const dangerous = [
580
+ 'plugin', 'market', 'install', 'uninstall', 'reload', 'restart',
581
+ 'config', 'database', 'db', 'file', 'delete', 'remove', 'exec',
582
+ 'eval', 'shell', 'token', 'env', 'broadcast',
583
+ '插件', '市场', '安装', '卸载', '重载', '重启', '配置', '数据库',
584
+ '文件', '删除', '执行', '脚本', '令牌', '环境变量', '广播',
585
+ ];
586
+ if (dangerous.some(keyword => text.includes(keyword))) {
587
+ return {
588
+ kind: 'admin',
589
+ roles: ['bot-admin'],
590
+ label: '仅 Bot 管理员',
591
+ reason: '命中实例级或高危操作关键词。',
592
+ };
593
+ }
594
+ const moderation = [
595
+ 'ban', 'mute', 'kick', 'warn', 'recall', 'approve', 'blacklist',
596
+ '禁言', '踢', '封禁', '警告', '撤回', '审核', '黑名单',
597
+ ];
598
+ if (moderation.some(keyword => text.includes(keyword))) {
599
+ return {
600
+ kind: 'moderation',
601
+ roles: ['guild-admin', 'guild-owner'],
602
+ label: '群管理员和群主',
603
+ reason: '命中群管理操作关键词。',
604
+ };
605
+ }
606
+ const ownerOnly = ['welcome', 'setting', 'notice', 'announce', '欢迎', '公告', '群设置'];
607
+ if (ownerOnly.some(keyword => text.includes(keyword))) {
608
+ return {
609
+ kind: 'owner',
610
+ roles: ['guild-owner'],
611
+ label: '群主',
612
+ reason: '更适合群主进行群内自治配置。',
613
+ };
614
+ }
615
+ const publicUse = [
616
+ 'help', 'sign', 'rank', 'weather', 'search', 'music', 'play', 'query',
617
+ '帮助', '签到', '排行', '天气', '搜索', '点歌', '播放', '查询',
618
+ ];
619
+ if (publicUse.some(keyword => text.includes(keyword))) {
620
+ return {
621
+ kind: 'public',
622
+ roles: ['guild-member', 'guild-admin', 'guild-owner'],
623
+ label: '群成员及以上',
624
+ reason: '命中普通用户功能关键词。',
625
+ };
626
+ }
627
+ const fallback = getLegacySuggestion(command.legacyAuthority);
628
+ return {
629
+ kind: 'legacy',
630
+ roles: fallback.roles,
631
+ label: fallback.label,
632
+ reason: '未命中明确语义,回退到旧 authority 建议。',
633
+ };
634
+ }
447
635
  async ensureRoleMember(roleId, platform, userId, scope) {
448
- const [role] = await this.ctx.database.get('new_auth_role', { id: roleId });
449
- if (!role)
450
- throw new Error(`role not found: ${roleId}`);
636
+ await this.ensureRoleExists(roleId);
451
637
  const query = { roleId, platform, userId, scope };
452
638
  const [existing] = await this.ctx.database.get('new_auth_role_member', query);
453
639
  if (!existing) {
454
640
  await this.ctx.database.create('new_auth_role_member', { ...query, createdAt: new Date() });
455
641
  }
456
642
  }
643
+ async ensureRoleExists(roleId) {
644
+ const [role] = await this.ctx.database.get('new_auth_role', { id: roleId });
645
+ if (!role)
646
+ throw new Error(`role not found: ${roleId}`);
647
+ return role;
648
+ }
457
649
  hasPlatformRole(session, roleNames) {
458
650
  const values = new Set();
459
651
  const author = session.author;
@@ -486,6 +678,71 @@ class NewAuthService {
486
678
  }
487
679
  }
488
680
  exports.NewAuthService = NewAuthService;
681
+ function createConsoleServiceClass(service) {
682
+ const { DataService } = require('@koishijs/plugin-console');
683
+ return class NewAuthConsoleDataService extends DataService {
684
+ constructor(ctx) {
685
+ super(ctx, 'newauth', {
686
+ immediate: true,
687
+ authority: 4,
688
+ });
689
+ }
690
+ async get(_forced, _client) {
691
+ return service.getConsoleData();
692
+ }
693
+ };
694
+ }
695
+ function registerConsole(ctx, service) {
696
+ ctx.console.addEntry({
697
+ dev: (0, path_1.resolve)(__dirname, '../client/index.ts'),
698
+ prod: (0, path_1.resolve)(__dirname, '../dist'),
699
+ });
700
+ ctx.plugin(createConsoleServiceClass(service));
701
+ const ok = async (task) => {
702
+ await task;
703
+ ctx.console.refresh('newauth');
704
+ return { success: true };
705
+ };
706
+ ctx.console.addListener('newauth/getData', async () => {
707
+ return service.getConsoleData();
708
+ }, { authority: 4 });
709
+ ctx.console.addListener('newauth/setPolicy', async (payload) => {
710
+ return ok(service.setCommandPolicy(payload.scope, payload.roleId, payload.commandId, payload.state));
711
+ }, { authority: 4 });
712
+ ctx.console.addListener('newauth/setCommandStatus', async (payload) => {
713
+ return ok(service.setCommandStatus(payload.commandId, payload.status));
714
+ }, { authority: 4 });
715
+ ctx.console.addListener('newauth/setGuildOverride', async (payload) => {
716
+ return ok(service.setCommandGuildOverride(payload.commandId, payload.allowGuildOverride));
717
+ }, { authority: 4 });
718
+ ctx.console.addListener('newauth/applySuggestion', async (payload) => {
719
+ return ok(service.applySuggestedPolicy(payload.commandId, payload.scope, payload.mode));
720
+ }, { authority: 4 });
721
+ ctx.console.addListener('newauth/autoAssignPending', async (payload = {}) => {
722
+ const count = await service.autoAssignPending(payload.scope, payload.mode);
723
+ ctx.console.refresh('newauth');
724
+ return { success: true, count };
725
+ }, { authority: 4 });
726
+ ctx.console.addListener('newauth/createRole', async (payload) => {
727
+ return ok(service.createCustomRole(payload.id, payload.name, payload.scopeType));
728
+ }, { authority: 4 });
729
+ ctx.console.addListener('newauth/addMember', async (payload) => {
730
+ return ok(service.addRoleMember(payload.roleId, payload.uid, payload.scope));
731
+ }, { authority: 4 });
732
+ ctx.console.addListener('newauth/removeMember', async (payload) => {
733
+ return ok(service.removeRoleMember(payload.roleId, payload.uid, payload.scope));
734
+ }, { authority: 4 });
735
+ ctx.console.addListener('newauth/copyRolePolicies', async (payload) => {
736
+ const count = await service.copyRolePolicies(payload.sourceRoleId, payload.targetRoleId, payload.scope);
737
+ ctx.console.refresh('newauth');
738
+ return { success: true, count };
739
+ }, { authority: 4 });
740
+ ctx.console.addListener('newauth/assignPluginCommands', async (payload) => {
741
+ const count = await service.assignPluginCommands(payload.plugin, payload.scope, payload.target);
742
+ ctx.console.refresh('newauth');
743
+ return { success: true, count };
744
+ }, { authority: 4 });
745
+ }
489
746
  function registerManagementCommands(ctx, config, service) {
490
747
  const authority = config.legacyAdminAuthority;
491
748
  ctx.command('newauth', '管理新权限系统', { authority })
@@ -575,6 +832,19 @@ function registerManagementCommands(ctx, config, service) {
575
832
  await service.removeRoleMember(roleId, uid, scope);
576
833
  return `已移除成员:${roleId} ${uid} ${scope}`;
577
834
  });
835
+ ctx.command('newauth.role.copy <sourceRoleId> <targetRoleId> [scope]', '复制角色权限', { authority })
836
+ .action(async (_, sourceRoleId, targetRoleId, scope = 'global') => {
837
+ const count = await service.copyRolePolicies(sourceRoleId, targetRoleId, scope);
838
+ return `已复制 ${count} 条策略:${sourceRoleId} -> ${targetRoleId} (${scope})`;
839
+ });
840
+ ctx.command('newauth.plugin.assign <plugin> <target> [scope]', '批量配置插件指令', { authority })
841
+ .action(async (_, plugin, target, scope = 'global') => {
842
+ if (!['legacy', 'bot-admin', 'guild-owner', 'guild-admin', 'guild-member'].includes(target)) {
843
+ return 'target 只能是 legacy、bot-admin、guild-owner、guild-admin 或 guild-member。';
844
+ }
845
+ const count = await service.assignPluginCommands(plugin, scope, target);
846
+ return `已批量配置 ${count} 个指令:${plugin} -> ${target} (${scope})`;
847
+ });
578
848
  }
579
849
  function inferLegacyAuthority(command) {
580
850
  if (typeof command.config.authority === 'number')
@@ -586,6 +856,49 @@ function inferLegacyAuthority(command) {
586
856
  }
587
857
  return 1;
588
858
  }
859
+ function getLegacySuggestion(authority) {
860
+ if (authority <= 0) {
861
+ return {
862
+ roles: ['guest', 'guild-member', 'guild-admin', 'guild-owner'],
863
+ label: '访客/群成员及以上',
864
+ };
865
+ }
866
+ if (authority === 1) {
867
+ return {
868
+ roles: ['guild-member', 'guild-admin', 'guild-owner'],
869
+ label: '群成员及以上',
870
+ };
871
+ }
872
+ if (authority === 2) {
873
+ return {
874
+ roles: ['guild-admin', 'guild-owner'],
875
+ label: '群管理员及以上',
876
+ };
877
+ }
878
+ if (authority === 3) {
879
+ return {
880
+ roles: ['guild-owner'],
881
+ label: '群主',
882
+ };
883
+ }
884
+ return {
885
+ roles: ['bot-admin'],
886
+ label: '仅 Bot 管理员',
887
+ };
888
+ }
889
+ function getAssignTargetRoles(target) {
890
+ if (target === 'guild-member')
891
+ return ['guild-member', 'guild-admin', 'guild-owner'];
892
+ if (target === 'guild-admin')
893
+ return ['guild-admin', 'guild-owner'];
894
+ if (target === 'guild-owner')
895
+ return ['guild-owner'];
896
+ return ['bot-admin'];
897
+ }
898
+ function omitPrimary(record, key) {
899
+ const { [key]: _value, ...rest } = record;
900
+ return rest;
901
+ }
589
902
  function inferPlugin(command) {
590
903
  const source = command.caller?.scope || command.ctx?.scope;
591
904
  const plugin = source?.plugin?.name || source?.uid || source?.id || 'unknown';
package/package.json CHANGED
@@ -1,16 +1,19 @@
1
1
  {
2
2
  "name": "koishi-plugin-new-auth",
3
- "version": "0.1.0",
3
+ "version": "0.3.1",
4
4
  "description": "Role and scope based command permission layer for Koishi.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
7
7
  "files": [
8
8
  "lib",
9
+ "dist",
9
10
  "README.md",
10
11
  "newauth.md"
11
12
  ],
12
13
  "scripts": {
13
- "build": "tsc -p tsconfig.json",
14
+ "build": "npm run build:server && npm run build:client",
15
+ "build:server": "tsc -p tsconfig.json",
16
+ "build:client": "node scripts/build-client.mjs",
14
17
  "typecheck": "tsc -p tsconfig.json --noEmit",
15
18
  "prepack": "npm run build"
16
19
  },
@@ -23,9 +26,17 @@
23
26
  ],
24
27
  "license": "MIT",
25
28
  "peerDependencies": {
29
+ "@koishijs/plugin-console": "^5.30.0",
26
30
  "koishi": "^4.18.0"
27
31
  },
32
+ "peerDependenciesMeta": {
33
+ "@koishijs/plugin-console": {
34
+ "optional": true
35
+ }
36
+ },
28
37
  "devDependencies": {
38
+ "@koishijs/client": "^5.30.11",
39
+ "@koishijs/plugin-console": "^5.30.11",
29
40
  "@types/node": "^22.0.0",
30
41
  "koishi": "4.18.11",
31
42
  "typescript": "^5.8.0"