@jsbot-tellus/core 2.5.0 → 2.6.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.
Files changed (175) hide show
  1. package/README.md +8 -5
  2. package/dist/app/cli.d.ts +10 -0
  3. package/dist/app/cli.d.ts.map +1 -0
  4. package/dist/app/cli.js +37 -0
  5. package/dist/app/cli.js.map +1 -0
  6. package/dist/app/runtime.d.ts +121 -0
  7. package/dist/app/runtime.d.ts.map +1 -0
  8. package/dist/app/runtime.js +296 -0
  9. package/dist/app/runtime.js.map +1 -0
  10. package/dist/app.js +63 -273
  11. package/dist/app.js.map +1 -1
  12. package/dist/core/plugin/command-params.d.ts +3 -37
  13. package/dist/core/plugin/command-params.d.ts.map +1 -1
  14. package/dist/core/plugin/command-params.js +2 -580
  15. package/dist/core/plugin/command-params.js.map +1 -1
  16. package/dist/core/plugin/install/paths.d.ts +7 -0
  17. package/dist/core/plugin/install/paths.d.ts.map +1 -0
  18. package/dist/core/plugin/install/paths.js +49 -0
  19. package/dist/core/plugin/install/paths.js.map +1 -0
  20. package/dist/core/plugin/install/state.d.ts +23 -0
  21. package/dist/core/plugin/install/state.d.ts.map +1 -0
  22. package/dist/core/plugin/install/state.js +129 -0
  23. package/dist/core/plugin/install/state.js.map +1 -0
  24. package/dist/core/plugin/install/validate-script.d.ts +3 -0
  25. package/dist/core/plugin/install/validate-script.d.ts.map +1 -0
  26. package/dist/core/plugin/install/validate-script.js +154 -0
  27. package/dist/core/plugin/install/validate-script.js.map +1 -0
  28. package/dist/core/plugin/install-dir.d.ts +3 -28
  29. package/dist/core/plugin/install-dir.d.ts.map +1 -1
  30. package/dist/core/plugin/install-dir.js +3 -326
  31. package/dist/core/plugin/install-dir.js.map +1 -1
  32. package/dist/core/plugin/installer/checks.d.ts +6 -0
  33. package/dist/core/plugin/installer/checks.d.ts.map +1 -0
  34. package/dist/core/plugin/installer/checks.js +99 -0
  35. package/dist/core/plugin/installer/checks.js.map +1 -0
  36. package/dist/core/plugin/installer/support.d.ts +37 -0
  37. package/dist/core/plugin/installer/support.d.ts.map +1 -0
  38. package/dist/core/plugin/installer/support.js +67 -0
  39. package/dist/core/plugin/installer/support.js.map +1 -0
  40. package/dist/core/plugin/installer/warnings.d.ts +5 -0
  41. package/dist/core/plugin/installer/warnings.d.ts.map +1 -0
  42. package/dist/core/plugin/installer/warnings.js +28 -0
  43. package/dist/core/plugin/installer/warnings.js.map +1 -0
  44. package/dist/core/plugin/installer.d.ts +4 -30
  45. package/dist/core/plugin/installer.d.ts.map +1 -1
  46. package/dist/core/plugin/installer.js +21 -202
  47. package/dist/core/plugin/installer.js.map +1 -1
  48. package/dist/core/plugin/params/compile.d.ts +3 -0
  49. package/dist/core/plugin/params/compile.d.ts.map +1 -0
  50. package/dist/core/plugin/params/compile.js +23 -0
  51. package/dist/core/plugin/params/compile.js.map +1 -0
  52. package/dist/core/plugin/params/declaration.d.ts +9 -0
  53. package/dist/core/plugin/params/declaration.d.ts.map +1 -0
  54. package/dist/core/plugin/params/declaration.js +286 -0
  55. package/dist/core/plugin/params/declaration.js.map +1 -0
  56. package/dist/core/plugin/params/input.d.ts +5 -0
  57. package/dist/core/plugin/params/input.d.ts.map +1 -0
  58. package/dist/core/plugin/params/input.js +196 -0
  59. package/dist/core/plugin/params/input.js.map +1 -0
  60. package/dist/core/plugin/params/reply.d.ts +3 -0
  61. package/dist/core/plugin/params/reply.d.ts.map +1 -0
  62. package/dist/core/plugin/params/reply.js +49 -0
  63. package/dist/core/plugin/params/reply.js.map +1 -0
  64. package/dist/core/plugin/params/type-level.d.ts +24 -0
  65. package/dist/core/plugin/params/type-level.d.ts.map +1 -0
  66. package/dist/core/plugin/params/type-level.js +2 -0
  67. package/dist/core/plugin/params/type-level.js.map +1 -0
  68. package/dist/core/plugin/params/types.d.ts +33 -0
  69. package/dist/core/plugin/params/types.d.ts.map +1 -0
  70. package/dist/core/plugin/params/types.js +12 -0
  71. package/dist/core/plugin/params/types.js.map +1 -0
  72. package/dist/core/plugin/params/values.d.ts +7 -0
  73. package/dist/core/plugin/params/values.d.ts.map +1 -0
  74. package/dist/core/plugin/params/values.js +111 -0
  75. package/dist/core/plugin/params/values.js.map +1 -0
  76. package/dist/core/web/api.d.ts +2 -14
  77. package/dist/core/web/api.d.ts.map +1 -1
  78. package/dist/core/web/api.js +8 -500
  79. package/dist/core/web/api.js.map +1 -1
  80. package/dist/core/web/routes/actions.d.ts +4 -0
  81. package/dist/core/web/routes/actions.d.ts.map +1 -0
  82. package/dist/core/web/routes/actions.js +33 -0
  83. package/dist/core/web/routes/actions.js.map +1 -0
  84. package/dist/core/web/routes/config.d.ts +5 -0
  85. package/dist/core/web/routes/config.d.ts.map +1 -0
  86. package/dist/core/web/routes/config.js +30 -0
  87. package/dist/core/web/routes/config.js.map +1 -0
  88. package/dist/core/web/routes/kv.d.ts +6 -0
  89. package/dist/core/web/routes/kv.d.ts.map +1 -0
  90. package/dist/core/web/routes/kv.js +80 -0
  91. package/dist/core/web/routes/kv.js.map +1 -0
  92. package/dist/core/web/routes/plugins.d.ts +5 -0
  93. package/dist/core/web/routes/plugins.d.ts.map +1 -0
  94. package/dist/core/web/routes/plugins.js +98 -0
  95. package/dist/core/web/routes/plugins.js.map +1 -0
  96. package/dist/core/web/routes/sessions.d.ts +8 -0
  97. package/dist/core/web/routes/sessions.d.ts.map +1 -0
  98. package/dist/core/web/routes/sessions.js +130 -0
  99. package/dist/core/web/routes/sessions.js.map +1 -0
  100. package/dist/core/web/routes/shared.d.ts +18 -0
  101. package/dist/core/web/routes/shared.d.ts.map +1 -0
  102. package/dist/core/web/routes/shared.js +19 -0
  103. package/dist/core/web/routes/shared.js.map +1 -0
  104. package/dist/core/web/routes/status.d.ts +6 -0
  105. package/dist/core/web/routes/status.d.ts.map +1 -0
  106. package/dist/core/web/routes/status.js +132 -0
  107. package/dist/core/web/routes/status.js.map +1 -0
  108. package/dist/core/web/server/auth.d.ts +17 -0
  109. package/dist/core/web/server/auth.d.ts.map +1 -0
  110. package/dist/core/web/server/auth.js +68 -0
  111. package/dist/core/web/server/auth.js.map +1 -0
  112. package/dist/core/web/server/csp.d.ts +11 -0
  113. package/dist/core/web/server/csp.d.ts.map +1 -0
  114. package/dist/core/web/server/csp.js +31 -0
  115. package/dist/core/web/server/csp.js.map +1 -0
  116. package/dist/core/web/server/request.d.ts +6 -0
  117. package/dist/core/web/server/request.d.ts.map +1 -0
  118. package/dist/core/web/server/request.js +33 -0
  119. package/dist/core/web/server/request.js.map +1 -0
  120. package/dist/core/web/server/security.d.ts +4 -0
  121. package/dist/core/web/server/security.d.ts.map +1 -0
  122. package/dist/core/web/server/security.js +12 -0
  123. package/dist/core/web/server/security.js.map +1 -0
  124. package/dist/core/web/server/sse.d.ts +18 -0
  125. package/dist/core/web/server/sse.d.ts.map +1 -0
  126. package/dist/core/web/server/sse.js +49 -0
  127. package/dist/core/web/server/sse.js.map +1 -0
  128. package/dist/core/web/server/static.d.ts +16 -0
  129. package/dist/core/web/server/static.d.ts.map +1 -0
  130. package/dist/core/web/server/static.js +80 -0
  131. package/dist/core/web/server/static.js.map +1 -0
  132. package/dist/core/web/server.d.ts +0 -7
  133. package/dist/core/web/server.d.ts.map +1 -1
  134. package/dist/core/web/server.js +41 -263
  135. package/dist/core/web/server.js.map +1 -1
  136. package/docs/architecture.md +28 -11
  137. package/docs/configuration.md +1 -1
  138. package/docs/migration.md +2 -2
  139. package/docs/plugin-api.md +24 -11
  140. package/docs/plugin-development.md +1 -1
  141. package/package.json +19 -17
  142. package/src/app/cli.ts +47 -0
  143. package/src/app/runtime.ts +522 -0
  144. package/src/app.ts +87 -395
  145. package/src/core/plugin/command-params.ts +8 -871
  146. package/src/core/plugin/install/paths.ts +72 -0
  147. package/src/core/plugin/install/state.ts +196 -0
  148. package/src/core/plugin/install/validate-script.ts +154 -0
  149. package/src/core/plugin/install-dir.ts +19 -427
  150. package/src/core/plugin/installer/checks.ts +137 -0
  151. package/src/core/plugin/installer/support.ts +120 -0
  152. package/src/core/plugin/installer/warnings.ts +52 -0
  153. package/src/core/plugin/installer.ts +52 -301
  154. package/src/core/plugin/params/compile.ts +41 -0
  155. package/src/core/plugin/params/declaration.ts +394 -0
  156. package/src/core/plugin/params/input.ts +281 -0
  157. package/src/core/plugin/params/reply.ts +59 -0
  158. package/src/core/plugin/params/type-level.ts +75 -0
  159. package/src/core/plugin/params/types.ts +55 -0
  160. package/src/core/plugin/params/values.ts +150 -0
  161. package/src/core/web/api.ts +33 -759
  162. package/src/core/web/routes/actions.ts +43 -0
  163. package/src/core/web/routes/config.ts +42 -0
  164. package/src/core/web/routes/kv.ts +122 -0
  165. package/src/core/web/routes/plugins.ts +151 -0
  166. package/src/core/web/routes/sessions.ts +188 -0
  167. package/src/core/web/routes/shared.ts +42 -0
  168. package/src/core/web/routes/status.ts +191 -0
  169. package/src/core/web/server/auth.ts +126 -0
  170. package/src/core/web/server/csp.ts +33 -0
  171. package/src/core/web/server/request.ts +38 -0
  172. package/src/core/web/server/security.ts +14 -0
  173. package/src/core/web/server/sse.ts +74 -0
  174. package/src/core/web/server/static.ts +112 -0
  175. package/src/core/web/server.ts +41 -383
@@ -0,0 +1,59 @@
1
+ import { parseSegments, type OneBotMessageEvent } from '@snowluma/sdk'
2
+
3
+ function isRecord(value: unknown): value is Record<string, unknown> {
4
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
5
+ }
6
+
7
+ function toMessageId(value: unknown): number | undefined {
8
+ const raw = typeof value === 'string' ? value.trim() : value
9
+
10
+ if (typeof raw !== 'number' && typeof raw !== 'string') {
11
+ return undefined
12
+ }
13
+
14
+ const parsed =
15
+ typeof raw === 'string'
16
+ ? /^-?\d+$/.test(raw)
17
+ ? Number(raw)
18
+ : Number.NaN
19
+ : raw
20
+
21
+ return Number.isSafeInteger(parsed) && parsed !== 0 ? parsed : undefined
22
+ }
23
+
24
+ function replyIdIn(segments: readonly unknown[]): number | undefined {
25
+ for (const segment of segments) {
26
+ if (!isRecord(segment) || segment.type !== 'reply') continue
27
+
28
+ const data = isRecord(segment.data) ? segment.data : undefined
29
+ const id = toMessageId(data?.id)
30
+
31
+ if (id !== undefined) return id
32
+ }
33
+
34
+ return undefined
35
+ }
36
+
37
+ export function quoteIdOf(
38
+ event: OneBotMessageEvent | undefined
39
+ ): number | undefined {
40
+ if (!event) return undefined
41
+
42
+ const message = event.message
43
+
44
+ if (typeof message === 'string') {
45
+ const id = replyIdIn(parseSegments(message))
46
+
47
+ if (id !== undefined) return id
48
+ } else if (Array.isArray(message)) {
49
+ const id = replyIdIn(message)
50
+
51
+ if (id !== undefined) return id
52
+ } else if (isRecord(message)) {
53
+ const id = replyIdIn([message])
54
+
55
+ if (id !== undefined) return id
56
+ }
57
+
58
+ return replyIdIn(parseSegments(event.raw_message))
59
+ }
@@ -0,0 +1,75 @@
1
+ type Whitespace = ' ' | '\n' | '\r' | '\t' | '\v' | '\f'
2
+
3
+ type TrimLeft<Source extends string> =
4
+ Source extends `${Whitespace}${infer Rest}` ? TrimLeft<Rest> : Source
5
+
6
+ type TrimRight<Source extends string> =
7
+ Source extends `${infer Rest}${Whitespace}` ? TrimRight<Rest> : Source
8
+
9
+ type Trim<Source extends string> = TrimLeft<TrimRight<Source>>
10
+
11
+ type StripLength<Type extends string> =
12
+ Trim<Type> extends `${infer Base}(${string})` ? Trim<Base> : Trim<Type>
13
+
14
+ type ParamValue<Type extends string> =
15
+ StripLength<Type> extends 'string' | 'text'
16
+ ? string
17
+ : StripLength<Type> extends
18
+ 'number' | 'integer' | 'at' | 'reply' | 'time'
19
+ ? number
20
+ : StripLength<Type> extends 'boolean'
21
+ ? boolean
22
+ : unknown
23
+
24
+ interface VariadicParam {
25
+ readonly variadic: true
26
+ }
27
+
28
+ type ParamType<Head extends string> = Head extends `${string}:${infer Type}`
29
+ ? ParamValue<Type>
30
+ : string
31
+
32
+ type ParamElement<Body extends string, Required extends boolean> =
33
+ Trim<Body> extends `...${string}`
34
+ ? VariadicParam
35
+ : Trim<Body> extends `${infer Head}=${string}`
36
+ ? ParamType<Head>
37
+ : Required extends true
38
+ ? ParamType<Body>
39
+ : ParamType<Body> | undefined
40
+
41
+ type AppendParam<Acc extends unknown[], Element> = [Element] extends [
42
+ VariadicParam,
43
+ ]
44
+ ? [...Acc, ...string[]]
45
+ : [...Acc, Element]
46
+
47
+ type ParseCommandParams<Source extends string, Acc extends unknown[] = []> =
48
+ Trim<Source> extends ''
49
+ ? Acc
50
+ : Trim<Source> extends `<${infer Body}>${infer Rest}`
51
+ ? ParamElement<Body, true> extends VariadicParam
52
+ ? any[]
53
+ : ParseCommandParams<
54
+ Rest,
55
+ AppendParam<Acc, ParamElement<Body, true>>
56
+ >
57
+ : Trim<Source> extends `[${infer Body}]${infer Rest}`
58
+ ? ParamElement<Body, false> extends VariadicParam
59
+ ? Trim<Rest> extends ''
60
+ ? [...Acc, ...string[]]
61
+ : any[]
62
+ : ParseCommandParams<
63
+ Rest,
64
+ AppendParam<Acc, ParamElement<Body, false>>
65
+ >
66
+ : any[]
67
+
68
+ /**
69
+ * 把 `params` 声明字符串推导成 handler 的位置参数 tuple。
70
+ *
71
+ * 非字面量声明(例如运行时拼接的 `string`)或无法解析的声明会回退为
72
+ * `any[]`,以保证现有插件仍然可以编译。
73
+ */
74
+ export type CommandParamArgs<Declaration extends string> =
75
+ string extends Declaration ? any[] : ParseCommandParams<Declaration>
@@ -0,0 +1,55 @@
1
+ import type { OneBotMessageEvent } from '@snowluma/sdk'
2
+
3
+ export type CommandParamType =
4
+ | 'string'
5
+ | 'text'
6
+ | 'number'
7
+ | 'integer'
8
+ | 'boolean'
9
+ | 'at'
10
+ | 'reply'
11
+ | 'time'
12
+
13
+ export interface ParsedCommandParams {
14
+ args: unknown[]
15
+ ignored: string[]
16
+ }
17
+
18
+ export interface CompiledCommandParams {
19
+ usage: string
20
+ parse(input: string, event?: OneBotMessageEvent): ParsedCommandParams
21
+ }
22
+
23
+ export class CommandParamsError extends Error {
24
+ readonly usage: string
25
+
26
+ constructor(message: string, usage: string) {
27
+ super(message)
28
+ this.name = 'CommandParamsError'
29
+ this.usage = usage
30
+ }
31
+ }
32
+
33
+ export interface ParamLength {
34
+ min?: number | undefined
35
+ max?: number | undefined
36
+ }
37
+
38
+ export interface ParamSpec {
39
+ name: string
40
+ type: CommandParamType
41
+ required: boolean
42
+ rest: boolean
43
+ hasDefault: boolean
44
+ defaultValue?: unknown
45
+ length?: ParamLength | undefined
46
+ }
47
+
48
+ export interface InputToken {
49
+ value: string
50
+ start: number
51
+ }
52
+
53
+ export function declarationError(message: string): Error {
54
+ return new Error(`参数声明错误:${message}`)
55
+ }
@@ -0,0 +1,150 @@
1
+ import { parseSegments } from '@snowluma/sdk'
2
+ import { CommandParamsError, declarationError } from './types.js'
3
+
4
+ export function unquote(value: string): string {
5
+ const trimmed = value.trim()
6
+ const quote = trimmed[0]
7
+
8
+ if (quote !== '"' && quote !== "'") {
9
+ return trimmed
10
+ }
11
+
12
+ if (trimmed.length < 2 || trimmed[trimmed.length - 1] !== quote) {
13
+ throw declarationError('默认值引号未闭合')
14
+ }
15
+
16
+ let result = ''
17
+
18
+ for (let index = 1; index < trimmed.length - 1; index += 1) {
19
+ const char = trimmed[index]
20
+
21
+ if (char === undefined) break
22
+
23
+ if (char === '\\') {
24
+ const next = trimmed[index + 1]
25
+
26
+ if (next !== undefined) {
27
+ result += next
28
+ index += 1
29
+ }
30
+
31
+ continue
32
+ }
33
+
34
+ result += char
35
+ }
36
+
37
+ return result
38
+ }
39
+
40
+ export function measureLength(value: string): number {
41
+ return Array.from(value).length
42
+ }
43
+
44
+ export function parseBoolean(value: string): boolean | null {
45
+ const normalized = value.trim().toLowerCase()
46
+
47
+ if (['true', '1', 'yes', 'y', 'on'].includes(normalized)) {
48
+ return true
49
+ }
50
+
51
+ if (['false', '0', 'no', 'n', 'off'].includes(normalized)) {
52
+ return false
53
+ }
54
+
55
+ return null
56
+ }
57
+
58
+ export function parseAt(value: string, name: string, usage: string): number {
59
+ const raw = value.trim()
60
+ const segments = parseSegments(raw)
61
+ const segment = segments[0]
62
+
63
+ if (segments.length === 1 && segment?.type === 'at') {
64
+ const qq = segment.data.qq
65
+
66
+ if (qq !== 'all') {
67
+ const parsed = Number(qq)
68
+
69
+ if (Number.isSafeInteger(parsed) && parsed > 0) {
70
+ return parsed
71
+ }
72
+ }
73
+ } else if (/^\d+$/.test(raw)) {
74
+ const parsed = Number(raw)
75
+
76
+ if (Number.isSafeInteger(parsed) && parsed > 0) {
77
+ return parsed
78
+ }
79
+ }
80
+
81
+ throw new CommandParamsError(
82
+ `参数 ${name} 需要 @ 一个用户或填写 QQ 号`,
83
+ usage
84
+ )
85
+ }
86
+
87
+ const TIME_UNITS: Record<string, number> = {
88
+ s: 1,
89
+ 秒: 1,
90
+ m: 60,
91
+ 分: 60,
92
+ 分钟: 60,
93
+ h: 3600,
94
+ 时: 3600,
95
+ 小时: 3600,
96
+ d: 86400,
97
+ 天: 86400,
98
+ w: 604800,
99
+ 周: 604800,
100
+ }
101
+
102
+ const TIME_PART = /^(\d+(?:\.\d+)?)(分钟|小时|秒|分|时|天|周|[smhdw])/i
103
+
104
+ export function parseTimeValue(value: string): number | null {
105
+ const raw = value.trim()
106
+
107
+ if (/^\d+$/.test(raw)) {
108
+ const seconds = Number(raw)
109
+
110
+ return Number.isSafeInteger(seconds) ? seconds : null
111
+ }
112
+
113
+ let index = 0
114
+ let total = 0
115
+ let parsed = false
116
+
117
+ while (index < raw.length) {
118
+ const match = TIME_PART.exec(raw.slice(index))
119
+
120
+ if (!match) return null
121
+
122
+ const unit = TIME_UNITS[match[2]?.toLowerCase() ?? '']
123
+ const amount = Number(match[1])
124
+
125
+ if (unit === undefined) return null
126
+
127
+ total += amount * unit
128
+ index += match[0].length
129
+ parsed = true
130
+ }
131
+
132
+ if (!parsed || !Number.isSafeInteger(total) || total < 0) {
133
+ return null
134
+ }
135
+
136
+ return total
137
+ }
138
+
139
+ export function parseTime(value: string, name: string, usage: string): number {
140
+ const seconds = parseTimeValue(value)
141
+
142
+ if (seconds === null) {
143
+ throw new CommandParamsError(
144
+ `参数 ${name} 需要是时间,例如 10s、5m、1h30m`,
145
+ usage
146
+ )
147
+ }
148
+
149
+ return seconds
150
+ }