@tx5dr/contracts 1.2.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 (180) hide show
  1. package/CLAUDE.md +41 -0
  2. package/dist/index.d.ts +55 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +86 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/schema/__tests__/plugin-runtime-log.schema.test.d.ts +2 -0
  7. package/dist/schema/__tests__/plugin-runtime-log.schema.test.d.ts.map +1 -0
  8. package/dist/schema/__tests__/plugin-runtime-log.schema.test.js +78 -0
  9. package/dist/schema/__tests__/plugin-runtime-log.schema.test.js.map +1 -0
  10. package/dist/schema/ability.d.ts +85 -0
  11. package/dist/schema/ability.d.ts.map +1 -0
  12. package/dist/schema/ability.js +119 -0
  13. package/dist/schema/ability.js.map +1 -0
  14. package/dist/schema/api-response.schema.d.ts +1017 -0
  15. package/dist/schema/api-response.schema.d.ts.map +1 -0
  16. package/dist/schema/api-response.schema.js +137 -0
  17. package/dist/schema/api-response.schema.js.map +1 -0
  18. package/dist/schema/audio-sidecar.schema.d.ts +106 -0
  19. package/dist/schema/audio-sidecar.schema.d.ts.map +1 -0
  20. package/dist/schema/audio-sidecar.schema.js +47 -0
  21. package/dist/schema/audio-sidecar.schema.js.map +1 -0
  22. package/dist/schema/audio.schema.d.ts +227 -0
  23. package/dist/schema/audio.schema.d.ts.map +1 -0
  24. package/dist/schema/audio.schema.js +56 -0
  25. package/dist/schema/audio.schema.js.map +1 -0
  26. package/dist/schema/auth.schema.d.ts +738 -0
  27. package/dist/schema/auth.schema.d.ts.map +1 -0
  28. package/dist/schema/auth.schema.js +161 -0
  29. package/dist/schema/auth.schema.js.map +1 -0
  30. package/dist/schema/cycle.schema.d.ts +33 -0
  31. package/dist/schema/cycle.schema.d.ts.map +1 -0
  32. package/dist/schema/cycle.schema.js +23 -0
  33. package/dist/schema/cycle.schema.js.map +1 -0
  34. package/dist/schema/desktop-https.schema.d.ts +146 -0
  35. package/dist/schema/desktop-https.schema.d.ts.map +1 -0
  36. package/dist/schema/desktop-https.schema.js +31 -0
  37. package/dist/schema/desktop-https.schema.js.map +1 -0
  38. package/dist/schema/ft8.schema.d.ts +1107 -0
  39. package/dist/schema/ft8.schema.d.ts.map +1 -0
  40. package/dist/schema/ft8.schema.js +268 -0
  41. package/dist/schema/ft8.schema.js.map +1 -0
  42. package/dist/schema/hello.schema.d.ts +10 -0
  43. package/dist/schema/hello.schema.d.ts.map +1 -0
  44. package/dist/schema/hello.schema.js +5 -0
  45. package/dist/schema/hello.schema.js.map +1 -0
  46. package/dist/schema/logbook.schema.d.ts +1472 -0
  47. package/dist/schema/logbook.schema.d.ts.map +1 -0
  48. package/dist/schema/logbook.schema.js +256 -0
  49. package/dist/schema/logbook.schema.js.map +1 -0
  50. package/dist/schema/mode.schema.d.ts +130 -0
  51. package/dist/schema/mode.schema.d.ts.map +1 -0
  52. package/dist/schema/mode.schema.js +141 -0
  53. package/dist/schema/mode.schema.js.map +1 -0
  54. package/dist/schema/openwebrx.schema.d.ts +352 -0
  55. package/dist/schema/openwebrx.schema.d.ts.map +1 -0
  56. package/dist/schema/openwebrx.schema.js +139 -0
  57. package/dist/schema/openwebrx.schema.js.map +1 -0
  58. package/dist/schema/operator.schema.d.ts +883 -0
  59. package/dist/schema/operator.schema.d.ts.map +1 -0
  60. package/dist/schema/operator.schema.js +97 -0
  61. package/dist/schema/operator.schema.js.map +1 -0
  62. package/dist/schema/plugin.schema.d.ts +1579 -0
  63. package/dist/schema/plugin.schema.d.ts.map +1 -0
  64. package/dist/schema/plugin.schema.js +336 -0
  65. package/dist/schema/plugin.schema.js.map +1 -0
  66. package/dist/schema/process-monitor.schema.d.ts +282 -0
  67. package/dist/schema/process-monitor.schema.d.ts.map +1 -0
  68. package/dist/schema/process-monitor.schema.js +31 -0
  69. package/dist/schema/process-monitor.schema.js.map +1 -0
  70. package/dist/schema/pskreporter.schema.d.ts +185 -0
  71. package/dist/schema/pskreporter.schema.d.ts.map +1 -0
  72. package/dist/schema/pskreporter.schema.js +80 -0
  73. package/dist/schema/pskreporter.schema.js.map +1 -0
  74. package/dist/schema/qso.schema.d.ts +419 -0
  75. package/dist/schema/qso.schema.d.ts.map +1 -0
  76. package/dist/schema/qso.schema.js +137 -0
  77. package/dist/schema/qso.schema.js.map +1 -0
  78. package/dist/schema/radio-capability.schema.d.ts +625 -0
  79. package/dist/schema/radio-capability.schema.d.ts.map +1 -0
  80. package/dist/schema/radio-capability.schema.js +191 -0
  81. package/dist/schema/radio-capability.schema.js.map +1 -0
  82. package/dist/schema/radio-power-support.d.ts +45 -0
  83. package/dist/schema/radio-power-support.d.ts.map +1 -0
  84. package/dist/schema/radio-power-support.js +64 -0
  85. package/dist/schema/radio-power-support.js.map +1 -0
  86. package/dist/schema/radio-power.schema.d.ts +319 -0
  87. package/dist/schema/radio-power.schema.d.ts.map +1 -0
  88. package/dist/schema/radio-power.schema.js +87 -0
  89. package/dist/schema/radio-power.schema.js.map +1 -0
  90. package/dist/schema/radio-profile.schema.d.ts +2694 -0
  91. package/dist/schema/radio-profile.schema.d.ts.map +1 -0
  92. package/dist/schema/radio-profile.schema.js +71 -0
  93. package/dist/schema/radio-profile.schema.js.map +1 -0
  94. package/dist/schema/radio.schema.d.ts +1438 -0
  95. package/dist/schema/radio.schema.d.ts.map +1 -0
  96. package/dist/schema/radio.schema.js +314 -0
  97. package/dist/schema/radio.schema.js.map +1 -0
  98. package/dist/schema/realtime.schema.d.ts +1299 -0
  99. package/dist/schema/realtime.schema.d.ts.map +1 -0
  100. package/dist/schema/realtime.schema.js +184 -0
  101. package/dist/schema/realtime.schema.js.map +1 -0
  102. package/dist/schema/rigctld.schema.d.ts +151 -0
  103. package/dist/schema/rigctld.schema.d.ts.map +1 -0
  104. package/dist/schema/rigctld.schema.js +42 -0
  105. package/dist/schema/rigctld.schema.js.map +1 -0
  106. package/dist/schema/server-message-keys.d.ts +21 -0
  107. package/dist/schema/server-message-keys.d.ts.map +1 -0
  108. package/dist/schema/server-message-keys.js +22 -0
  109. package/dist/schema/server-message-keys.js.map +1 -0
  110. package/dist/schema/slot-info.schema.d.ts +696 -0
  111. package/dist/schema/slot-info.schema.d.ts.map +1 -0
  112. package/dist/schema/slot-info.schema.js +144 -0
  113. package/dist/schema/slot-info.schema.js.map +1 -0
  114. package/dist/schema/spectrum.schema.d.ts +716 -0
  115. package/dist/schema/spectrum.schema.d.ts.map +1 -0
  116. package/dist/schema/spectrum.schema.js +128 -0
  117. package/dist/schema/spectrum.schema.js.map +1 -0
  118. package/dist/schema/station-info.schema.d.ts +202 -0
  119. package/dist/schema/station-info.schema.d.ts.map +1 -0
  120. package/dist/schema/station-info.schema.js +32 -0
  121. package/dist/schema/station-info.schema.js.map +1 -0
  122. package/dist/schema/system.schema.d.ts +113 -0
  123. package/dist/schema/system.schema.d.ts.map +1 -0
  124. package/dist/schema/system.schema.js +95 -0
  125. package/dist/schema/system.schema.js.map +1 -0
  126. package/dist/schema/transmission.schema.d.ts +413 -0
  127. package/dist/schema/transmission.schema.d.ts.map +1 -0
  128. package/dist/schema/transmission.schema.js +78 -0
  129. package/dist/schema/transmission.schema.js.map +1 -0
  130. package/dist/schema/voice.schema.d.ts +38 -0
  131. package/dist/schema/voice.schema.d.ts.map +1 -0
  132. package/dist/schema/voice.schema.js +24 -0
  133. package/dist/schema/voice.schema.js.map +1 -0
  134. package/dist/schema/websocket.schema.d.ts +16916 -0
  135. package/dist/schema/websocket.schema.d.ts.map +1 -0
  136. package/dist/schema/websocket.schema.js +1050 -0
  137. package/dist/schema/websocket.schema.js.map +1 -0
  138. package/dist/utils/grid.d.ts +13 -0
  139. package/dist/utils/grid.d.ts.map +1 -0
  140. package/dist/utils/grid.js +71 -0
  141. package/dist/utils/grid.js.map +1 -0
  142. package/eslint.config.js +15 -0
  143. package/package.json +28 -0
  144. package/src/index.ts +152 -0
  145. package/src/schema/__tests__/plugin-runtime-log.schema.test.ts +90 -0
  146. package/src/schema/ability.ts +205 -0
  147. package/src/schema/api-response.schema.ts +187 -0
  148. package/src/schema/audio-sidecar.schema.ts +53 -0
  149. package/src/schema/audio.schema.ts +73 -0
  150. package/src/schema/auth.schema.ts +224 -0
  151. package/src/schema/cycle.schema.ts +25 -0
  152. package/src/schema/desktop-https.schema.ts +43 -0
  153. package/src/schema/ft8.schema.ts +362 -0
  154. package/src/schema/hello.schema.ts +7 -0
  155. package/src/schema/logbook.schema.ts +310 -0
  156. package/src/schema/mode.schema.ts +158 -0
  157. package/src/schema/openwebrx.schema.ts +160 -0
  158. package/src/schema/operator.schema.ts +112 -0
  159. package/src/schema/plugin.schema.ts +503 -0
  160. package/src/schema/process-monitor.schema.ts +41 -0
  161. package/src/schema/pskreporter.schema.ts +102 -0
  162. package/src/schema/qso.schema.ts +173 -0
  163. package/src/schema/radio-capability.schema.ts +244 -0
  164. package/src/schema/radio-power-support.ts +107 -0
  165. package/src/schema/radio-power.schema.ts +101 -0
  166. package/src/schema/radio-profile.schema.ts +91 -0
  167. package/src/schema/radio.schema.ts +385 -0
  168. package/src/schema/realtime.schema.ts +262 -0
  169. package/src/schema/rigctld.schema.ts +51 -0
  170. package/src/schema/server-message-keys.ts +26 -0
  171. package/src/schema/slot-info.schema.ts +162 -0
  172. package/src/schema/spectrum.schema.ts +166 -0
  173. package/src/schema/station-info.schema.ts +40 -0
  174. package/src/schema/system.schema.ts +121 -0
  175. package/src/schema/transmission.schema.ts +105 -0
  176. package/src/schema/voice.schema.ts +28 -0
  177. package/src/schema/websocket.schema.ts +1480 -0
  178. package/src/utils/grid.ts +94 -0
  179. package/test/mode-schema.test.ts +48 -0
  180. package/tsconfig.json +9 -0
@@ -0,0 +1,503 @@
1
+ import { z } from 'zod';
2
+
3
+ // ===== 核心枚举 =====
4
+
5
+ /**
6
+ * High-level plugin category.
7
+ *
8
+ * - `strategy`: owns the operator's automation runtime and is mutually
9
+ * exclusive per operator.
10
+ * - `utility`: composes with other utility plugins to filter, score, monitor or
11
+ * augment UI.
12
+ */
13
+ export const PluginTypeSchema = z.enum(['strategy', 'utility']);
14
+
15
+ /**
16
+ * High-level plugin category used by manifests and runtime status objects.
17
+ */
18
+ export type PluginType = z.infer<typeof PluginTypeSchema>;
19
+
20
+ /**
21
+ * Runtime instance scope for a plugin.
22
+ *
23
+ * - `operator`: the host creates one instance per operator.
24
+ * - `global`: the host creates a single shared instance for the whole station.
25
+ */
26
+ export const PluginInstanceScopeSchema = z.enum(['operator', 'global']);
27
+
28
+ /**
29
+ * Runtime instance scope for a plugin.
30
+ */
31
+ export type PluginInstanceScope = z.infer<typeof PluginInstanceScopeSchema>;
32
+
33
+ /**
34
+ * Explicit permission declarations requested by a plugin.
35
+ *
36
+ * Permissions let the host gate sensitive capabilities behind manifest-level
37
+ * intent. Plugins should request the smallest possible set.
38
+ */
39
+ export const PluginPermissionSchema = z.enum(['network']);
40
+
41
+ /**
42
+ * Explicit permission declarations requested by a plugin.
43
+ */
44
+ export type PluginPermission = z.infer<typeof PluginPermissionSchema>;
45
+
46
+ /**
47
+ * Built-in frontend renderer kinds supported by declarative plugin panels.
48
+ */
49
+ export const PluginPanelComponentSchema = z.enum(['table', 'key-value', 'chart', 'log', 'iframe']);
50
+
51
+ /**
52
+ * Built-in frontend renderer kinds supported by declarative plugin panels.
53
+ */
54
+ export type PluginPanelComponent = z.infer<typeof PluginPanelComponentSchema>;
55
+
56
+ // ===== 设置声明 =====
57
+
58
+ /**
59
+ * Supported generated-form field types for plugin settings.
60
+ *
61
+ * These values control both validation expectations and default frontend
62
+ * rendering in plugin settings UIs.
63
+ */
64
+ export const PluginSettingTypeSchema = z.enum(['boolean', 'number', 'string', 'string[]', 'info']);
65
+
66
+ /**
67
+ * Supported generated-form field types for plugin settings.
68
+ */
69
+ export type PluginSettingType = z.infer<typeof PluginSettingTypeSchema>;
70
+
71
+ /**
72
+ * Label/value pair used by select-like plugin settings.
73
+ */
74
+ export const PluginSettingOptionSchema = z.object({
75
+ label: z.string(),
76
+ value: z.string(),
77
+ });
78
+
79
+ /**
80
+ * Label/value pair used by select-like plugin settings.
81
+ */
82
+ export type PluginSettingOption = z.infer<typeof PluginSettingOptionSchema>;
83
+
84
+ /**
85
+ * Persistence and UI scope for a plugin setting.
86
+ *
87
+ * - `global`: shared by the whole station and typically edited in plugin
88
+ * management views.
89
+ * - `operator`: isolated per operator and typically edited in operator-specific
90
+ * automation settings.
91
+ */
92
+ export const PluginSettingScopeSchema = z.enum(['global', 'operator']);
93
+
94
+ /**
95
+ * Persistence and UI scope for a plugin setting.
96
+ */
97
+ export type PluginSettingScope = z.infer<typeof PluginSettingScopeSchema>;
98
+
99
+ /**
100
+ * Declarative description of a persisted plugin setting.
101
+ *
102
+ * The host uses this schema to generate configuration forms, validate updates
103
+ * and resolve default values before injecting them into `ctx.config`.
104
+ */
105
+ export const PluginSettingDescriptorSchema = z.object({
106
+ type: PluginSettingTypeSchema,
107
+ default: z.unknown(),
108
+ label: z.string(),
109
+ description: z.string().optional(),
110
+ min: z.number().optional(),
111
+ max: z.number().optional(),
112
+ options: z.array(PluginSettingOptionSchema).optional(),
113
+ /** 设置作用域:global(所有操作员共享)或 operator(每操作员独立),默认 global */
114
+ scope: PluginSettingScopeSchema.optional().default('global'),
115
+ });
116
+
117
+ /**
118
+ * Declarative description of a persisted plugin setting.
119
+ *
120
+ * `default` is the resolved fallback value, `label`/`description` power the UI,
121
+ * `min` and `max` constrain numeric fields, `options` enumerates valid choices
122
+ * for select-like inputs, and `scope` controls whether the value is shared or
123
+ * operator-specific.
124
+ */
125
+ export type PluginSettingDescriptor = z.infer<typeof PluginSettingDescriptorSchema>;
126
+
127
+ // ===== 快捷操作 =====
128
+
129
+ /**
130
+ * Declarative quick-action button shown in operator-facing plugin UI.
131
+ *
132
+ * Quick actions are intended for one-shot commands and are dispatched through
133
+ * the plugin user-action channel when clicked.
134
+ */
135
+ export const PluginQuickActionSchema = z.object({
136
+ id: z.string(),
137
+ label: z.string(),
138
+ icon: z.string().optional(),
139
+ });
140
+
141
+ /**
142
+ * Declarative quick-action button shown in operator-facing plugin UI.
143
+ */
144
+ export type PluginQuickAction = z.infer<typeof PluginQuickActionSchema>;
145
+
146
+ /**
147
+ * Shortcut reference to an operator-scope setting that should be surfaced in a
148
+ * compact quick-settings panel.
149
+ */
150
+ export const PluginQuickSettingSchema = z.object({
151
+ settingKey: z.string(),
152
+ });
153
+
154
+ /**
155
+ * Shortcut reference to an operator-scope setting that should be surfaced in a
156
+ * compact quick-settings panel.
157
+ */
158
+ export type PluginQuickSetting = z.infer<typeof PluginQuickSettingSchema>;
159
+
160
+ // ===== 能力标签 =====
161
+
162
+ /**
163
+ * Host-derived capability tags exposed to the frontend.
164
+ *
165
+ * These tags are computed from the plugin definition so the UI can reason
166
+ * about plugin roles without hard-coding specific plugin names.
167
+ */
168
+ export const PluginCapabilitySchema = z.enum([
169
+ 'auto_call_candidate',
170
+ 'auto_call_execution',
171
+ ]);
172
+
173
+ /**
174
+ * Host-derived capability tags exposed to the frontend.
175
+ */
176
+ export type PluginCapability = z.infer<typeof PluginCapabilitySchema>;
177
+
178
+ // ===== 面板 =====
179
+
180
+ /**
181
+ * Rendering slot that determines where a panel appears in the UI.
182
+ *
183
+ * - `operator`: shown in the expanded operator card's live-panel area (default).
184
+ * - `automation`: shown inside the top-right automation quick-action popover.
185
+ */
186
+ export const PluginPanelSlotSchema = z.enum(['operator', 'automation']);
187
+
188
+ /**
189
+ * Rendering slot that determines where a panel appears in the UI.
190
+ */
191
+ export type PluginPanelSlot = z.infer<typeof PluginPanelSlotSchema>;
192
+
193
+ /**
194
+ * Preferred width hint for plugin-owned panels.
195
+ *
196
+ * Hosts may interpret this hint differently per slot. Today the operator-card
197
+ * host treats `full` as "span the full row on desktop", while automation
198
+ * popover hosts may choose to ignore it.
199
+ */
200
+ export const PluginPanelWidthSchema = z.enum(['half', 'full']);
201
+
202
+ /**
203
+ * Preferred width hint for plugin-owned panels.
204
+ */
205
+ export type PluginPanelWidth = z.infer<typeof PluginPanelWidthSchema>;
206
+
207
+ /**
208
+ * Declarative definition of a plugin-owned panel in the frontend.
209
+ *
210
+ * Panels are passive containers rendered by the host. A plugin sends data into
211
+ * them through `ctx.ui.send(panelId, data)`. When `component` is `'iframe'`,
212
+ * the panel renders a custom UI page inside a sandboxed iframe instead.
213
+ */
214
+ export const PluginPanelDescriptorSchema = z.object({
215
+ id: z.string(),
216
+ title: z.string(),
217
+ component: PluginPanelComponentSchema,
218
+ /** Required when `component` is `'iframe'`. References a page id from `ui.pages`. */
219
+ pageId: z.string().optional(),
220
+ /** Where the panel renders. Defaults to `'operator'` (operator card live-panel area). */
221
+ slot: PluginPanelSlotSchema.optional(),
222
+ /** Preferred width hint. Defaults to `'half'`. */
223
+ width: PluginPanelWidthSchema.optional(),
224
+ });
225
+
226
+ /**
227
+ * Declarative definition of a plugin-owned panel in the frontend.
228
+ */
229
+ export type PluginPanelDescriptor = z.infer<typeof PluginPanelDescriptorSchema>;
230
+
231
+ // ===== 自定义 UI 页面 =====
232
+
233
+ /**
234
+ * Declarative descriptor for a custom UI page served from a plugin's static
235
+ * file directory and rendered inside an iframe by the host.
236
+ *
237
+ * Pages are registered in `PluginDefinition.ui.pages` and can be consumed by
238
+ * any host component via `<PluginIframeHost pluginName={...} pageId={...} />`.
239
+ */
240
+ export const PluginUIPageDescriptorSchema = z.object({
241
+ /** Unique page identifier within the plugin (e.g. 'settings', 'dashboard'). */
242
+ id: z.string(),
243
+ /** Display title (i18n key or literal text). */
244
+ title: z.string(),
245
+ /** Entry HTML file path relative to the UI directory (e.g. 'settings.html'). */
246
+ entry: z.string(),
247
+ /** Optional icon identifier. */
248
+ icon: z.string().optional(),
249
+ /** Who may access this page through the host iframe bridge. Defaults to admin. */
250
+ accessScope: z.enum(['admin', 'operator']).optional().default('admin'),
251
+ /** Optional resource binding enforced by the host for iframe invoke requests. */
252
+ resourceBinding: z.enum(['none', 'callsign', 'operator']).optional().default('none'),
253
+ });
254
+
255
+ /**
256
+ * Declarative descriptor for a custom UI page served from a plugin's static
257
+ * file directory.
258
+ */
259
+ export type PluginUIPageDescriptor = z.infer<typeof PluginUIPageDescriptorSchema>;
260
+
261
+ /**
262
+ * Declares that a plugin provides custom UI pages hosted in an iframe.
263
+ */
264
+ export const PluginUIConfigSchema = z.object({
265
+ /** Static file directory relative to the plugin root (default: 'ui'). */
266
+ dir: z.string().optional().default('ui'),
267
+ /** Registered custom UI pages. */
268
+ pages: z.array(PluginUIPageDescriptorSchema).optional().default([]),
269
+ });
270
+
271
+ /**
272
+ * Declares that a plugin provides custom UI pages hosted in an iframe.
273
+ */
274
+ export type PluginUIConfig = z.infer<typeof PluginUIConfigSchema>;
275
+
276
+ // ===== 存储配置 =====
277
+
278
+ /**
279
+ * Storage scope requested by a plugin.
280
+ */
281
+ export const PluginStorageScopeSchema = z.enum(['global', 'operator']);
282
+
283
+ /**
284
+ * Storage scope requested by a plugin.
285
+ */
286
+ export type PluginStorageScope = z.infer<typeof PluginStorageScopeSchema>;
287
+
288
+ /**
289
+ * Declares which persistent storage scopes the host should provision.
290
+ */
291
+ export const PluginStorageConfigSchema = z.object({
292
+ scopes: z.array(PluginStorageScopeSchema),
293
+ });
294
+
295
+ /**
296
+ * Declares which persistent storage scopes the host should provision.
297
+ */
298
+ export type PluginStorageConfig = z.infer<typeof PluginStorageConfigSchema>;
299
+
300
+ // ===== 插件清单 =====
301
+
302
+ /**
303
+ * Normalized manifest describing a plugin's static metadata and declarations.
304
+ *
305
+ * This is effectively the serializable subset of a plugin definition that the
306
+ * host can expose to management UI and diagnostics.
307
+ */
308
+ export const PluginManifestSchema = z.object({
309
+ name: z.string(),
310
+ version: z.string(),
311
+ type: PluginTypeSchema,
312
+ instanceScope: PluginInstanceScopeSchema.optional().default('operator'),
313
+ description: z.string().optional(),
314
+ permissions: z.array(PluginPermissionSchema).optional(),
315
+ settings: z.record(z.string(), PluginSettingDescriptorSchema).optional(),
316
+ quickActions: z.array(PluginQuickActionSchema).optional(),
317
+ quickSettings: z.array(PluginQuickSettingSchema).optional(),
318
+ panels: z.array(PluginPanelDescriptorSchema).optional(),
319
+ storage: PluginStorageConfigSchema.optional(),
320
+ ui: PluginUIConfigSchema.optional(),
321
+ });
322
+
323
+ /**
324
+ * Normalized manifest describing a plugin's static metadata and declarations.
325
+ */
326
+ export type PluginManifest = z.infer<typeof PluginManifestSchema>;
327
+
328
+ // ===== 运行时状态(推送给前端) =====
329
+
330
+ /**
331
+ * Runtime-facing plugin status snapshot exposed to the frontend.
332
+ *
333
+ * This extends the static manifest with host state such as whether the plugin
334
+ * is loaded, enabled, auto-disabled or currently assigned to operators.
335
+ */
336
+ export const PluginStatusSchema = z.object({
337
+ name: z.string(),
338
+ type: PluginTypeSchema,
339
+ instanceScope: PluginInstanceScopeSchema.optional().default('operator'),
340
+ version: z.string(),
341
+ description: z.string().optional(),
342
+ isBuiltIn: z.boolean(),
343
+ loaded: z.boolean().default(true),
344
+ enabled: z.boolean(),
345
+ /** 是否被自动禁用(连续错误达到阈值) */
346
+ autoDisabled: z.boolean().optional().default(false),
347
+ errorCount: z.number(),
348
+ lastError: z.string().optional(),
349
+ /** 仅对 strategy 插件有意义:当前被哪些 operator 选中 */
350
+ assignedOperatorIds: z.array(z.string()).optional(),
351
+ settings: z.record(z.string(), PluginSettingDescriptorSchema).optional(),
352
+ quickActions: z.array(PluginQuickActionSchema).optional(),
353
+ quickSettings: z.array(PluginQuickSettingSchema).optional(),
354
+ panels: z.array(PluginPanelDescriptorSchema).optional(),
355
+ permissions: z.array(PluginPermissionSchema).optional(),
356
+ capabilities: z.array(PluginCapabilitySchema).optional(),
357
+ ui: PluginUIConfigSchema.optional(),
358
+ locales: z.record(z.string(), z.record(z.string(), z.string())).optional(),
359
+ });
360
+
361
+ /**
362
+ * Runtime-facing plugin status snapshot exposed to the frontend.
363
+ */
364
+ export type PluginStatus = z.infer<typeof PluginStatusSchema>;
365
+
366
+ export const PluginSystemStateSchema = z.enum(['ready', 'reloading', 'error']);
367
+ export type PluginSystemState = z.infer<typeof PluginSystemStateSchema>;
368
+
369
+ export const PluginSystemSnapshotSchema = z.object({
370
+ state: PluginSystemStateSchema,
371
+ generation: z.number().int().nonnegative(),
372
+ plugins: z.array(PluginStatusSchema),
373
+ lastError: z.string().optional(),
374
+ });
375
+ export type PluginSystemSnapshot = z.infer<typeof PluginSystemSnapshotSchema>;
376
+
377
+ // ===== 插件宿主运行时信息 =====
378
+
379
+ export const PluginDistributionSchema = z.enum([
380
+ 'electron',
381
+ 'docker',
382
+ 'linux-service',
383
+ 'generic-server',
384
+ 'web-dev',
385
+ ]);
386
+ export type PluginDistribution = z.infer<typeof PluginDistributionSchema>;
387
+
388
+ export const PluginRuntimeInfoSchema = z.object({
389
+ pluginDir: z.string(),
390
+ pluginDataDir: z.string(),
391
+ dataDir: z.string(),
392
+ configDir: z.string(),
393
+ logsDir: z.string(),
394
+ cacheDir: z.string(),
395
+ distribution: PluginDistributionSchema,
396
+ hostPluginDirHint: z.string().optional(),
397
+ });
398
+ export type PluginRuntimeInfo = z.infer<typeof PluginRuntimeInfoSchema>;
399
+
400
+ // ===== 持久化配置(存入 config.json) =====
401
+
402
+ /**
403
+ * 单个插件的持久化配置
404
+ */
405
+ export const PluginConfigEntrySchema = z.object({
406
+ enabled: z.boolean(),
407
+ settings: z.record(z.string(), z.unknown()),
408
+ });
409
+ export type PluginConfigEntry = z.infer<typeof PluginConfigEntrySchema>;
410
+
411
+ /**
412
+ * 所有插件的持久化配置
413
+ */
414
+ export const PluginsConfigSchema = z.object({
415
+ /** 全局插件配置(enabled 状态 + global scope settings) */
416
+ configs: z.record(z.string(), PluginConfigEntrySchema).optional().default({}),
417
+ /** 每操作员的策略插件选择 */
418
+ operatorStrategies: z.record(z.string(), z.string()).optional().default({}),
419
+ /** 每操作员的 operator scope plugin settings:operatorId → pluginName → settings */
420
+ operatorSettings: z.record(
421
+ z.string(),
422
+ z.record(z.string(), z.record(z.string(), z.unknown()))
423
+ ).optional().default({}),
424
+ });
425
+ export type PluginsConfig = z.infer<typeof PluginsConfigSchema>;
426
+
427
+ // ===== WebSocket 数据载荷 =====
428
+
429
+ /**
430
+ * 插件数据推送载荷(ctx.ui.send() 发送到前端)
431
+ */
432
+ export const PluginDataPayloadSchema = z.object({
433
+ pluginName: z.string(),
434
+ operatorId: z.string(),
435
+ panelId: z.string(),
436
+ data: z.unknown(),
437
+ });
438
+ export type PluginDataPayload = z.infer<typeof PluginDataPayloadSchema>;
439
+
440
+ /**
441
+ * 插件日志条目
442
+ */
443
+ export const PluginLogEntrySchema = z.object({
444
+ pluginName: z.string(),
445
+ level: z.enum(['debug', 'info', 'warn', 'error']),
446
+ message: z.string(),
447
+ data: z.unknown().optional(),
448
+ timestamp: z.number(),
449
+ });
450
+ export type PluginLogEntry = z.infer<typeof PluginLogEntrySchema>;
451
+
452
+ export const PluginRuntimeLogStageSchema = z.enum([
453
+ 'scan',
454
+ 'load',
455
+ 'validate',
456
+ 'reload',
457
+ 'activate',
458
+ ]);
459
+ export type PluginRuntimeLogStage = z.infer<typeof PluginRuntimeLogStageSchema>;
460
+
461
+ /**
462
+ * 宿主级插件运行日志条目
463
+ */
464
+ export const PluginRuntimeLogEntrySchema = z.object({
465
+ source: z.literal('system'),
466
+ stage: PluginRuntimeLogStageSchema,
467
+ level: z.enum(['debug', 'info', 'warn', 'error']),
468
+ message: z.string(),
469
+ timestamp: z.number(),
470
+ pluginName: z.string().optional(),
471
+ directoryName: z.string().optional(),
472
+ details: z.unknown().optional(),
473
+ });
474
+ export type PluginRuntimeLogEntry = z.infer<typeof PluginRuntimeLogEntrySchema>;
475
+
476
+ /**
477
+ * 宿主级插件运行日志历史响应载荷
478
+ */
479
+ export const PluginRuntimeLogHistoryPayloadSchema = z.object({
480
+ entries: z.array(PluginRuntimeLogEntrySchema),
481
+ });
482
+ export type PluginRuntimeLogHistoryPayload = z.infer<typeof PluginRuntimeLogHistoryPayloadSchema>;
483
+
484
+ /**
485
+ * 插件用户操作载荷(前端 → 后端)
486
+ */
487
+ export const PluginUserActionPayloadSchema = z.object({
488
+ pluginName: z.string(),
489
+ actionId: z.string(),
490
+ operatorId: z.string().optional(),
491
+ payload: z.unknown().optional(),
492
+ });
493
+ export type PluginUserActionPayload = z.infer<typeof PluginUserActionPayloadSchema>;
494
+
495
+ /**
496
+ * 操作员维度的插件设置更新载荷
497
+ */
498
+ export const PluginOperatorSettingsPayloadSchema = z.object({
499
+ pluginName: z.string(),
500
+ operatorId: z.string(),
501
+ settings: z.record(z.string(), z.unknown()),
502
+ });
503
+ export type PluginOperatorSettingsPayload = z.infer<typeof PluginOperatorSettingsPayloadSchema>;
@@ -0,0 +1,41 @@
1
+ import { z } from 'zod';
2
+
3
+ export const ProcessMemorySchema = z.object({
4
+ heapUsed: z.number(),
5
+ heapTotal: z.number(),
6
+ rss: z.number(),
7
+ external: z.number(),
8
+ arrayBuffers: z.number(),
9
+ });
10
+
11
+ export const ProcessCpuSchema = z.object({
12
+ user: z.number(),
13
+ system: z.number(),
14
+ total: z.number(),
15
+ });
16
+
17
+ export const EventLoopDelaySchema = z.object({
18
+ mean: z.number(),
19
+ p50: z.number(),
20
+ p99: z.number(),
21
+ });
22
+
23
+ export const ProcessSnapshotSchema = z.object({
24
+ timestamp: z.number(),
25
+ uptimeSeconds: z.number(),
26
+ memory: ProcessMemorySchema,
27
+ cpu: ProcessCpuSchema,
28
+ eventLoop: EventLoopDelaySchema,
29
+ });
30
+
31
+ export const ProcessSnapshotHistorySchema = z.object({
32
+ snapshots: z.array(ProcessSnapshotSchema),
33
+ intervalMs: z.number(),
34
+ maxHistory: z.number(),
35
+ });
36
+
37
+ export type ProcessMemory = z.infer<typeof ProcessMemorySchema>;
38
+ export type ProcessCpu = z.infer<typeof ProcessCpuSchema>;
39
+ export type EventLoopDelay = z.infer<typeof EventLoopDelaySchema>;
40
+ export type ProcessSnapshot = z.infer<typeof ProcessSnapshotSchema>;
41
+ export type ProcessSnapshotHistory = z.infer<typeof ProcessSnapshotHistorySchema>;
@@ -0,0 +1,102 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * PSKReporter 统计信息 Schema
5
+ */
6
+ export const PSKReporterStatsSchema = z.object({
7
+ /** 最后成功上报时间 (Unix毫秒) */
8
+ lastReportTime: z.number().optional(),
9
+ /** 今日上报数量 */
10
+ todayReportCount: z.number().default(0),
11
+ /** 总上报数量 */
12
+ totalReportCount: z.number().default(0),
13
+ /** 最后错误信息 */
14
+ lastError: z.string().optional(),
15
+ /** 连续失败次数 */
16
+ consecutiveFailures: z.number().default(0),
17
+ });
18
+
19
+ /**
20
+ * PSKReporter 配置 Schema
21
+ */
22
+ export const PSKReporterConfigSchema = z.object({
23
+ /** 是否启用 PSKReporter 上报 */
24
+ enabled: z.boolean().default(false),
25
+
26
+ /** 接收电台呼号 (可选,留空时使用第一个操作员的呼号) */
27
+ receiverCallsign: z.string().default(''),
28
+
29
+ /** 接收电台网格 (可选,留空时使用第一个操作员的网格) */
30
+ receiverLocator: z.string().default(''),
31
+
32
+ /** 解码软件名称 (自动填充) */
33
+ decodingSoftware: z.string().default('TX-5DR'),
34
+
35
+ /** 天线信息 (可选,会显示在 PSKReporter 地图上) */
36
+ antennaInformation: z.string().max(64, '天线信息不能超过64字符').default(''),
37
+
38
+ /** 上报间隔(秒),最小10秒,最大60秒,默认30秒 */
39
+ reportIntervalSeconds: z.number().min(10).max(60).default(30),
40
+
41
+ /** 是否使用测试服务器(仅用于开发调试) */
42
+ useTestServer: z.boolean().default(false),
43
+
44
+ /** 上报统计 */
45
+ stats: PSKReporterStatsSchema.default({}),
46
+ });
47
+
48
+ /**
49
+ * PSKReporter 上报记录 Schema (用于收集待上报数据)
50
+ */
51
+ export const PSKReporterSpotSchema = z.object({
52
+ /** 发送电台呼号 (必填) */
53
+ senderCallsign: z.string(),
54
+
55
+ /** 频率 (Hz,可选) */
56
+ frequency: z.number().optional(),
57
+
58
+ /** 模式 (FT8, FT4 等) */
59
+ mode: z.string(),
60
+
61
+ /** 信噪比 (可选) */
62
+ snr: z.number().optional(),
63
+
64
+ /** 接收时间戳 (Unix 秒) */
65
+ flowStartSeconds: z.number(),
66
+
67
+ /** 发送电台网格 (可选,从消息中解析) */
68
+ senderLocator: z.string().optional(),
69
+
70
+ /** 信息源 (1 = automatic) */
71
+ informationSource: z.number().default(1),
72
+ });
73
+
74
+ /**
75
+ * PSKReporter 上报状态 Schema
76
+ */
77
+ export const PSKReporterStatusSchema = z.object({
78
+ /** 是否已启用 */
79
+ enabled: z.boolean(),
80
+ /** 是否配置有效 */
81
+ configValid: z.boolean(),
82
+ /** 当前使用的呼号 */
83
+ activeCallsign: z.string().optional(),
84
+ /** 当前使用的网格 */
85
+ activeLocator: z.string().optional(),
86
+ /** 待上报记录数 */
87
+ pendingSpots: z.number(),
88
+ /** 最后上报时间 (Unix毫秒) */
89
+ lastReportTime: z.number().optional(),
90
+ /** 距离下次上报秒数 */
91
+ nextReportIn: z.number().optional(),
92
+ /** 是否正在上报 */
93
+ isReporting: z.boolean(),
94
+ /** 最后错误 */
95
+ lastError: z.string().optional(),
96
+ });
97
+
98
+ // 类型导出
99
+ export type PSKReporterStats = z.infer<typeof PSKReporterStatsSchema>;
100
+ export type PSKReporterConfig = z.infer<typeof PSKReporterConfigSchema>;
101
+ export type PSKReporterSpot = z.infer<typeof PSKReporterSpotSchema>;
102
+ export type PSKReporterStatus = z.infer<typeof PSKReporterStatusSchema>;