@koishi-ce/plugin-status 1.0.0 → 1.1.0

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 ADDED
@@ -0,0 +1,123 @@
1
+ # @koishi-ce/plugin-status
2
+
3
+ **简体中文** | [English](#english)
4
+
5
+ 仪表盘插件,移植自上游 [koishijs/webui](https://github.com/koishijs/webui) 的 `plugins/status`。它采集运行环境信息与性能数据,在控制台各处展示机器人的运行状态。依赖 console 服务。
6
+
7
+ ## 功能与页面
8
+
9
+ 本插件没有独立路由,全部展示经插槽挂载:
10
+
11
+ - 状态栏右侧:机器人状态指示灯与 CPU / 内存负载。
12
+ - 状态栏左侧:版本号等运行环境信息。
13
+ - `analytic-number` 数值卡插槽:当前 / 近期的消息量数值卡(供 analytics 插件的首页数值区复用)。
14
+ - 插件详情页:该插件所辖机器人的预览。
15
+
16
+ 数据服务:
17
+
18
+ - `envinfo`:一次性采集 OS、CPU、运行时版本、包管理器与 Koishi 生态版本信息,结果在进程生命周期内永久缓存。
19
+ - `status`:按 `tickInterval` 周期采集并推送——CPU 负载率与内存负载率(各为「进程, 整机」二元组)、各机器人最近一分钟收发消息量的滑窗计数(每秒一槽、共 60 槽的滑动窗口)。
20
+
21
+ ## 指令
22
+
23
+ | 指令 | 说明 |
24
+ | --- | --- |
25
+ | `status` | 查看运行状态:逐个输出各机器人状态,末尾附 CPU / 内存使用率摘要 |
26
+
27
+ ## 配置项
28
+
29
+ | 配置项 | 类型 | 默认值 | 说明 |
30
+ | --- | --- | --- | --- |
31
+ | `tickInterval` | natural(role ms) | 5 秒 | 性能数据推送的时间间隔 |
32
+
33
+ 客户端本地设置(控制台「机器人设置」面板):
34
+
35
+ | 配置项 | 类型 | 默认值 | 说明 |
36
+ | --- | --- | --- | --- |
37
+ | `mergeThreshold` | number | 10 | 机器人数量超过该值时合并显示状态指示灯 |
38
+
39
+ ## 用法
40
+
41
+ 需要先启用 `console`(@koishi-ce/plugin-console)。安装:
42
+
43
+ ```bash
44
+ bun add @koishi-ce/plugin-status
45
+ ```
46
+
47
+ 也可以在控制台的插件市场中直接安装。随后在配置文件中启用:
48
+
49
+ ```yaml
50
+ plugins:
51
+ status: {}
52
+ ```
53
+
54
+ ## 备注
55
+
56
+ - `status` 指令的输出含各机器人的在线状态(运行中 / 离线 / 正在连接等)与整体 CPU / 内存使用率(进程与整机两个口径)。
57
+ - 机器人登录、登出与更新事件会触发防抖刷新。
58
+ - 不建表。
59
+
60
+ ## 许可证
61
+
62
+ [AGPL-3.0](https://github.com/Koishi-CE/koishi/blob/main/LICENSES/AGPL-3.0.txt)。版权归 Shigma 及 Koishijs 贡献者(上游)与 Koishi-CE 贡献者,见 [NOTICE](https://github.com/Koishi-CE/koishi/blob/main/NOTICE)。上游仓库:[koishijs/webui](https://github.com/koishijs/webui) 的 `plugins/status`。
63
+
64
+ ---
65
+
66
+ ## English
67
+
68
+ Dashboard plugin, ported from `plugins/status` of the upstream [koishijs/webui](https://github.com/koishijs/webui) repository. It collects environment info and performance data, displaying bot status across the console. Depends on the console service.
69
+
70
+ ## Features and Pages
71
+
72
+ No standalone route; everything is mounted via slots:
73
+
74
+ - Status bar (right): bot status indicators and CPU / memory load.
75
+ - Status bar (left): runtime environment and version info.
76
+ - `analytic-number` slot: message-rate number cards reused by the analytics plugin's home page.
77
+ - Plugin details slot: bot preview for the plugin.
78
+
79
+ Data services:
80
+
81
+ - `envinfo`: one-shot collection of OS, CPU, runtime versions, package manager, and Koishi ecosystem versions, cached for the process lifetime.
82
+ - `status`: periodic collection at `tickInterval` — CPU and memory load rates (each a `[app, total]` pair) and per-bot send/receive message counts over the last minute (60-slot sliding window, one slot per second).
83
+
84
+ ## Commands
85
+
86
+ | Command | Description |
87
+ | --- | --- |
88
+ | `status` | Show runtime status: each bot's state, followed by a CPU / memory usage summary |
89
+
90
+ ## Configuration
91
+
92
+ | Option | Type | Default | Description |
93
+ | --- | --- | --- | --- |
94
+ | `tickInterval` | natural (role ms) | 5 s | Interval for pushing performance data |
95
+
96
+ Client-side local setting ("Bot Settings" panel):
97
+
98
+ | Option | Type | Default | Description |
99
+ | --- | --- | --- | --- |
100
+ | `mergeThreshold` | number | 10 | Merge status indicators when the bot count exceeds this value |
101
+
102
+ ## Usage
103
+
104
+ Requires `console` (@koishi-ce/plugin-console) to be enabled first.
105
+
106
+ ```bash
107
+ bun add @koishi-ce/plugin-status
108
+ ```
109
+
110
+ The plugin can also be installed from the console plugin market, then enabled in the config file:
111
+
112
+ ```yaml
113
+ plugins:
114
+ status: {}
115
+ ```
116
+
117
+ ## Notes
118
+
119
+ - Login events trigger debounced refreshes. No tables.
120
+
121
+ ## License
122
+
123
+ [AGPL-3.0](https://github.com/Koishi-CE/koishi/blob/main/LICENSES/AGPL-3.0.txt). Copyright belongs to Shigma and Koishijs contributors (upstream) and Koishi-CE contributors; see [NOTICE](https://github.com/Koishi-CE/koishi/blob/main/NOTICE). Upstream: [koishijs/webui](https://github.com/koishijs/webui), `plugins/status`.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import{Schema as e,Universal as t,capitalize as n,icons as r,message as i,router as a,store as o,useConfig as ee,withProxy as s}from"@koishi-ce/client";import{Fragment as c,computed as l,createBlock as u,createCommentVNode as d,createElementBlock as f,createElementVNode as p,createTextVNode as m,createVNode as h,defineComponent as g,inject as _,normalizeClass as v,normalizeStyle as y,openBlock as b,renderList as x,resolveComponent as S,toDisplayString as C,unref as w,withCtx as T}from"vue";var te=g({__name:`analytics`,setup(e){let t=_(`component:analytic-number`),n=l(()=>Object.values(o.status.bots).reduce((e,t)=>e+t.messageReceived,0)/60),r=l(()=>Object.values(o.analytics.messageByDate).slice(-7).reduce((e,t)=>e+t.receive,0)/7/24/60/60);return(e,i)=>(b(),u(w(t),{icon:`analytic:pulse`,title:`当前 QPS`},{default:T(()=>[m(C(+n.value.toFixed(2)),1)]),"footer-left":T(()=>[...i[0]||=[m(`近期 QPS`,-1)]]),"footer-right":T(()=>[m(C(+r.value.toFixed(2)),1)]),_:1}))}}),E=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},D={},O={class:`status-light`};function ne(e,t){return b(),f(`span`,O)}var k=E(D,[[`render`,ne],[`__scopeId`,`data-v-ea7f6fce`]]),A={OFFLINE:0,ONLINE:1,CONNECT:2,DISCONNECT:3,RECONNECT:4};function j(e){switch(e){case A.OFFLINE:return`offline`;case A.ONLINE:return`online`;case A.CONNECT:return`connect`;case A.DISCONNECT:return`disconnect`;case A.RECONNECT:return`reconnect`}}var M={class:`bot-view`},N={class:`info`},P=[`title`],F=[`title`],I={class:`truncate cur-frequency`},L={style:{"margin-right":`8px`}},R=E(g({__name:`preview`,props:{data:{}},setup(e){let n={[t.Status.ONLINE]:`运行中`,[t.Status.OFFLINE]:`离线`,[t.Status.CONNECT]:`正在连接`,[t.Status.RECONNECT]:`正在重连`,[t.Status.DISCONNECT]:`正在断开`};return(t,r)=>{let i=S(`el-tooltip`),a=S(`k-icon`);return b(),f(`section`,M,[p(`div`,{class:`avatar`,style:y({backgroundImage:`url(${w(s)(e.data.user.avatar)})`}),onClick:r[0]||=e=>t.$emit(`avatar-click`)},[h(i,{content:n[e.data.status],placement:`right`},{default:T(()=>[h(k,{class:v(w(j)(e.data.status))},null,8,[`class`])]),_:1},8,[`content`])],4),p(`div`,N,[p(`div`,{class:`truncate`,title:e.data.user.name},[h(a,{name:`robot`}),m(C(e.data.user.name),1)],8,P),p(`div`,{class:`truncate`,title:e.data.platform},[h(a,{name:`platform`}),m(C(e.data.platform),1)],8,F),p(`div`,I,[p(`span`,L,[h(a,{name:`arrow-up`}),p(`span`,null,C(e.data.messageSent)+`/min`,1)]),p(`span`,null,[h(a,{name:`arrow-down`}),p(`span`,null,C(e.data.messageReceived)+`/min`,1)])])])])}}}),[[`__scopeId`,`data-v-1cbf178f`]]),z={key:0,class:`el-popper__empty`},B={class:`count`},V=E(g({__name:`index`,setup(e){let t=ee(),n=l(()=>{let e={};for(let t of Object.values(o.status.bots)){let n=j(t.status);e[n]=(e[n]||0)+1}return Object.fromEntries(Object.entries(e).sort((e,t)=>e[0].localeCompare(t[0])))}),r=l(()=>Object.values(o.status.bots).reduce((e,t)=>e+t.messageSent,0)),i=l(()=>Object.values(o.status.bots).reduce((e,t)=>e+t.messageReceived,0));return(e,ee)=>{let s=S(`k-icon`),l=S(`k-status`);return w(o).status?(b(),u(l,{key:0},{tooltip:T(()=>[Object.values(w(o).status.bots).length?d(`v-if`,!0):(b(),f(`span`,z)),(b(!0),f(c,null,x(w(o).status.bots,(e,t)=>(b(),u(R,{key:t,data:e,class:v({"has-link":e.paths?.length}),onClick:t=>w(a).push(`/plugins/`+e.paths[0].replace(/\./,`/`))},null,8,[`data`,`class`,`onClick`]))),128))]),default:T(()=>[(b(!0),f(c,null,x(n.value,(e,n)=>(b(),f(c,{key:n},[e>(w(t).mergeThreshold??10)?(b(),f(c,{key:0},[h(k,{class:v(n)},null,8,[`class`]),p(`span`,B,`×`+C(e),1)],64)):(b(!0),f(c,{key:1},x(Array(e),(e,t)=>(b(),u(k,{key:t,class:v(n)},null,8,[`class`]))),128))],64))),128)),h(s,{name:`arrow-up`}),p(`span`,null,C(r.value)+`/min`,1),h(s,{name:`arrow-down`}),p(`span`,null,C(i.value)+`/min`,1)]),_:1})):d(`v-if`,!0)}}}),[[`__scopeId`,`data-v-e9800c9a`]]),H=e=>{e.slot({type:`status-right`,component:V})},U={class:`bots-container flex flex-wrap gap-4`},W=E(g({__name:`config`,setup(e){let t=_(`manager.settings.current`),n=l(()=>Object.values(o.status?.bots||{}).filter(e=>e.paths?.includes(t.value.path)));return(e,t)=>n.value?.length?(b(),f(c,{key:0},[t[0]||=p(`h2`,{class:`k-schema-header`},` 机器人 `,-1),p(`div`,U,[(b(!0),f(c,null,x(n.value,(e,t)=>(b(),u(R,{key:t,data:e},null,8,[`data`]))),128))])],64)):d(`v-if`,!0)}}),[[`__scopeId`,`data-v-19020f78`]]);async function G(e){try{return navigator.clipboard.writeText(e)}catch{let t=document.createElement(`textarea`),n=document.activeElement;t.value=e,t.setAttribute(`readonly`,``),t.style.contain=`strict`,t.style.position=`absolute`,t.style.left=`-9999px`,t.style.fontSize=`12pt`;let r=document.getSelection(),i=r?r.rangeCount>0&&r.getRangeAt(0):null;document.body.appendChild(t),t.select(),t.selectionStart=0,t.selectionEnd=e.length,document.execCommand(`copy`),document.body.removeChild(t),i&&r&&(r.removeAllRanges(),r.addRange(i)),n&&n.focus()}}var K={class:`title`},q=E(g({__name:`envinfo`,setup(e){async function t(){await G(Object.entries(o.envinfo).map(([e,t])=>`${n(e)}:\n`+Object.entries(t).map(([e,t])=>` ${e}: ${t}`).join(`
1
+ import{Schema as e,Universal as t,capitalize as n,icons as r,message as i,router as a,store as o,useConfig as ee,withProxy as s}from"@koishi-ce/client";import{Fragment as c,computed as l,createBlock as u,createCommentVNode as d,createElementBlock as f,createElementVNode as p,createTextVNode as m,createVNode as h,defineComponent as g,inject as _,normalizeClass as v,normalizeStyle as y,openBlock as b,renderList as x,resolveComponent as S,toDisplayString as C,unref as w,withCtx as T}from"vue";var te=g({__name:`analytics`,setup(e){let t=_(`component:analytic-number`),n=l(()=>Object.values(o.status.bots).reduce((e,t)=>e+t.messageReceived,0)/60),r=l(()=>Object.values(o.analytics.messageByDate).slice(-7).reduce((e,t)=>e+t.receive,0)/7/24/60/60);return(e,i)=>(b(),u(w(t),{icon:`analytic:pulse`,title:`当前 QPS`},{default:T(()=>[m(C(+n.value.toFixed(2)),1)]),"footer-left":T(()=>[...i[0]||=[m(`近期 QPS`,-1)]]),"footer-right":T(()=>[m(C(+r.value.toFixed(2)),1)]),_:1}))}}),E=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},D={},O={class:`status-light`};function ne(e,t){return b(),f(`span`,O)}var k=E(D,[[`render`,ne],[`__scopeId`,`data-v-bf35056f`]]),A={OFFLINE:0,ONLINE:1,CONNECT:2,DISCONNECT:3,RECONNECT:4};function j(e){switch(e){case A.OFFLINE:return`offline`;case A.ONLINE:return`online`;case A.CONNECT:return`connect`;case A.DISCONNECT:return`disconnect`;case A.RECONNECT:return`reconnect`}}var M={class:`bot-view`},N={class:`info`},P=[`title`],F=[`title`],I={class:`truncate cur-frequency`},L={style:{"margin-right":`8px`}},R=E(g({__name:`preview`,props:{data:{}},setup(e){let n={[t.Status.ONLINE]:`运行中`,[t.Status.OFFLINE]:`离线`,[t.Status.CONNECT]:`正在连接`,[t.Status.RECONNECT]:`正在重连`,[t.Status.DISCONNECT]:`正在断开`};return(t,r)=>{let i=S(`el-tooltip`),a=S(`k-icon`);return b(),f(`section`,M,[p(`div`,{class:`avatar`,style:y({backgroundImage:`url(${w(s)(e.data.user.avatar)})`}),onClick:r[0]||=e=>t.$emit(`avatar-click`)},[h(i,{content:n[e.data.status],placement:`right`},{default:T(()=>[h(k,{class:v(w(j)(e.data.status))},null,8,[`class`])]),_:1},8,[`content`])],4),p(`div`,N,[p(`div`,{class:`truncate`,title:e.data.user.name},[h(a,{name:`robot`}),m(C(e.data.user.name),1)],8,P),p(`div`,{class:`truncate`,title:e.data.platform},[h(a,{name:`platform`}),m(C(e.data.platform),1)],8,F),p(`div`,I,[p(`span`,L,[h(a,{name:`arrow-up`}),p(`span`,null,C(e.data.messageSent)+`/min`,1)]),p(`span`,null,[h(a,{name:`arrow-down`}),p(`span`,null,C(e.data.messageReceived)+`/min`,1)])])])])}}}),[[`__scopeId`,`data-v-0d87ef44`]]),z={key:0,class:`el-popper__empty`},B={class:`count`},V=E(g({__name:`index`,setup(e){let t=ee(),n=l(()=>{let e={};for(let t of Object.values(o.status.bots)){let n=j(t.status);e[n]=(e[n]||0)+1}return Object.fromEntries(Object.entries(e).sort((e,t)=>e[0].localeCompare(t[0])))}),r=l(()=>Object.values(o.status.bots).reduce((e,t)=>e+t.messageSent,0)),i=l(()=>Object.values(o.status.bots).reduce((e,t)=>e+t.messageReceived,0));return(e,ee)=>{let s=S(`k-icon`),l=S(`k-status`);return w(o).status?(b(),u(l,{key:0},{tooltip:T(()=>[Object.values(w(o).status.bots).length?d(`v-if`,!0):(b(),f(`span`,z)),(b(!0),f(c,null,x(w(o).status.bots,(e,t)=>(b(),u(R,{key:t,data:e,class:v({"has-link":e.paths?.length}),onClick:t=>w(a).push(`/plugins/`+e.paths[0].replace(/\./,`/`))},null,8,[`data`,`class`,`onClick`]))),128))]),default:T(()=>[(b(!0),f(c,null,x(n.value,(e,n)=>(b(),f(c,{key:n},[e>(w(t).mergeThreshold??10)?(b(),f(c,{key:0},[h(k,{class:v(n)},null,8,[`class`]),p(`span`,B,`×`+C(e),1)],64)):(b(!0),f(c,{key:1},x(Array(e),(e,t)=>(b(),u(k,{key:t,class:v(n)},null,8,[`class`]))),128))],64))),128)),h(s,{name:`arrow-up`}),p(`span`,null,C(r.value)+`/min`,1),h(s,{name:`arrow-down`}),p(`span`,null,C(i.value)+`/min`,1)]),_:1})):d(`v-if`,!0)}}}),[[`__scopeId`,`data-v-a2e0e762`]]),H=e=>{e.slot({type:`status-right`,component:V})},U={class:`bots-container flex flex-wrap gap-4`},W=E(g({__name:`config`,setup(e){let t=_(`manager.settings.current`),n=l(()=>Object.values(o.status?.bots||{}).filter(e=>e.paths?.includes(t.value.path)));return(e,t)=>n.value?.length?(b(),f(c,{key:0},[t[0]||=p(`h2`,{class:`k-schema-header`},` 机器人 `,-1),p(`div`,U,[(b(!0),f(c,null,x(n.value,(e,t)=>(b(),u(R,{key:t,data:e},null,8,[`data`]))),128))])],64)):d(`v-if`,!0)}}),[[`__scopeId`,`data-v-3117e871`]]);async function G(e){try{return navigator.clipboard.writeText(e)}catch{let t=document.createElement(`textarea`),n=document.activeElement;t.value=e,t.setAttribute(`readonly`,``),t.style.contain=`strict`,t.style.position=`absolute`,t.style.left=`-9999px`,t.style.fontSize=`12pt`;let r=document.getSelection(),i=r?r.rangeCount>0&&r.getRangeAt(0):null;document.body.appendChild(t),t.select(),t.selectionStart=0,t.selectionEnd=e.length,document.execCommand(`copy`),document.body.removeChild(t),i&&r&&(r.removeAllRanges(),r.addRange(i)),n&&n.focus()}}var K={class:`title`},q=E(g({__name:`envinfo`,setup(e){async function t(){await G(Object.entries(o.envinfo).map(([e,t])=>`${n(e)}:\n`+Object.entries(t).map(([e,t])=>` ${e}: ${t}`).join(`
2
2
  `)).join(`
3
3
 
4
- `)),i.success(`已复制环境信息!`)}return(e,r)=>{let i=S(`k-status`);return w(o).envinfo?(b(),u(i,{key:0,class:`version`,onClick:t},{tooltip:T(()=>[(b(!0),f(c,null,x(w(o).envinfo,(e,t)=>(b(),f(`div`,{class:`section`,key:t},[p(`p`,K,C(w(n)(t))+`:`,1),(b(!0),f(c,null,x(e,(e,t)=>(b(),f(`p`,{class:`info`,key:t},C(t)+`: `+C(e),1))),128))]))),128))]),default:T(()=>[m(` Koishi v`+C(w(o).envinfo.koishi.Core)+` `,1)]),_:1})):d(`v-if`,!0)}}}),[[`__scopeId`,`data-v-184a36f6`]]),J={class:`load-bar`},Y={class:`title`},X={class:`body`},Z=E(g({__name:`load-bar`,props:{rate:{},title:{}},setup(e){let t=e;function n(e,t=1){return`${+(e*100).toFixed(t)}%`}let r=[`used`,`app`,`free`],i=l(()=>[t.rate[1]-t.rate[0],t.rate[0],1-t.rate[1]]),a=l(()=>i.value.findIndex(e=>e>=.5)),o=l(()=>`${n(t.rate[0],1)} / ${n(t.rate[1],1)}`);return(t,ee)=>(b(),f(`div`,J,[p(`span`,Y,C(e.title),1),p(`span`,X,[(b(),f(c,null,x(r,(e,t)=>p(`span`,{key:e,class:v([e,`bar`]),style:y({width:n(i.value[t])})},[t===a.value?(b(),f(c,{key:0},[m(C(o.value),1)],64)):a.value===-1&&i.value[t]>=.2?(b(),f(c,{key:1},[m(C(n(i.value[t])),1)],64)):d(`v-if`,!0)],6)),64))])]))}}),[[`__scopeId`,`data-v-c8d81760`]]);function Q(e,t=3){return`${(e*100).toFixed(t)}%`}var re={class:`load`},ie=E(g({__name:`index`,setup(e){return(e,t)=>{let n=S(`k-status`);return w(o).status?(b(),u(n,{key:0},{tooltip:T(()=>[p(`div`,re,[h(Z,{title:`CPU`,rate:w(o).status.cpu},null,8,[`rate`]),h(Z,{title:`内存`,rate:w(o).status.memory},null,8,[`rate`])])]),default:T(()=>[m(` CPU: `+C(w(Q)(w(o).status.cpu[1],0))+` 内存: `+C(w(Q)(w(o).status.memory[1],0)),1)]),_:1})):d(`v-if`,!0)}}}),[[`__scopeId`,`data-v-8c7391c4`]]),ae=e=>{e.slot({type:`status-right`,component:ie})},oe={},se={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 384 512`};function ce(e,t){return b(),f(`svg`,se,[...t[0]||=[p(`path`,{fill:`currentColor`,d:`M377.4 296.6l-168 176C204.8 477.3 198.6 480 192 480s-12.84-2.688-17.38-7.438l-168-176C-2.5 286.1-2.156 271.8 7.438 262.6c9.5-9.156 24.75-8.812 33.94 .8125L168 396.1V56.02c0-13.25 10.75-24.01 23.1-24.01S216 42.77 216 56.02v340.1l126.6-132.7c9.156-9.625 24.41-9.969 33.94-.8125C386.2 271.8 386.5 286.1 377.4 296.6z`},null,-1)]])}var le=E(oe,[[`render`,ce]]),ue={},de={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 384 512`};function fe(e,t){return b(),f(`svg`,de,[...t[0]||=[p(`path`,{fill:`currentColor`,d:`M6.625 215.5l168-176C179.2 34.7 185.4 32.02 192 32.02s12.84 2.688 17.38 7.438l168 176c9.125 9.594 8.781 24.78-.8125 33.94c-9.5 9.156-24.75 8.812-33.94-.8125L216 115.9V456c0 13.25-10.75 23.1-23.1 23.1S168 469.3 168 456V115.9l-126.6 132.7C32.22 258.2 16.97 258.5 7.438 249.4C-2.156 240.2-2.5 225 6.625 215.5z`},null,-1)]])}var pe=E(ue,[[`render`,fe]]),me={},he={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 512 512`};function ge(e,t){return b(),f(`svg`,he,[...t[0]||=[p(`path`,{fill:`currentColor`,d:`M12.41 148.02l232.94 105.67c6.8 3.09 14.49 3.09 21.29 0l232.94-105.67c16.55-7.51 16.55-32.52 0-40.03L266.65 2.31a25.607 25.607 0 0 0-21.29 0L12.41 107.98c-16.55 7.51-16.55 32.53 0 40.04zm487.18 88.28l-58.09-26.33-161.64 73.27c-7.56 3.43-15.59 5.17-23.86 5.17s-16.29-1.74-23.86-5.17L70.51 209.97l-58.1 26.33c-16.55 7.5-16.55 32.5 0 40l232.94 105.59c6.8 3.08 14.49 3.08 21.29 0L499.59 276.3c16.55-7.5 16.55-32.5 0-40zm0 127.8l-57.87-26.23-161.86 73.37c-7.56 3.43-15.59 5.17-23.86 5.17s-16.29-1.74-23.86-5.17L70.29 337.87 12.41 364.1c-16.55 7.5-16.55 32.5 0 40l232.94 105.59c6.8 3.08 14.49 3.08 21.29 0L499.59 404.1c16.55-7.5 16.55-32.5 0-40z`},null,-1)]])}var _e=E(me,[[`render`,ge]]),ve={},ye={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 640 512`};function be(e,t){return b(),f(`svg`,ye,[...t[0]||=[p(`path`,{fill:`currentColor`,d:`M640 240C640 248.8 632.8 256 624 256h-133.1l-60 150c-2.625 6.375-8.375 10.25-16 9.1C408 415.5 402.1 410.6 400.5 403.9l-78.75-315l-82 410.3C238.3 506.5 231.9 511.8 224.4 512H224c-7.25 0-13.75-5-15.5-12.12L147.5 256H16C7.201 256 0 248.8 0 240s7.201-15.1 16-15.1H160c7.375 0 13.75 4.993 15.5 12.12l46.75 187l82-410.3C305.8 5.5 312 .25 319.5 0c8.125 .125 14.25 4.875 16 12.12l84 336l45.63-114.1C467.5 228 473.5 224 480 224h144C632.8 224 640 231.2 640 240z`},null,-1)]])}var $=E(ve,[[`render`,be]]),xe={},Se={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 640 512`};function Ce(e,t){return b(),f(`svg`,Se,[...t[0]||=[p(`path`,{fill:`currentColor`,d:`M32,224H64V416H32A31.96166,31.96166,0,0,1,0,384V256A31.96166,31.96166,0,0,1,32,224Zm512-48V448a64.06328,64.06328,0,0,1-64,64H160a64.06328,64.06328,0,0,1-64-64V176a79.974,79.974,0,0,1,80-80H288V32a32,32,0,0,1,64,0V96H464A79.974,79.974,0,0,1,544,176ZM264,256a40,40,0,1,0-40,40A39.997,39.997,0,0,0,264,256Zm-8,128H192v32h64Zm96,0H288v32h64ZM456,256a40,40,0,1,0-40,40A39.997,39.997,0,0,0,456,256Zm-8,128H384v32h64ZM640,256V384a31.96166,31.96166,0,0,1-32,32H576V224h32A31.96166,31.96166,0,0,1,640,256Z`},null,-1)]])}var we=E(xe,[[`render`,Ce]]);r.register(`arrow-up`,pe),r.register(`arrow-down`,le),r.register(`platform`,_e),r.register(`analytic:pulse`,$),r.register(`robot`,we);var Te=t=>{t.plugin(H),t.plugin(ae),t.slot({type:`status-left`,component:q}),t.slot({type:`analytic-number`,component:te}),t.slot({type:`plugin-details`,component:W,order:-500}),t.settings({id:`status`,schema:e.object({mergeThreshold:e.number().default(10).description(`当机器人的数量超过这个值时将合并显示状态指示灯。`)}).description(`机器人设置`)})};export{Te as default};
4
+ `)),i.success(`已复制环境信息!`)}return(e,r)=>{let i=S(`k-status`);return w(o).envinfo?(b(),u(i,{key:0,class:`version`,onClick:t},{tooltip:T(()=>[(b(!0),f(c,null,x(w(o).envinfo,(e,t)=>(b(),f(`div`,{class:`section`,key:t},[p(`p`,K,C(w(n)(t))+`:`,1),(b(!0),f(c,null,x(e,(e,t)=>(b(),f(`p`,{class:`info`,key:t},C(t)+`: `+C(e),1))),128))]))),128))]),default:T(()=>[m(` Koishi v`+C(w(o).envinfo.koishi.Core)+` `,1)]),_:1})):d(`v-if`,!0)}}}),[[`__scopeId`,`data-v-52da28fa`]]),J={class:`load-bar`},Y={class:`title`},X={class:`body`},Z=E(g({__name:`load-bar`,props:{rate:{},title:{}},setup(e){let t=e;function n(e,t=1){return`${+(e*100).toFixed(t)}%`}let r=[`used`,`app`,`free`],i=l(()=>[t.rate[1]-t.rate[0],t.rate[0],1-t.rate[1]]),a=l(()=>i.value.findIndex(e=>e>=.5)),o=l(()=>`${n(t.rate[0],1)} / ${n(t.rate[1],1)}`);return(t,ee)=>(b(),f(`div`,J,[p(`span`,Y,C(e.title),1),p(`span`,X,[(b(),f(c,null,x(r,(e,t)=>p(`span`,{key:e,class:v([e,`bar`]),style:y({width:n(i.value[t])})},[t===a.value?(b(),f(c,{key:0},[m(C(o.value),1)],64)):a.value===-1&&i.value[t]>=.2?(b(),f(c,{key:1},[m(C(n(i.value[t])),1)],64)):d(`v-if`,!0)],6)),64))])]))}}),[[`__scopeId`,`data-v-c6c8a320`]]);function Q(e,t=3){return`${(e*100).toFixed(t)}%`}var re={class:`load`},ie=E(g({__name:`index`,setup(e){return(e,t)=>{let n=S(`k-status`);return w(o).status?(b(),u(n,{key:0},{tooltip:T(()=>[p(`div`,re,[h(Z,{title:`CPU`,rate:w(o).status.cpu},null,8,[`rate`]),h(Z,{title:`内存`,rate:w(o).status.memory},null,8,[`rate`])])]),default:T(()=>[m(` CPU: `+C(w(Q)(w(o).status.cpu[1],0))+` 内存: `+C(w(Q)(w(o).status.memory[1],0)),1)]),_:1})):d(`v-if`,!0)}}}),[[`__scopeId`,`data-v-7fdf3e68`]]),ae=e=>{e.slot({type:`status-right`,component:ie})},oe={},se={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 384 512`};function ce(e,t){return b(),f(`svg`,se,[...t[0]||=[p(`path`,{fill:`currentColor`,d:`M377.4 296.6l-168 176C204.8 477.3 198.6 480 192 480s-12.84-2.688-17.38-7.438l-168-176C-2.5 286.1-2.156 271.8 7.438 262.6c9.5-9.156 24.75-8.812 33.94 .8125L168 396.1V56.02c0-13.25 10.75-24.01 23.1-24.01S216 42.77 216 56.02v340.1l126.6-132.7c9.156-9.625 24.41-9.969 33.94-.8125C386.2 271.8 386.5 286.1 377.4 296.6z`},null,-1)]])}var le=E(oe,[[`render`,ce]]),ue={},de={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 384 512`};function fe(e,t){return b(),f(`svg`,de,[...t[0]||=[p(`path`,{fill:`currentColor`,d:`M6.625 215.5l168-176C179.2 34.7 185.4 32.02 192 32.02s12.84 2.688 17.38 7.438l168 176c9.125 9.594 8.781 24.78-.8125 33.94c-9.5 9.156-24.75 8.812-33.94-.8125L216 115.9V456c0 13.25-10.75 23.1-23.1 23.1S168 469.3 168 456V115.9l-126.6 132.7C32.22 258.2 16.97 258.5 7.438 249.4C-2.156 240.2-2.5 225 6.625 215.5z`},null,-1)]])}var pe=E(ue,[[`render`,fe]]),me={},he={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 512 512`};function ge(e,t){return b(),f(`svg`,he,[...t[0]||=[p(`path`,{fill:`currentColor`,d:`M12.41 148.02l232.94 105.67c6.8 3.09 14.49 3.09 21.29 0l232.94-105.67c16.55-7.51 16.55-32.52 0-40.03L266.65 2.31a25.607 25.607 0 0 0-21.29 0L12.41 107.98c-16.55 7.51-16.55 32.53 0 40.04zm487.18 88.28l-58.09-26.33-161.64 73.27c-7.56 3.43-15.59 5.17-23.86 5.17s-16.29-1.74-23.86-5.17L70.51 209.97l-58.1 26.33c-16.55 7.5-16.55 32.5 0 40l232.94 105.59c6.8 3.08 14.49 3.08 21.29 0L499.59 276.3c16.55-7.5 16.55-32.5 0-40zm0 127.8l-57.87-26.23-161.86 73.37c-7.56 3.43-15.59 5.17-23.86 5.17s-16.29-1.74-23.86-5.17L70.29 337.87 12.41 364.1c-16.55 7.5-16.55 32.5 0 40l232.94 105.59c6.8 3.08 14.49 3.08 21.29 0L499.59 404.1c16.55-7.5 16.55-32.5 0-40z`},null,-1)]])}var _e=E(me,[[`render`,ge]]),ve={},ye={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 640 512`};function be(e,t){return b(),f(`svg`,ye,[...t[0]||=[p(`path`,{fill:`currentColor`,d:`M640 240C640 248.8 632.8 256 624 256h-133.1l-60 150c-2.625 6.375-8.375 10.25-16 9.1C408 415.5 402.1 410.6 400.5 403.9l-78.75-315l-82 410.3C238.3 506.5 231.9 511.8 224.4 512H224c-7.25 0-13.75-5-15.5-12.12L147.5 256H16C7.201 256 0 248.8 0 240s7.201-15.1 16-15.1H160c7.375 0 13.75 4.993 15.5 12.12l46.75 187l82-410.3C305.8 5.5 312 .25 319.5 0c8.125 .125 14.25 4.875 16 12.12l84 336l45.63-114.1C467.5 228 473.5 224 480 224h144C632.8 224 640 231.2 640 240z`},null,-1)]])}var $=E(ve,[[`render`,be]]),xe={},Se={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 640 512`};function Ce(e,t){return b(),f(`svg`,Se,[...t[0]||=[p(`path`,{fill:`currentColor`,d:`M32,224H64V416H32A31.96166,31.96166,0,0,1,0,384V256A31.96166,31.96166,0,0,1,32,224Zm512-48V448a64.06328,64.06328,0,0,1-64,64H160a64.06328,64.06328,0,0,1-64-64V176a79.974,79.974,0,0,1,80-80H288V32a32,32,0,0,1,64,0V96H464A79.974,79.974,0,0,1,544,176ZM264,256a40,40,0,1,0-40,40A39.997,39.997,0,0,0,264,256Zm-8,128H192v32h64Zm96,0H288v32h64ZM456,256a40,40,0,1,0-40,40A39.997,39.997,0,0,0,456,256Zm-8,128H384v32h64ZM640,256V384a31.96166,31.96166,0,0,1-32,32H576V224h32A31.96166,31.96166,0,0,1,640,256Z`},null,-1)]])}var we=E(xe,[[`render`,Ce]]);r.register(`arrow-up`,pe),r.register(`arrow-down`,le),r.register(`platform`,_e),r.register(`analytic:pulse`,$),r.register(`robot`,we);var Te=t=>{t.plugin(H),t.plugin(ae),t.slot({type:`status-left`,component:q}),t.slot({type:`analytic-number`,component:te}),t.slot({type:`plugin-details`,component:W,order:-500}),t.settings({id:`status`,schema:e.object({mergeThreshold:e.number().default(10).description(`当机器人的数量超过这个值时将合并显示状态指示灯。`)}).description(`机器人设置`)})};export{Te as default};
package/dist/style.css ADDED
@@ -0,0 +1,2 @@
1
+ .status-light[data-v-bf35056f]{transition:var(--color-transition);border-radius:.625rem;width:.625rem;height:.625rem}.status-light.online[data-v-bf35056f]{background-color:var(--k-color-success)}.status-light.connect[data-v-bf35056f],.status-light.reconnect[data-v-bf35056f]{background-color:var(--k-color-warning)}.status-light.error[data-v-bf35056f]{background-color:var(--k-color-danger)!important}.status-light.offline[data-v-bf35056f]{background-color:var(--k-color-disabled)}.status-light+.status-light[data-v-bf35056f]{margin-left:4px}.bot-view[data-v-0d87ef44]{width:15rem;padding:.75rem 1rem;font-size:14px;transition:all .3s;display:flex}.bot-view+.bot-view[data-v-0d87ef44]{border-top:1px solid var(--k-color-divider)}.bot-view.active>div.avatar[data-v-0d87ef44]{border-color:var(--active)}.bot-view>div.avatar[data-v-0d87ef44]{box-sizing:content-box;border:1px solid var(--k-color-divider);background-repeat:no-repeat;background-size:100%;border-radius:100%;flex-shrink:0;width:4rem;height:4rem;transition:all .1s;position:relative}.bot-view>div.avatar .status-light[data-v-0d87ef44]{border:1px solid var(--k-color-divider);width:.875rem;height:.875rem;position:absolute;bottom:-1px;right:-1px}.bot-view>div.info[data-v-0d87ef44]{flex-direction:column;flex-grow:1;justify-content:space-around;margin-left:1.25rem;display:flex;overflow:hidden}.bot-view>div.info .k-icon[data-v-0d87ef44]{text-align:center;vertical-align:-2px;width:20px;margin-right:6px}.bot-view.has-link[data-v-0d87ef44]{cursor:pointer}.bot-view.has-link[data-v-0d87ef44]:hover{background-color:var(--bg1)}.k-status .k-icon[data-v-a2e0e762]{margin-right:4px}.k-status *+.k-icon[data-v-a2e0e762]{margin-left:6px}.k-status .count[data-v-a2e0e762]{letter-spacing:1px;margin:0 4px}.bots-container .bot-view[data-v-3117e871]{background-color:var(--bg0);border-radius:.5rem}p[data-v-52da28fa]{margin:8px;padding:0 6px;font-size:12px;line-height:1}.info[data-v-52da28fa]{padding-left:20px}.load-bar[data-v-c6c8a320]{-webkit-user-select:none;user-select:none;align-items:center;margin:.5rem 0;font-size:.9em;display:flex}.load-bar .title[data-v-c6c8a320]{min-width:3em}.load-bar .body[data-v-c6c8a320]{background-color:var(--k-c-divisor);width:10rem;height:.8rem;transition:var(--color-transition);color:var(--fg1);border-radius:1rem;font-size:10px;display:inline;position:relative;overflow:hidden}.load-bar .bar[data-v-c6c8a320]{float:left;white-space:pre;justify-content:center;align-items:center;height:100%;display:flex;position:relative}.load-bar .used[data-v-c6c8a320]{background-color:var(--primary);color:#fff;transition:color .3s,background-color .3s}.load-bar .used[data-v-c6c8a320]:hover{background-color:var(--primary-tint,var(--primary))}.load-bar .app[data-v-c6c8a320]{background-color:var(--k-color-warning);transition:color .3s,background-color .3s}.load-bar .app[data-v-c6c8a320]:hover{background-color:var(--k-color-warning-tint,var(--k-color-warning))}.load[data-v-7fdf3e68]{padding:.25rem 1rem}.absolute{position:absolute}.relative{position:relative}.inline{display:inline}.hidden{display:none}.h2{height:.5rem}.flex{display:flex}.flex-shrink{flex-shrink:1}.flex-grow{flex-grow:1}.flex-wrap{flex-wrap:wrap}.gap-4{gap:1rem}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.border{border-width:1px}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter,backdrop-filter;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease{transition-timing-function:cubic-bezier(.4,0,.2,1)}
2
+ /*$vite$:1*/
@@ -1,3 +1,7 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ # Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  commands:
2
6
  status:
3
7
  description: 查看运行状态
package/lib/index.d.ts CHANGED
@@ -45,7 +45,7 @@ declare class TickCounter {
45
45
  static initialize(bot: Bot, ctx: Context): void;
46
46
  }
47
47
  /** 负载率二元组:[app, total],app 为本进程占比,total 为整机占比,取值 0~1。 */
48
- type LoadRate = [app: number, total: number];
48
+ export type LoadRate = [app: number, total: number];
49
49
  /**
50
50
  * 性能数据服务:以 DataService 形式向前端广播 Payload,
51
51
  * 前端经 store.status 读取(键名即服务名 "status")。
@@ -84,7 +84,7 @@ declare namespace ProfileProvider {
84
84
  //#endregion
85
85
  //#region src/index.d.ts
86
86
  /** 频道活跃度记录:键为小时数(0~23),值为该小时内触发频道的次数。 */
87
- type Activity = Record<number, number>;
87
+ export type Activity = Record<number, number>;
88
88
  declare module "@koishi-ce/koishi" {
89
89
  interface Channel {
90
90
  name: string;
@@ -99,10 +99,10 @@ declare module "@koishi-ce/console" {
99
99
  }
100
100
  }
101
101
  }
102
- declare const name = "status";
103
- declare const inject: string[];
104
- interface Config extends ProfileProvider.Config, EnvInfoProvider.Config {}
105
- declare const Config: Schema<Config>;
102
+ export declare const name = "status";
103
+ export declare const inject: string[];
104
+ export interface Config extends ProfileProvider.Config, EnvInfoProvider.Config {}
105
+ export declare const Config: Schema<Config>;
106
106
  /**
107
107
  * 插件入口:注册控制台前端资源(dev 指向 client 源码,prod 指向构建产物),
108
108
  * 再将环境信息与性能采集两个服务作为子插件挂载,共享同一份配置。
@@ -110,6 +110,6 @@ declare const Config: Schema<Config>;
110
110
  * @param ctx 应用上下文
111
111
  * @param config 插件配置(两个子服务配置的交集合并)
112
112
  */
113
- declare function apply(ctx: Context, config: Config): void;
113
+ export declare function apply(ctx: Context, config: Config): void;
114
114
  //#endregion
115
- export { Activity, Config, EnvInfoProvider, LoadRate, ProfileProvider, apply, inject, name };
115
+ export { EnvInfoProvider, ProfileProvider };
package/lib/index.mjs CHANGED
@@ -2,10 +2,9 @@ import { createRequire } from "node:module";
2
2
  import { resolve } from "node:path";
3
3
  import { Schema, Time, version } from "@koishi-ce/koishi";
4
4
  import { readFile } from "node:fs/promises";
5
+ import { arch, cpus, freemem, platform, release, totalmem } from "node:os";
5
6
  import { DataService } from "@koishi-ce/console";
6
- import { helpers } from "envinfo";
7
- import { cpus, freemem, totalmem } from "node:os";
8
- import zhCN from "./assets/zh-CN-BHjPKRmW.yml";
7
+ import zhCN from "./assets/zh-CN-DOq0O2N7.yml";
9
8
  //#region \0rolldown/runtime.js
10
9
  var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
11
10
  //#endregion
@@ -17,7 +16,6 @@ var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
17
16
  * 三组分节字符串字典,供控制台前端在状态栏展示与一键复制。
18
17
  * 环境信息在进程生命周期内不会变化,因此采集结果以 Promise 形式永久缓存。
19
18
  */
20
- const whichPMRuns = createRequire(import.meta.url)("which-pm-runs");
21
19
  var EnvInfoProvider = class extends DataService {
22
20
  /** 采集任务的缓存:get() 首次调用时创建,之后始终复用同一 Promise。 */
23
21
  task;
@@ -28,17 +26,14 @@ var EnvInfoProvider = class extends DataService {
28
26
  }
29
27
  /** 执行一次实际采集:系统信息、运行时版本、Koishi 生态版本。 */
30
28
  async _get() {
31
- const [[, Os = ""], [, Cpu = ""]] = await Promise.all([helpers.getOSInfo(), helpers.getCPUInfo()]);
32
- const agent = whichPMRuns();
33
29
  const system = {
34
- OS: Os,
35
- CPU: Cpu
30
+ OS: `${platform()} ${release()} ${arch()}`,
31
+ CPU: cpus()[0]?.model.trim() ?? ""
32
+ };
33
+ const binaries = {
34
+ Node: process.versions.node,
35
+ Bun: Bun.version
36
36
  };
37
- const binaries = { Node: process.versions.node };
38
- if (agent) {
39
- if (agent.name === "yarn") agent.name = "Yarn";
40
- binaries[agent.name] = agent.version;
41
- }
42
37
  const metapath = __require.resolve("@koishi-ce/console/package.json");
43
38
  const meta = await readFile(metapath, "utf8").then(JSON.parse);
44
39
  const koishi = {
package/locales/de-DE.yml CHANGED
@@ -1,15 +1,19 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ # Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  commands:
2
6
  status:
3
- description: 查看运行状态
7
+ description: Status anzeigen
4
8
  messages:
5
9
  status:
6
- 1: 运行中
7
- 0: 离线
8
- 2: 正在连接
9
- 4: 正在重连
10
- 3: 正在断开
11
- bot: <p>{user.name}:<i18n path={"commands.status.messages.status." + status}/></p>
10
+ 1: Läuft
11
+ 0: Offline
12
+ 2: Verbindung wird hergestellt
13
+ 4: Verbindung wird wiederhergestellt
14
+ 3: Verbindung wird getrennt
15
+ bot: '<p>{user.name}: <i18n path={"commands.status.messages.status." + status}/></p>'
12
16
  epilog: |-
13
17
  ==========
14
- CPU 使用率:{ (cpu[0] * 100).toFixed() }% / { (cpu[1] * 100).toFixed() }%
15
- 内存使用率:{ (memory[0] * 100).toFixed() }% / { (memory[1] * 100).toFixed() }%
18
+ CPU-Auslastung: { (cpu[0] * 100).toFixed() }% / { (cpu[1] * 100).toFixed() }%
19
+ Speicherauslastung: { (memory[0] * 100).toFixed() }% / { (memory[1] * 100).toFixed() }%
package/locales/en-US.yml CHANGED
@@ -1,15 +1,19 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ # Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  commands:
2
6
  status:
3
7
  description: Display the status
4
8
  messages:
5
9
  status:
6
- 1: 运行中
7
- 0: 离线
8
- 2: 正在连接
9
- 4: 正在重连
10
- 3: 正在断开
11
- bot: <p>{user.name}:<i18n path={"commands.status.messages.status." + status}/></p>
10
+ 1: Running
11
+ 0: Offline
12
+ 2: Connecting
13
+ 4: Reconnecting
14
+ 3: Disconnecting
15
+ bot: '<p>{user.name}: <i18n path={"commands.status.messages.status." + status}/></p>'
12
16
  epilog: |-
13
17
  ==========
14
- CPU 使用率:{ (cpu[0] * 100).toFixed() }% / { (cpu[1] * 100).toFixed() }%
15
- 内存使用率:{ (memory[0] * 100).toFixed() }% / { (memory[1] * 100).toFixed() }%
18
+ CPU usage: { (cpu[0] * 100).toFixed() }% / { (cpu[1] * 100).toFixed() }%
19
+ Memory usage: { (memory[0] * 100).toFixed() }% / { (memory[1] * 100).toFixed() }%
package/locales/fr-FR.yml CHANGED
@@ -1,15 +1,19 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ # Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  commands:
2
6
  status:
3
- description: 查看运行状态
7
+ description: Afficher le statut
4
8
  messages:
5
9
  status:
6
- 1: 运行中
7
- 0: 离线
8
- 2: 正在连接
9
- 4: 正在重连
10
- 3: 正在断开
11
- bot: <p>{user.name}:<i18n path={"commands.status.messages.status." + status}/></p>
10
+ 1: En cours d'exécution
11
+ 0: Hors ligne
12
+ 2: Connexion en cours
13
+ 4: Reconnexion en cours
14
+ 3: Déconnexion en cours
15
+ bot: '<p>{user.name} : <i18n path={"commands.status.messages.status." + status}/></p>'
12
16
  epilog: |-
13
17
  ==========
14
- CPU 使用率:{ (cpu[0] * 100).toFixed() }% / { (cpu[1] * 100).toFixed() }%
15
- 内存使用率:{ (memory[0] * 100).toFixed() }% / { (memory[1] * 100).toFixed() }%
18
+ Utilisation du CPU : { (cpu[0] * 100).toFixed() }% / { (cpu[1] * 100).toFixed() }%
19
+ Utilisation de la mémoire : { (memory[0] * 100).toFixed() }% / { (memory[1] * 100).toFixed() }%
package/locales/ja-JP.yml CHANGED
@@ -1,15 +1,19 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ # Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  commands:
2
6
  status:
3
7
  description: 実行状態を確認する
4
8
  messages:
5
9
  status:
6
- 1: 运行中
7
- 0: 离线
8
- 2: 正在连接
9
- 4: 正在重连
10
- 3: 正在断开
10
+ 1: 実行中
11
+ 0: オフライン
12
+ 2: 接続中
13
+ 4: 再接続中
14
+ 3: 切断中
11
15
  bot: <p>{user.name}:<i18n path={"commands.status.messages.status." + status}/></p>
12
16
  epilog: |-
13
17
  ==========
14
18
  CPU 使用率:{ (cpu[0] * 100).toFixed() }% / { (cpu[1] * 100).toFixed() }%
15
- 内存使用率:{ (memory[0] * 100).toFixed() }% / { (memory[1] * 100).toFixed() }%
19
+ メモリ使用率:{ (memory[0] * 100).toFixed() }% / { (memory[1] * 100).toFixed() }%
package/locales/ru-RU.yml CHANGED
@@ -1,15 +1,19 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ # Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  commands:
2
6
  status:
3
- description: 查看运行状态
7
+ description: Показать статус
4
8
  messages:
5
9
  status:
6
- 1: 运行中
7
- 0: 离线
8
- 2: 正在连接
9
- 4: 正在重连
10
- 3: 正在断开
11
- bot: <p>{user.name}:<i18n path={"commands.status.messages.status." + status}/></p>
10
+ 1: Работает
11
+ 0: Не в сети
12
+ 2: Подключение
13
+ 4: Переподключение
14
+ 3: Отключение
15
+ bot: '<p>{user.name}: <i18n path={"commands.status.messages.status." + status}/></p>'
12
16
  epilog: |-
13
17
  ==========
14
- CPU 使用率:{ (cpu[0] * 100).toFixed() }% / { (cpu[1] * 100).toFixed() }%
15
- 内存使用率:{ (memory[0] * 100).toFixed() }% / { (memory[1] * 100).toFixed() }%
18
+ Использование CPU: { (cpu[0] * 100).toFixed() }% / { (cpu[1] * 100).toFixed() }%
19
+ Использование памяти: { (memory[0] * 100).toFixed() }% / { (memory[1] * 100).toFixed() }%
package/locales/zh-CN.yml CHANGED
@@ -1,3 +1,7 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ # Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  commands:
2
6
  status:
3
7
  description: 查看运行状态
package/locales/zh-TW.yml CHANGED
@@ -1,3 +1,7 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ # Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  commands:
2
6
  status:
3
7
  description: 查看運行狀態
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@koishi-ce/plugin-status",
3
3
  "description": "Status view for Koishi",
4
- "version": "1.0.0",
4
+ "version": "1.1.0",
5
5
  "type": "module",
6
6
  "main": "lib/index.mjs",
7
7
  "typings": "lib/index.d.ts",
@@ -46,20 +46,16 @@
46
46
  }
47
47
  },
48
48
  "peerDependencies": {
49
- "@koishijs/plugin-console": "^5.30.11",
50
- "koishi": "^4.18.11"
49
+ "@koishi-ce/plugin-console": "^1.0.0",
50
+ "@koishi-ce/koishi": "^1.0.0"
51
51
  },
52
52
  "devDependencies": {
53
- "@koishi-ce/client": "^1.0.0",
54
- "@koishi-ce/koishi": "^1.0.0",
55
- "@koishi-ce/plugin-analytics": "^1.0.0",
56
- "@koishi-ce/plugin-config": "^1.0.0",
57
- "@types/envinfo": "^7.8.3"
53
+ "@koishi-ce/client": "^1.1.0",
54
+ "@koishi-ce/koishi": "^1.0.8",
55
+ "@koishi-ce/plugin-analytics": "^1.1.0"
58
56
  },
59
57
  "dependencies": {
60
- "@koishi-ce/console": "^1.0.0",
61
- "envinfo": "^7.21.0",
62
- "which-pm-runs": "^1.1.0"
58
+ "@koishi-ce/console": "^1.0.0"
63
59
  },
64
60
  "exports": {
65
61
  ".": {
package/src/envinfo.ts CHANGED
@@ -1,3 +1,7 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ // Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  /**
2
6
  * 运行环境信息服务(服务名 "envinfo")。
3
7
  *
@@ -7,61 +11,61 @@
7
11
  */
8
12
 
9
13
  import { readFile } from "node:fs/promises";
10
- import { createRequire } from "node:module";
14
+ import { arch, cpus, platform, release } from "node:os";
11
15
  import { DataService } from "@koishi-ce/console";
12
- import { type Context, type Dict, Schema, version } from "@koishi-ce/koishi";
13
- import { helpers } from "envinfo";
14
-
15
- // 经 createRequire 加载 CJS 包并就地断言签名:不走 ESM 导入互操作,
16
- // 规避多包合并类型检查(大一统 tsconfig)下 export = 交织失效问题
17
- // (此前具名导入在 Bun 运行时下本就拿不到具名导出,属顺带修复)
18
- const whichPMRuns = createRequire(import.meta.url)("which-pm-runs") as () =>
19
- | undefined
20
- | { name: string; version: string };
16
+ import {
17
+ type Context,
18
+ type Dict,
19
+ Schema,
20
+ version,
21
+ } from "@koishi-ce/koishi";
21
22
 
22
- class EnvInfoProvider extends DataService<Dict<Dict<string>>> {
23
+ class EnvInfoProvider extends DataService<
24
+ Dict<Dict<string>>
25
+ > {
23
26
  /** 采集任务的缓存:get() 首次调用时创建,之后始终复用同一 Promise。 */
24
27
  private task!: Promise<Dict<Dict<string>>>;
25
28
 
26
29
  // 基类链(cordis Service)上已有 config 成员,故需 override
27
30
  override config: EnvInfoProvider.Config;
28
31
 
29
- constructor(ctx: Context, config: EnvInfoProvider.Config) {
32
+ constructor(
33
+ ctx: Context,
34
+ config: EnvInfoProvider.Config,
35
+ ) {
30
36
  super(ctx, "envinfo");
31
37
  this.config = config;
32
38
  }
33
39
 
34
40
  /** 执行一次实际采集:系统信息、运行时版本、Koishi 生态版本。 */
35
41
  async _get(): Promise<Dict<Dict<string>>> {
36
- // @types/envinfo 把各 helper 声明为 Promise<string>,运行时实际返回
37
- // [标题, 值] 二元组;解构默认值仅用于收窄类型,正常路径不会触发
38
- const [[, Os = ""], [, Cpu = ""]] = await Promise.all([
39
- helpers.getOSInfo(),
40
- helpers.getCPUInfo(),
41
- ]);
42
- const agent = whichPMRuns();
43
- const system: Dict<string> = { OS: Os, CPU: Cpu };
42
+ // 不再依赖 npm 包 envinfo / which-pm-runs:OS platform +
43
+ // release + arch 原生拼出,CPU 取 cpus() 首核型号;Bun-first
44
+ // 下包管理器恒为 bun(唯一启动方式),直接上报其版本
45
+ const system: Dict<string> = {
46
+ OS: `${platform()} ${release()} ${arch()}`,
47
+ CPU: cpus()[0]?.model.trim() ?? "",
48
+ };
44
49
  const binaries: Dict<string> = {
45
50
  Node: process.versions.node,
51
+ Bun: Bun.version,
46
52
  };
47
- if (agent) {
48
- // yarn 的名称归一为首字母大写,与 Node 等键的展示风格一致
49
- if (agent.name === "yarn") {
50
- agent.name = "Yarn";
51
- }
52
- binaries[agent.name] = agent.version;
53
- }
54
53
  // 不直接 require package.json 是为了避免其被模块缓存固定住,
55
54
  // 热更新后读取到的仍是旧版本号
56
- const metapath = require.resolve("@koishi-ce/console/package.json");
57
- const meta = await readFile(metapath, "utf8").then(JSON.parse);
55
+ const metapath = require.resolve(
56
+ "@koishi-ce/console/package.json",
57
+ );
58
+ const meta = await readFile(metapath, "utf8").then(
59
+ JSON.parse,
60
+ );
58
61
  const koishi: Dict<string> = {
59
62
  Core: version,
60
63
  Console: meta.version,
61
64
  };
62
65
  // 宿主代理(如 koishi-plugin-browser)可通过该环境变量上报自身名称与版本
63
66
  if (process.env["KOISHI_AGENT"]) {
64
- const [name, agentVersion] = process.env["KOISHI_AGENT"].split("/");
67
+ const [name, agentVersion] =
68
+ process.env["KOISHI_AGENT"].split("/");
65
69
  if (name && agentVersion) {
66
70
  koishi[name] = agentVersion;
67
71
  }
@@ -77,7 +81,8 @@ class EnvInfoProvider extends DataService<Dict<Dict<string>>> {
77
81
 
78
82
  // erasableSyntaxOnly 禁止含运行时值的 namespace,
79
83
  // 原 namespace 内的 Config 常量移到此处的静态字段,对外形状不变
80
- static Config: Schema<EnvInfoProvider.Config> = Schema.object({});
84
+ static Config: Schema<EnvInfoProvider.Config> =
85
+ Schema.object({});
81
86
  }
82
87
 
83
88
  namespace EnvInfoProvider {
@@ -0,0 +1,199 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ // Copyright (c) 2026-present Koishi-CE contributors.
4
+
5
+ import {
6
+ afterAll,
7
+ beforeAll,
8
+ describe,
9
+ expect,
10
+ it,
11
+ } from "bun:test";
12
+ import { Console, type Entry } from "@koishi-ce/console";
13
+ import { App, type Plugin } from "@koishi-ce/koishi";
14
+ import mock from "@koishi-ce/plugin-mock";
15
+ import memory from "@koishijs/plugin-database-memory";
16
+ import * as status from "./index.ts";
17
+ import {
18
+ EnvInfoProvider,
19
+ ProfileProvider,
20
+ type ProfileProvider as ProfileProviderType,
21
+ } from "./index.ts";
22
+
23
+ /** 控制台服务桩:仅实现入口登记所需的最小面。 */
24
+ class FakeConsole extends Console {
25
+ protected resolveEntry(
26
+ _files: Entry.Files,
27
+ _key: string,
28
+ ): string[] {
29
+ return [];
30
+ }
31
+ }
32
+
33
+ const app = new App();
34
+
35
+ // 同 admin:CJS 实现配 ESM 声明,nodenext 互操作视图多包一层 default,类型层穿透取真实类
36
+ app.plugin(memory as unknown as typeof memory.default);
37
+ // Console 基类的 static inject 是 cordis 3 旧形态({ optional: [...] }),
38
+ // 与 Plugin.Constructor 期待的 Dict<Meta> 索引签名不兼容,仅做类型层转型
39
+ app.plugin(
40
+ FakeConsole as unknown as Plugin.Constructor<App>,
41
+ );
42
+ // tickInterval 取小值,驱动 ready 定时器尽快完成首次 CPU 采样与刷新
43
+ app.plugin(status, { tickInterval: 50 });
44
+ app.plugin(mock);
45
+
46
+ const client = app.mock.client("123", "321");
47
+
48
+ const profile = () =>
49
+ app.get("console.services.status") as ProfileProviderType;
50
+ const envinfo = () =>
51
+ app.get("console.services.envinfo") as EnvInfoProvider;
52
+
53
+ beforeAll(async () => {
54
+ await app.start();
55
+ await app.mock.initUser("123", 1);
56
+ await app.mock.initChannel("321");
57
+ });
58
+
59
+ afterAll(async () => {
60
+ await app.stop();
61
+ });
62
+
63
+ describe("status 插件", () => {
64
+ it("加载后同时挂载 envinfo 与 status 两个数据服务", () => {
65
+ expect(
66
+ app.get("console.services.envinfo"),
67
+ ).toBeDefined();
68
+ expect(
69
+ app.get("console.services.status"),
70
+ ).toBeDefined();
71
+ expect(
72
+ Object.keys(app.console.entries).length,
73
+ ).toBeGreaterThan(0);
74
+ });
75
+
76
+ it("envinfo 采集系统 / 运行时 / Koishi 三组信息并缓存", async () => {
77
+ const data = await envinfo().get();
78
+ expect(Object.keys(data)).toEqual([
79
+ "system",
80
+ "binaries",
81
+ "koishi",
82
+ ]);
83
+ // 索引签名属性走方括号访问,undefined 安全由断言本身兜底
84
+ expect(data["system"]?.["OS"]).toBeTruthy();
85
+ expect(data["binaries"]?.["Node"]).toBe(
86
+ process.versions.node,
87
+ );
88
+ expect(data["koishi"]?.["Core"]).toBeTruthy();
89
+ expect(data["koishi"]?.["Console"]).toBeTruthy();
90
+
91
+ // 首次采集后缓存:内部 task 不再更换
92
+ const before =
93
+ envinfo()["task" as keyof EnvInfoProvider];
94
+ await envinfo().get();
95
+ expect(envinfo()["task" as keyof EnvInfoProvider]).toBe(
96
+ before,
97
+ );
98
+ });
99
+
100
+ it("envinfo 上报 KOISHI_AGENT 宿主代理信息", async () => {
101
+ process.env["KOISHI_AGENT"] = "test-agent/1.2.3";
102
+ try {
103
+ const data = await envinfo()["_get"]();
104
+ expect(data["koishi"]?.["test-agent"]).toBe("1.2.3");
105
+ } finally {
106
+ delete process.env["KOISHI_AGENT"];
107
+ }
108
+ });
109
+
110
+ it("profile 采集内存 / CPU 负载与机器人收发速率", async () => {
111
+ // 等待首个 tick 完成 CPU 差值采样
112
+ await new Promise((resolve) =>
113
+ setTimeout(resolve, 120),
114
+ );
115
+ const data = await profile().get();
116
+ expect(data.memory).toHaveLength(2);
117
+ expect(data.cpu).toHaveLength(2);
118
+ // 内存与 CPU 负载率均为有限数
119
+ for (const rate of [...data.memory, ...data.cpu]) {
120
+ expect(Number.isFinite(rate)).toBe(true);
121
+ }
122
+ // mock 机器人不隐藏,应出现在列表中
123
+ expect(Object.keys(data.bots).length).toBeGreaterThan(
124
+ 0,
125
+ );
126
+ });
127
+
128
+ it("get 每次重算且结构稳定(cached 恒为 undefined 的死分支)", async () => {
129
+ const svc = profile();
130
+ const first = await svc.get();
131
+ const second = await svc.get();
132
+ // 源码从不写入 cached 字段,非强制调用同样重算
133
+ expect(svc.cached).toBeUndefined();
134
+ expect(Object.keys(second)).toEqual(Object.keys(first));
135
+ expect(Object.keys(second.bots)).toEqual(
136
+ Object.keys(first.bots),
137
+ );
138
+ const forced = await svc.get(true);
139
+ expect(Object.keys(forced)).toEqual(Object.keys(first));
140
+ });
141
+
142
+ it("消息收发计入滑动窗口计数器", async () => {
143
+ const bot = [...app.bots][0];
144
+ expect(bot).toBeDefined();
145
+ const before = bot?._messageSent.get() ?? 0;
146
+ const receivedBefore = bot?._messageReceived.get() ?? 0;
147
+ // 收到一条用户消息(触发 message 事件),机器人无回复
148
+ await client.receive("ping-should-not-reply");
149
+ expect(bot?._messageReceived.get()).toBe(
150
+ receivedBefore + 1,
151
+ );
152
+ expect(bot?._messageSent.get()).toBe(before);
153
+
154
+ const data = await profile().get(true);
155
+ const entry = data.bots[bot?.sid ?? ""];
156
+ expect(entry?.messageReceived).toBe(receivedBefore + 1);
157
+ });
158
+
159
+ it("滑动窗口计数器经秒级 tick 后继续累加", async () => {
160
+ const bot = [...app.bots][0];
161
+ expect(bot).toBeDefined();
162
+ bot?._messageSent.add(1);
163
+ expect(bot?._messageSent.get()).toBe(1);
164
+ // 等待一次以上的窗口滑动(计数移入历史槽位),新计数继续记入队头
165
+ await new Promise((resolve) =>
166
+ setTimeout(resolve, 1100),
167
+ );
168
+ bot?._messageSent.add(2);
169
+ expect(bot?._messageSent.get()).toBe(3);
170
+ });
171
+
172
+ it("login-added / login-removed / login-updated 事件驱动刷新", async () => {
173
+ const bot = [...app.bots][0];
174
+ expect(bot).toBeDefined();
175
+ // login-added:重新初始化计数器并防抖刷新(bot 已由上方断言保证存在)。
176
+ // satori 把 login-* 载荷声明为 Session,本仓运行时实际按 { bot } 对象分发
177
+ // (profile.ts 按此解构),为不改运行时行为仅做类型层断言
178
+ app.emit("login-added", { bot: bot! } as never);
179
+ expect(bot?._messageSent).toBeDefined();
180
+ // login-removed:停掉计数器(stop 被调用不抛错)并刷新
181
+ app.emit("login-removed", { bot: bot! } as never);
182
+ // login-updated:仅刷新
183
+ app.emit("login-updated", bot! as never);
184
+ await new Promise((resolve) => setTimeout(resolve, 10));
185
+ });
186
+
187
+ it("status 指令输出机器人状态与性能摘要", async () => {
188
+ const replies = await client.receive("status");
189
+ expect(replies).toHaveLength(1);
190
+ expect(replies[0]).toContain("mock");
191
+ });
192
+
193
+ it("两个子服务的 Config 均可解析", () => {
194
+ expect(ProfileProvider.Config({})).toEqual({
195
+ tickInterval: 5000,
196
+ });
197
+ expect(EnvInfoProvider.Config({})).toEqual({});
198
+ });
199
+ });
package/src/index.ts CHANGED
@@ -1,3 +1,7 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ // Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  /**
2
6
  * status 插件(node 侧)入口。
3
7
  *
package/src/profile.ts CHANGED
@@ -1,3 +1,7 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ // Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  /**
2
6
  * 性能数据采集服务(服务名 "status")。
3
7
  *
@@ -48,7 +52,10 @@ class TickCounter {
48
52
  };
49
53
 
50
54
  constructor(ctx: Context) {
51
- this.stop = ctx.setInterval(() => this.tick(), Time.second);
55
+ this.stop = ctx.setInterval(
56
+ () => this.tick(),
57
+ Time.second,
58
+ );
52
59
  }
53
60
 
54
61
  /** 在当前秒的槽位上累加计数。 */
@@ -79,7 +86,10 @@ let usedRate: number;
79
86
  /** 采集内存负载率:[进程 RSS / 总内存, 1 - 空闲内存 / 总内存]。 */
80
87
  async function memoryRate(): Promise<LoadRate> {
81
88
  const total = totalmem();
82
- return [process.memoryUsage().rss / total, 1 - freemem() / total];
89
+ return [
90
+ process.memoryUsage().rss / total,
91
+ 1 - freemem() / total,
92
+ ];
83
93
  }
84
94
 
85
95
  /**
@@ -135,7 +145,10 @@ class ProfileProvider extends DataService<ProfileProvider.Payload> {
135
145
  // 基类链(cordis Service)上已有 config 成员,故需 override
136
146
  override config: ProfileProvider.Config;
137
147
 
138
- constructor(ctx: Context, config: ProfileProvider.Config) {
148
+ constructor(
149
+ ctx: Context,
150
+ config: ProfileProvider.Config,
151
+ ) {
139
152
  super(ctx, "status");
140
153
 
141
154
  this.config = config;
@@ -209,7 +222,9 @@ class ProfileProvider extends DataService<ProfileProvider.Payload> {
209
222
  const bots: Dict<ProfileProvider.BotData> = {};
210
223
  for (const bot of this.ctx.bots) {
211
224
  if (bot.hidden) continue;
212
- const paths = this.ctx.get("loader")?.paths(bot.ctx.scope);
225
+ const paths = this.ctx
226
+ .get("loader")
227
+ ?.paths(bot.ctx.scope);
213
228
  bots[bot.sid] = {
214
229
  ...bot.toJSON(),
215
230
  error: bot.error?.message,
@@ -224,12 +239,13 @@ class ProfileProvider extends DataService<ProfileProvider.Payload> {
224
239
 
225
240
  // erasableSyntaxOnly 禁止含运行时值的 namespace,
226
241
  // 原 namespace 内的 Config 常量移到此处的静态字段,对外形状不变
227
- static Config: Schema<ProfileProvider.Config> = Schema.object({
228
- tickInterval: Schema.natural()
229
- .role("ms")
230
- .description("性能数据推送的时间间隔。")
231
- .default(Time.second * 5),
232
- });
242
+ static Config: Schema<ProfileProvider.Config> =
243
+ Schema.object({
244
+ tickInterval: Schema.natural()
245
+ .role("ms")
246
+ .description("性能数据推送的时间间隔。")
247
+ .default(Time.second * 5),
248
+ });
233
249
  }
234
250
 
235
251
  namespace ProfileProvider {
package/dist/index.css DELETED
@@ -1,2 +0,0 @@
1
- .status-light[data-v-ea7f6fce]{transition:var(--color-transition);border-radius:.625rem;width:.625rem;height:.625rem}.status-light.online[data-v-ea7f6fce]{background-color:var(--k-color-success)}.status-light.connect[data-v-ea7f6fce],.status-light.reconnect[data-v-ea7f6fce]{background-color:var(--k-color-warning)}.status-light.error[data-v-ea7f6fce]{background-color:var(--k-color-danger)!important}.status-light.offline[data-v-ea7f6fce]{background-color:var(--k-color-disabled)}.status-light+.status-light[data-v-ea7f6fce]{margin-left:4px}.bot-view[data-v-1cbf178f]{width:15rem;padding:.75rem 1rem;font-size:14px;transition:all .3s;display:flex}.bot-view+.bot-view[data-v-1cbf178f]{border-top:1px solid var(--k-color-divider)}.bot-view.active>div.avatar[data-v-1cbf178f]{border-color:var(--active)}.bot-view>div.avatar[data-v-1cbf178f]{box-sizing:content-box;border:1px solid var(--k-color-divider);background-repeat:no-repeat;background-size:100%;border-radius:100%;flex-shrink:0;width:4rem;height:4rem;transition:all .1s;position:relative}.bot-view>div.avatar .status-light[data-v-1cbf178f]{border:1px solid var(--k-color-divider);width:.875rem;height:.875rem;position:absolute;bottom:-1px;right:-1px}.bot-view>div.info[data-v-1cbf178f]{flex-direction:column;flex-grow:1;justify-content:space-around;margin-left:1.25rem;display:flex;overflow:hidden}.bot-view>div.info .k-icon[data-v-1cbf178f]{text-align:center;vertical-align:-2px;width:20px;margin-right:6px}.bot-view.has-link[data-v-1cbf178f]{cursor:pointer}.bot-view.has-link[data-v-1cbf178f]:hover{background-color:var(--bg1)}.k-status .k-icon[data-v-e9800c9a]{margin-right:4px}.k-status *+.k-icon[data-v-e9800c9a]{margin-left:6px}.k-status .count[data-v-e9800c9a]{letter-spacing:1px;margin:0 4px}.bots-container .bot-view[data-v-19020f78]{background-color:var(--bg0);border-radius:.5rem}p[data-v-184a36f6]{margin:8px;padding:0 6px;font-size:12px;line-height:1}.info[data-v-184a36f6]{padding-left:20px}.load-bar[data-v-c8d81760]{-webkit-user-select:none;user-select:none;align-items:center;margin:.5rem 0;font-size:.9em;display:flex}.load-bar .title[data-v-c8d81760]{min-width:3em}.load-bar .body[data-v-c8d81760]{background-color:var(--k-c-divisor);width:10rem;height:.8rem;transition:var(--color-transition);color:var(--fg1);border-radius:1rem;font-size:10px;display:inline;position:relative;overflow:hidden}.load-bar .bar[data-v-c8d81760]{float:left;white-space:pre;justify-content:center;align-items:center;height:100%;display:flex;position:relative}.load-bar .used[data-v-c8d81760]{background-color:var(--primary);color:#fff;transition:color .3s,background-color .3s}.load-bar .used[data-v-c8d81760]:hover{background-color:var(--primary-tint,var(--primary))}.load-bar .app[data-v-c8d81760]{background-color:var(--k-color-warning);transition:color .3s,background-color .3s}.load-bar .app[data-v-c8d81760]:hover{background-color:var(--k-color-warning-tint,var(--k-color-warning))}.load[data-v-8c7391c4]{padding:.25rem 1rem}.absolute{position:absolute}.relative{position:relative}.inline{display:inline}.hidden{display:none}.h2{height:.5rem}.flex{display:flex}.flex-shrink{flex-shrink:1}.flex-grow{flex-grow:1}.flex-wrap{flex-wrap:wrap}.gap-4{gap:1rem}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.border{border-width:1px}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter,backdrop-filter;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease{transition-timing-function:cubic-bezier(.4,0,.2,1)}
2
- /*$vite$:1*/
@@ -1,12 +0,0 @@
1
- // which-pm-runs 没有官方类型声明,npm 上也不存在 @types/which-pm-runs。
2
- // 以下按其 1.1.0 源码(index.js)手写形状:
3
- // 未检测到 npm_config_user_agent 环境变量时返回 undefined,
4
- // 否则返回包管理器的名称(npminstall 会归一化为 cnpm)与版本。
5
- declare module "which-pm-runs" {
6
- export interface PackageManager {
7
- name: string;
8
- version: string;
9
- }
10
-
11
- export function whichPMRuns(): PackageManager | undefined;
12
- }