@koishi-ce/plugin-status 1.0.0 → 1.0.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 +123 -0
- package/dist/index.js +2 -2
- package/dist/style.css +2 -0
- package/lib/assets/{zh-CN-BHjPKRmW.yml → zh-CN-DOq0O2N7.yml} +4 -0
- package/lib/index.mjs +8 -13
- package/locales/de-DE.yml +4 -0
- package/locales/en-US.yml +4 -0
- package/locales/fr-FR.yml +4 -0
- package/locales/ja-JP.yml +4 -0
- package/locales/ru-RU.yml +4 -0
- package/locales/zh-CN.yml +4 -0
- package/locales/zh-TW.yml +4 -0
- package/package.json +7 -11
- package/src/envinfo.ts +13 -24
- package/src/index.test.ts +159 -0
- package/src/index.ts +4 -0
- package/src/profile.ts +4 -0
- package/dist/index.css +0 -2
- package/src/which-pm-runs.d.ts +0 -12
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-
|
|
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-a7e2b701`]]),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-d26f82dc`]]);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-
|
|
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-9d2842fa`]]),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-19b83288`]]);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-a7e2b701]{margin-right:4px}.k-status *+.k-icon[data-v-a7e2b701]{margin-left:6px}.k-status .count[data-v-a7e2b701]{letter-spacing:1px;margin:0 4px}.bots-container .bot-view[data-v-d26f82dc]{background-color:var(--bg0);border-radius:.5rem}p[data-v-9d2842fa]{margin:8px;padding:0 6px;font-size:12px;line-height:1}.info[data-v-9d2842fa]{padding-left:20px}.load-bar[data-v-19b83288]{-webkit-user-select:none;user-select:none;align-items:center;margin:.5rem 0;font-size:.9em;display:flex}.load-bar .title[data-v-19b83288]{min-width:3em}.load-bar .body[data-v-19b83288]{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-19b83288]{float:left;white-space:pre;justify-content:center;align-items:center;height:100%;display:flex;position:relative}.load-bar .used[data-v-19b83288]{background-color:var(--primary);color:#fff;transition:color .3s,background-color .3s}.load-bar .used[data-v-19b83288]:hover{background-color:var(--primary-tint,var(--primary))}.load-bar .app[data-v-19b83288]{background-color:var(--k-color-warning);transition:color .3s,background-color .3s}.load-bar .app[data-v-19b83288]: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*/
|
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
|
|
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:
|
|
35
|
-
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
package/locales/en-US.yml
CHANGED
package/locales/fr-FR.yml
CHANGED
package/locales/ja-JP.yml
CHANGED
package/locales/ru-RU.yml
CHANGED
package/locales/zh-CN.yml
CHANGED
package/locales/zh-TW.yml
CHANGED
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.
|
|
4
|
+
"version": "1.0.1",
|
|
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
|
-
"@
|
|
50
|
-
"koishi": "^
|
|
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.
|
|
54
|
-
"@koishi-ce/koishi": "^1.0.
|
|
55
|
-
"@koishi-ce/plugin-analytics": "^1.0.
|
|
56
|
-
"@koishi-ce/plugin-config": "^1.0.0",
|
|
57
|
-
"@types/envinfo": "^7.8.3"
|
|
53
|
+
"@koishi-ce/client": "^1.0.3",
|
|
54
|
+
"@koishi-ce/koishi": "^1.0.4",
|
|
55
|
+
"@koishi-ce/plugin-analytics": "^1.0.2"
|
|
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,17 +11,9 @@
|
|
|
7
11
|
*/
|
|
8
12
|
|
|
9
13
|
import { readFile } from "node:fs/promises";
|
|
10
|
-
import {
|
|
14
|
+
import { arch, cpus, platform, release } from "node:os";
|
|
11
15
|
import { DataService } from "@koishi-ce/console";
|
|
12
16
|
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 };
|
|
21
17
|
|
|
22
18
|
class EnvInfoProvider extends DataService<Dict<Dict<string>>> {
|
|
23
19
|
/** 采集任务的缓存:get() 首次调用时创建,之后始终复用同一 Promise。 */
|
|
@@ -33,24 +29,17 @@ class EnvInfoProvider extends DataService<Dict<Dict<string>>> {
|
|
|
33
29
|
|
|
34
30
|
/** 执行一次实际采集:系统信息、运行时版本、Koishi 生态版本。 */
|
|
35
31
|
async _get(): Promise<Dict<Dict<string>>> {
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const system: Dict<string> = { OS: Os, CPU: Cpu };
|
|
32
|
+
// 不再依赖 npm 包 envinfo / which-pm-runs:OS 由 platform +
|
|
33
|
+
// release + arch 原生拼出,CPU 取 cpus() 首核型号;Bun-first
|
|
34
|
+
// 下包管理器恒为 bun(唯一启动方式),直接上报其版本
|
|
35
|
+
const system: Dict<string> = {
|
|
36
|
+
OS: `${platform()} ${release()} ${arch()}`,
|
|
37
|
+
CPU: cpus()[0]?.model.trim() ?? "",
|
|
38
|
+
};
|
|
44
39
|
const binaries: Dict<string> = {
|
|
45
40
|
Node: process.versions.node,
|
|
41
|
+
Bun: Bun.version,
|
|
46
42
|
};
|
|
47
|
-
if (agent) {
|
|
48
|
-
// yarn 的名称归一为首字母大写,与 Node 等键的展示风格一致
|
|
49
|
-
if (agent.name === "yarn") {
|
|
50
|
-
agent.name = "Yarn";
|
|
51
|
-
}
|
|
52
|
-
binaries[agent.name] = agent.version;
|
|
53
|
-
}
|
|
54
43
|
// 不直接 require package.json 是为了避免其被模块缓存固定住,
|
|
55
44
|
// 热更新后读取到的仍是旧版本号
|
|
56
45
|
const metapath = require.resolve("@koishi-ce/console/package.json");
|
|
@@ -0,0 +1,159 @@
|
|
|
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 { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
|
6
|
+
import { Console, type Entry } from "@koishi-ce/console";
|
|
7
|
+
import { App, type Plugin } from "@koishi-ce/koishi";
|
|
8
|
+
import mock from "@koishi-ce/plugin-mock";
|
|
9
|
+
import memory from "@koishijs/plugin-database-memory";
|
|
10
|
+
import * as status from "./index.ts";
|
|
11
|
+
import {
|
|
12
|
+
EnvInfoProvider,
|
|
13
|
+
ProfileProvider,
|
|
14
|
+
type ProfileProvider as ProfileProviderType,
|
|
15
|
+
} from "./index.ts";
|
|
16
|
+
|
|
17
|
+
/** 控制台服务桩:仅实现入口登记所需的最小面。 */
|
|
18
|
+
class FakeConsole extends Console {
|
|
19
|
+
protected resolveEntry(_files: Entry.Files, _key: string): string[] {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const app = new App();
|
|
25
|
+
|
|
26
|
+
// 同 admin:CJS 实现配 ESM 声明,nodenext 互操作视图多包一层 default,类型层穿透取真实类
|
|
27
|
+
app.plugin(memory as unknown as typeof memory.default);
|
|
28
|
+
// Console 基类的 static inject 是 cordis 3 旧形态({ optional: [...] }),
|
|
29
|
+
// 与 Plugin.Constructor 期待的 Dict<Meta> 索引签名不兼容,仅做类型层转型
|
|
30
|
+
app.plugin(FakeConsole as unknown as Plugin.Constructor<App>);
|
|
31
|
+
// tickInterval 取小值,驱动 ready 定时器尽快完成首次 CPU 采样与刷新
|
|
32
|
+
app.plugin(status, { tickInterval: 50 });
|
|
33
|
+
app.plugin(mock);
|
|
34
|
+
|
|
35
|
+
const client = app.mock.client("123", "321");
|
|
36
|
+
|
|
37
|
+
const profile = () => app.get("console.services.status") as ProfileProviderType;
|
|
38
|
+
const envinfo = () => app.get("console.services.envinfo") as EnvInfoProvider;
|
|
39
|
+
|
|
40
|
+
beforeAll(async () => {
|
|
41
|
+
await app.start();
|
|
42
|
+
await app.mock.initUser("123", 1);
|
|
43
|
+
await app.mock.initChannel("321");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
afterAll(async () => {
|
|
47
|
+
await app.stop();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe("status 插件", () => {
|
|
51
|
+
it("加载后同时挂载 envinfo 与 status 两个数据服务", () => {
|
|
52
|
+
expect(app.get("console.services.envinfo")).toBeDefined();
|
|
53
|
+
expect(app.get("console.services.status")).toBeDefined();
|
|
54
|
+
expect(Object.keys(app.console.entries).length).toBeGreaterThan(0);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("envinfo 采集系统 / 运行时 / Koishi 三组信息并缓存", async () => {
|
|
58
|
+
const data = await envinfo().get();
|
|
59
|
+
expect(Object.keys(data)).toEqual(["system", "binaries", "koishi"]);
|
|
60
|
+
// 索引签名属性走方括号访问,undefined 安全由断言本身兜底
|
|
61
|
+
expect(data["system"]?.["OS"]).toBeTruthy();
|
|
62
|
+
expect(data["binaries"]?.["Node"]).toBe(process.versions.node);
|
|
63
|
+
expect(data["koishi"]?.["Core"]).toBeTruthy();
|
|
64
|
+
expect(data["koishi"]?.["Console"]).toBeTruthy();
|
|
65
|
+
|
|
66
|
+
// 首次采集后缓存:内部 task 不再更换
|
|
67
|
+
const before = envinfo()["task" as keyof EnvInfoProvider];
|
|
68
|
+
await envinfo().get();
|
|
69
|
+
expect(envinfo()["task" as keyof EnvInfoProvider]).toBe(before);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("envinfo 上报 KOISHI_AGENT 宿主代理信息", async () => {
|
|
73
|
+
process.env["KOISHI_AGENT"] = "test-agent/1.2.3";
|
|
74
|
+
try {
|
|
75
|
+
const data = await envinfo()["_get"]();
|
|
76
|
+
expect(data["koishi"]?.["test-agent"]).toBe("1.2.3");
|
|
77
|
+
} finally {
|
|
78
|
+
delete process.env["KOISHI_AGENT"];
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("profile 采集内存 / CPU 负载与机器人收发速率", async () => {
|
|
83
|
+
// 等待首个 tick 完成 CPU 差值采样
|
|
84
|
+
await new Promise((resolve) => setTimeout(resolve, 120));
|
|
85
|
+
const data = await profile().get();
|
|
86
|
+
expect(data.memory).toHaveLength(2);
|
|
87
|
+
expect(data.cpu).toHaveLength(2);
|
|
88
|
+
// 内存与 CPU 负载率均为有限数
|
|
89
|
+
for (const rate of [...data.memory, ...data.cpu]) {
|
|
90
|
+
expect(Number.isFinite(rate)).toBe(true);
|
|
91
|
+
}
|
|
92
|
+
// mock 机器人不隐藏,应出现在列表中
|
|
93
|
+
expect(Object.keys(data.bots).length).toBeGreaterThan(0);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("get 每次重算且结构稳定(cached 恒为 undefined 的死分支)", async () => {
|
|
97
|
+
const svc = profile();
|
|
98
|
+
const first = await svc.get();
|
|
99
|
+
const second = await svc.get();
|
|
100
|
+
// 源码从不写入 cached 字段,非强制调用同样重算
|
|
101
|
+
expect(svc.cached).toBeUndefined();
|
|
102
|
+
expect(Object.keys(second)).toEqual(Object.keys(first));
|
|
103
|
+
expect(Object.keys(second.bots)).toEqual(Object.keys(first.bots));
|
|
104
|
+
const forced = await svc.get(true);
|
|
105
|
+
expect(Object.keys(forced)).toEqual(Object.keys(first));
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("消息收发计入滑动窗口计数器", async () => {
|
|
109
|
+
const bot = [...app.bots][0];
|
|
110
|
+
expect(bot).toBeDefined();
|
|
111
|
+
const before = bot?._messageSent.get() ?? 0;
|
|
112
|
+
const receivedBefore = bot?._messageReceived.get() ?? 0;
|
|
113
|
+
// 收到一条用户消息(触发 message 事件),机器人无回复
|
|
114
|
+
await client.receive("ping-should-not-reply");
|
|
115
|
+
expect(bot?._messageReceived.get()).toBe(receivedBefore + 1);
|
|
116
|
+
expect(bot?._messageSent.get()).toBe(before);
|
|
117
|
+
|
|
118
|
+
const data = await profile().get(true);
|
|
119
|
+
const entry = data.bots[bot?.sid ?? ""];
|
|
120
|
+
expect(entry?.messageReceived).toBe(receivedBefore + 1);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("滑动窗口计数器经秒级 tick 后继续累加", async () => {
|
|
124
|
+
const bot = [...app.bots][0];
|
|
125
|
+
expect(bot).toBeDefined();
|
|
126
|
+
bot?._messageSent.add(1);
|
|
127
|
+
expect(bot?._messageSent.get()).toBe(1);
|
|
128
|
+
// 等待一次以上的窗口滑动(计数移入历史槽位),新计数继续记入队头
|
|
129
|
+
await new Promise((resolve) => setTimeout(resolve, 1100));
|
|
130
|
+
bot?._messageSent.add(2);
|
|
131
|
+
expect(bot?._messageSent.get()).toBe(3);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("login-added / login-removed / login-updated 事件驱动刷新", async () => {
|
|
135
|
+
const bot = [...app.bots][0];
|
|
136
|
+
expect(bot).toBeDefined();
|
|
137
|
+
// login-added:重新初始化计数器并防抖刷新(bot 已由上方断言保证存在)。
|
|
138
|
+
// satori 把 login-* 载荷声明为 Session,本仓运行时实际按 { bot } 对象分发
|
|
139
|
+
// (profile.ts 按此解构),为不改运行时行为仅做类型层断言
|
|
140
|
+
app.emit("login-added", { bot: bot! } as never);
|
|
141
|
+
expect(bot?._messageSent).toBeDefined();
|
|
142
|
+
// login-removed:停掉计数器(stop 被调用不抛错)并刷新
|
|
143
|
+
app.emit("login-removed", { bot: bot! } as never);
|
|
144
|
+
// login-updated:仅刷新
|
|
145
|
+
app.emit("login-updated", bot! as never);
|
|
146
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("status 指令输出机器人状态与性能摘要", async () => {
|
|
150
|
+
const replies = await client.receive("status");
|
|
151
|
+
expect(replies).toHaveLength(1);
|
|
152
|
+
expect(replies[0]).toContain("mock");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("两个子服务的 Config 均可解析", () => {
|
|
156
|
+
expect(ProfileProvider.Config({})).toEqual({ tickInterval: 5000 });
|
|
157
|
+
expect(EnvInfoProvider.Config({})).toEqual({});
|
|
158
|
+
});
|
|
159
|
+
});
|
package/src/index.ts
CHANGED
package/src/profile.ts
CHANGED
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*/
|
package/src/which-pm-runs.d.ts
DELETED
|
@@ -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
|
-
}
|