@tx5dr/contracts 1.7.12 → 2.5.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 (255) hide show
  1. package/dist/index.d.ts +7 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +6 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/schema/__tests__/android-operator-audio.schema.test.d.ts +2 -0
  6. package/dist/schema/__tests__/android-operator-audio.schema.test.d.ts.map +1 -0
  7. package/dist/schema/__tests__/android-operator-audio.schema.test.js +27 -0
  8. package/dist/schema/__tests__/android-operator-audio.schema.test.js.map +1 -0
  9. package/dist/schema/__tests__/assisted-queue.schema.test.d.ts +2 -0
  10. package/dist/schema/__tests__/assisted-queue.schema.test.d.ts.map +1 -0
  11. package/dist/schema/__tests__/assisted-queue.schema.test.js +114 -0
  12. package/dist/schema/__tests__/assisted-queue.schema.test.js.map +1 -0
  13. package/dist/schema/__tests__/audio-sidecar.schema.test.d.ts +2 -0
  14. package/dist/schema/__tests__/audio-sidecar.schema.test.d.ts.map +1 -0
  15. package/dist/schema/__tests__/audio-sidecar.schema.test.js +42 -0
  16. package/dist/schema/__tests__/audio-sidecar.schema.test.js.map +1 -0
  17. package/dist/schema/__tests__/audio.schema.test.js +92 -1
  18. package/dist/schema/__tests__/audio.schema.test.js.map +1 -1
  19. package/dist/schema/__tests__/diagnostics.schema.test.d.ts +2 -0
  20. package/dist/schema/__tests__/diagnostics.schema.test.d.ts.map +1 -0
  21. package/dist/schema/__tests__/diagnostics.schema.test.js +31 -0
  22. package/dist/schema/__tests__/diagnostics.schema.test.js.map +1 -0
  23. package/dist/schema/__tests__/logbook-health.schema.test.d.ts +2 -0
  24. package/dist/schema/__tests__/logbook-health.schema.test.d.ts.map +1 -0
  25. package/dist/schema/__tests__/logbook-health.schema.test.js +104 -0
  26. package/dist/schema/__tests__/logbook-health.schema.test.js.map +1 -0
  27. package/dist/schema/__tests__/meter.schema.test.d.ts +2 -0
  28. package/dist/schema/__tests__/meter.schema.test.d.ts.map +1 -0
  29. package/dist/schema/__tests__/meter.schema.test.js +27 -0
  30. package/dist/schema/__tests__/meter.schema.test.js.map +1 -0
  31. package/dist/schema/__tests__/observability.schema.test.d.ts +2 -0
  32. package/dist/schema/__tests__/observability.schema.test.d.ts.map +1 -0
  33. package/dist/schema/__tests__/observability.schema.test.js +12 -0
  34. package/dist/schema/__tests__/observability.schema.test.js.map +1 -0
  35. package/dist/schema/__tests__/operator-context-report.schema.test.d.ts +2 -0
  36. package/dist/schema/__tests__/operator-context-report.schema.test.d.ts.map +1 -0
  37. package/dist/schema/__tests__/operator-context-report.schema.test.js +70 -0
  38. package/dist/schema/__tests__/operator-context-report.schema.test.js.map +1 -0
  39. package/dist/schema/__tests__/operator.schema.test.js +9 -3
  40. package/dist/schema/__tests__/operator.schema.test.js.map +1 -1
  41. package/dist/schema/__tests__/plugin-market.schema.test.js +149 -3
  42. package/dist/schema/__tests__/plugin-market.schema.test.js.map +1 -1
  43. package/dist/schema/__tests__/qso.schema.test.d.ts +2 -0
  44. package/dist/schema/__tests__/qso.schema.test.d.ts.map +1 -0
  45. package/dist/schema/__tests__/qso.schema.test.js +70 -0
  46. package/dist/schema/__tests__/qso.schema.test.js.map +1 -0
  47. package/dist/schema/__tests__/radio-power-support.test.js +15 -0
  48. package/dist/schema/__tests__/radio-power-support.test.js.map +1 -1
  49. package/dist/schema/__tests__/radio-profile.schema.test.d.ts +2 -0
  50. package/dist/schema/__tests__/radio-profile.schema.test.d.ts.map +1 -0
  51. package/dist/schema/__tests__/radio-profile.schema.test.js +36 -0
  52. package/dist/schema/__tests__/radio-profile.schema.test.js.map +1 -0
  53. package/dist/schema/__tests__/radio.schema.test.js +92 -1
  54. package/dist/schema/__tests__/radio.schema.test.js.map +1 -1
  55. package/dist/schema/__tests__/realtime-settings.schema.test.js +2 -1
  56. package/dist/schema/__tests__/realtime-settings.schema.test.js.map +1 -1
  57. package/dist/schema/__tests__/remote-access.schema.test.d.ts +2 -0
  58. package/dist/schema/__tests__/remote-access.schema.test.d.ts.map +1 -0
  59. package/dist/schema/__tests__/remote-access.schema.test.js +18 -0
  60. package/dist/schema/__tests__/remote-access.schema.test.js.map +1 -0
  61. package/dist/schema/__tests__/spectrum.schema.test.d.ts +2 -0
  62. package/dist/schema/__tests__/spectrum.schema.test.d.ts.map +1 -0
  63. package/dist/schema/__tests__/spectrum.schema.test.js +107 -0
  64. package/dist/schema/__tests__/spectrum.schema.test.js.map +1 -0
  65. package/dist/schema/__tests__/strategy-transmission.schema.test.d.ts +2 -0
  66. package/dist/schema/__tests__/strategy-transmission.schema.test.d.ts.map +1 -0
  67. package/dist/schema/__tests__/strategy-transmission.schema.test.js +95 -0
  68. package/dist/schema/__tests__/strategy-transmission.schema.test.js.map +1 -0
  69. package/dist/schema/android-operator-audio.schema.d.ts +174 -0
  70. package/dist/schema/android-operator-audio.schema.d.ts.map +1 -0
  71. package/dist/schema/android-operator-audio.schema.js +38 -0
  72. package/dist/schema/android-operator-audio.schema.js.map +1 -0
  73. package/dist/schema/api-response.schema.d.ts +503 -44
  74. package/dist/schema/api-response.schema.d.ts.map +1 -1
  75. package/dist/schema/api-response.schema.js +9 -0
  76. package/dist/schema/api-response.schema.js.map +1 -1
  77. package/dist/schema/audio-sidecar.schema.d.ts +75 -0
  78. package/dist/schema/audio-sidecar.schema.d.ts.map +1 -1
  79. package/dist/schema/audio-sidecar.schema.js +32 -0
  80. package/dist/schema/audio-sidecar.schema.js.map +1 -1
  81. package/dist/schema/audio.schema.d.ts +4749 -3
  82. package/dist/schema/audio.schema.d.ts.map +1 -1
  83. package/dist/schema/audio.schema.js +101 -2
  84. package/dist/schema/audio.schema.js.map +1 -1
  85. package/dist/schema/auth.schema.d.ts +173 -14
  86. package/dist/schema/auth.schema.d.ts.map +1 -1
  87. package/dist/schema/auth.schema.js +65 -1
  88. package/dist/schema/auth.schema.js.map +1 -1
  89. package/dist/schema/cpu-profile.schema.d.ts +2 -2
  90. package/dist/schema/cw-decoder.schema.d.ts +74 -74
  91. package/dist/schema/cw-keyer.schema.d.ts +20 -14
  92. package/dist/schema/cw-keyer.schema.d.ts.map +1 -1
  93. package/dist/schema/cw-keyer.schema.js +3 -0
  94. package/dist/schema/cw-keyer.schema.js.map +1 -1
  95. package/dist/schema/desktop-https.schema.d.ts +6 -6
  96. package/dist/schema/device-ui.schema.d.ts +16 -16
  97. package/dist/schema/diagnostics.schema.d.ts +163 -0
  98. package/dist/schema/diagnostics.schema.d.ts.map +1 -0
  99. package/dist/schema/diagnostics.schema.js +46 -0
  100. package/dist/schema/diagnostics.schema.js.map +1 -0
  101. package/dist/schema/ft8.schema.d.ts +12 -2
  102. package/dist/schema/ft8.schema.d.ts.map +1 -1
  103. package/dist/schema/ft8.schema.js +8 -0
  104. package/dist/schema/ft8.schema.js.map +1 -1
  105. package/dist/schema/image-radio.schema.d.ts +2597 -0
  106. package/dist/schema/image-radio.schema.d.ts.map +1 -0
  107. package/dist/schema/image-radio.schema.js +283 -0
  108. package/dist/schema/image-radio.schema.js.map +1 -0
  109. package/dist/schema/logbook.schema.d.ts +2128 -18
  110. package/dist/schema/logbook.schema.d.ts.map +1 -1
  111. package/dist/schema/logbook.schema.js +159 -1
  112. package/dist/schema/logbook.schema.js.map +1 -1
  113. package/dist/schema/mode.schema.d.ts +4 -6
  114. package/dist/schema/mode.schema.d.ts.map +1 -1
  115. package/dist/schema/mode.schema.js +18 -6
  116. package/dist/schema/mode.schema.js.map +1 -1
  117. package/dist/schema/observability.schema.d.ts +66 -0
  118. package/dist/schema/observability.schema.d.ts.map +1 -0
  119. package/dist/schema/observability.schema.js +20 -0
  120. package/dist/schema/observability.schema.js.map +1 -0
  121. package/dist/schema/openwebrx.schema.d.ts +2 -2
  122. package/dist/schema/operator.schema.d.ts +2682 -6
  123. package/dist/schema/operator.schema.d.ts.map +1 -1
  124. package/dist/schema/operator.schema.js +10 -20
  125. package/dist/schema/operator.schema.js.map +1 -1
  126. package/dist/schema/plugin.schema.d.ts +1977 -492
  127. package/dist/schema/plugin.schema.d.ts.map +1 -1
  128. package/dist/schema/plugin.schema.js +165 -14
  129. package/dist/schema/plugin.schema.js.map +1 -1
  130. package/dist/schema/process-monitor.schema.d.ts +132 -132
  131. package/dist/schema/pskreporter.schema.d.ts +1 -3
  132. package/dist/schema/pskreporter.schema.d.ts.map +1 -1
  133. package/dist/schema/pskreporter.schema.js +1 -3
  134. package/dist/schema/pskreporter.schema.js.map +1 -1
  135. package/dist/schema/qso.schema.d.ts +246 -1
  136. package/dist/schema/qso.schema.d.ts.map +1 -1
  137. package/dist/schema/qso.schema.js +68 -0
  138. package/dist/schema/qso.schema.js.map +1 -1
  139. package/dist/schema/radio-capability.schema.d.ts +31 -40
  140. package/dist/schema/radio-capability.schema.d.ts.map +1 -1
  141. package/dist/schema/radio-capability.schema.js +8 -15
  142. package/dist/schema/radio-capability.schema.js.map +1 -1
  143. package/dist/schema/radio-power-support.d.ts.map +1 -1
  144. package/dist/schema/radio-power-support.js +3 -0
  145. package/dist/schema/radio-power-support.js.map +1 -1
  146. package/dist/schema/radio-power.schema.d.ts +102 -13
  147. package/dist/schema/radio-power.schema.d.ts.map +1 -1
  148. package/dist/schema/radio-power.schema.js +15 -8
  149. package/dist/schema/radio-power.schema.js.map +1 -1
  150. package/dist/schema/radio-profile.schema.d.ts +1491 -97
  151. package/dist/schema/radio-profile.schema.d.ts.map +1 -1
  152. package/dist/schema/radio-profile.schema.js +7 -0
  153. package/dist/schema/radio-profile.schema.js.map +1 -1
  154. package/dist/schema/radio.schema.d.ts +442 -48
  155. package/dist/schema/radio.schema.d.ts.map +1 -1
  156. package/dist/schema/radio.schema.js +75 -7
  157. package/dist/schema/radio.schema.js.map +1 -1
  158. package/dist/schema/realtime.schema.d.ts +60 -52
  159. package/dist/schema/realtime.schema.d.ts.map +1 -1
  160. package/dist/schema/realtime.schema.js +9 -1
  161. package/dist/schema/realtime.schema.js.map +1 -1
  162. package/dist/schema/rigctld.schema.d.ts +6 -6
  163. package/dist/schema/slot-info.schema.d.ts +28 -9
  164. package/dist/schema/slot-info.schema.d.ts.map +1 -1
  165. package/dist/schema/slot-info.schema.js +17 -10
  166. package/dist/schema/slot-info.schema.js.map +1 -1
  167. package/dist/schema/spectrum-config.schema.d.ts +638 -0
  168. package/dist/schema/spectrum-config.schema.d.ts.map +1 -0
  169. package/dist/schema/spectrum-config.schema.js +129 -0
  170. package/dist/schema/spectrum-config.schema.js.map +1 -0
  171. package/dist/schema/spectrum.schema.d.ts +1323 -30
  172. package/dist/schema/spectrum.schema.d.ts.map +1 -1
  173. package/dist/schema/spectrum.schema.js +72 -0
  174. package/dist/schema/spectrum.schema.js.map +1 -1
  175. package/dist/schema/station-info.schema.d.ts +5 -0
  176. package/dist/schema/station-info.schema.d.ts.map +1 -1
  177. package/dist/schema/station-info.schema.js +5 -0
  178. package/dist/schema/station-info.schema.js.map +1 -1
  179. package/dist/schema/system.schema.d.ts +56 -6
  180. package/dist/schema/system.schema.d.ts.map +1 -1
  181. package/dist/schema/system.schema.js +22 -0
  182. package/dist/schema/system.schema.js.map +1 -1
  183. package/dist/schema/transmission.schema.d.ts +8 -7
  184. package/dist/schema/transmission.schema.d.ts.map +1 -1
  185. package/dist/schema/transmission.schema.js +2 -7
  186. package/dist/schema/transmission.schema.js.map +1 -1
  187. package/dist/schema/tx-audio-input.schema.d.ts +8 -0
  188. package/dist/schema/tx-audio-input.schema.d.ts.map +1 -0
  189. package/dist/schema/tx-audio-input.schema.js +19 -0
  190. package/dist/schema/tx-audio-input.schema.js.map +1 -0
  191. package/dist/schema/voice-keyer.schema.d.ts +16 -16
  192. package/dist/schema/voice.schema.d.ts +1 -1
  193. package/dist/schema/voice.schema.d.ts.map +1 -1
  194. package/dist/schema/voice.schema.js +1 -1
  195. package/dist/schema/voice.schema.js.map +1 -1
  196. package/dist/schema/websocket.schema.d.ts +35811 -7829
  197. package/dist/schema/websocket.schema.d.ts.map +1 -1
  198. package/dist/schema/websocket.schema.js +453 -9
  199. package/dist/schema/websocket.schema.js.map +1 -1
  200. package/package.json +6 -1
  201. package/src/index.ts +7 -0
  202. package/src/schema/__tests__/android-operator-audio.schema.test.ts +31 -0
  203. package/src/schema/__tests__/assisted-queue.schema.test.ts +122 -0
  204. package/src/schema/__tests__/audio-sidecar.schema.test.ts +48 -0
  205. package/src/schema/__tests__/audio.schema.test.ts +102 -0
  206. package/src/schema/__tests__/diagnostics.schema.test.ts +32 -0
  207. package/src/schema/__tests__/logbook-health.schema.test.ts +117 -0
  208. package/src/schema/__tests__/meter.schema.test.ts +28 -0
  209. package/src/schema/__tests__/observability.schema.test.ts +15 -0
  210. package/src/schema/__tests__/operator-context-report.schema.test.ts +77 -0
  211. package/src/schema/__tests__/operator.schema.test.ts +11 -3
  212. package/src/schema/__tests__/plugin-market.schema.test.ts +165 -2
  213. package/src/schema/__tests__/qso.schema.test.ts +91 -0
  214. package/src/schema/__tests__/radio-power-support.test.ts +17 -0
  215. package/src/schema/__tests__/radio-profile.schema.test.ts +44 -0
  216. package/src/schema/__tests__/radio.schema.test.ts +108 -1
  217. package/src/schema/__tests__/realtime-settings.schema.test.ts +2 -1
  218. package/src/schema/__tests__/remote-access.schema.test.ts +22 -0
  219. package/src/schema/__tests__/spectrum.schema.test.ts +119 -0
  220. package/src/schema/__tests__/strategy-transmission.schema.test.ts +106 -0
  221. package/src/schema/android-operator-audio.schema.ts +49 -0
  222. package/src/schema/api-response.schema.ts +11 -0
  223. package/src/schema/audio-sidecar.schema.ts +35 -0
  224. package/src/schema/audio.schema.ts +115 -2
  225. package/src/schema/auth.schema.ts +79 -1
  226. package/src/schema/cw-keyer.schema.ts +5 -0
  227. package/src/schema/diagnostics.schema.ts +55 -0
  228. package/src/schema/ft8.schema.ts +8 -0
  229. package/src/schema/image-radio.schema.ts +342 -0
  230. package/src/schema/logbook.schema.ts +205 -1
  231. package/src/schema/mode.schema.ts +18 -6
  232. package/src/schema/observability.schema.ts +26 -0
  233. package/src/schema/operator.schema.ts +11 -20
  234. package/src/schema/plugin.schema.ts +186 -15
  235. package/src/schema/pskreporter.schema.ts +1 -3
  236. package/src/schema/qso.schema.ts +78 -0
  237. package/src/schema/radio-capability.schema.ts +8 -15
  238. package/src/schema/radio-power-support.ts +3 -0
  239. package/src/schema/radio-power.schema.ts +15 -8
  240. package/src/schema/radio-profile.schema.ts +7 -0
  241. package/src/schema/radio.schema.ts +90 -7
  242. package/src/schema/realtime.schema.ts +9 -1
  243. package/src/schema/slot-info.schema.ts +18 -11
  244. package/src/schema/spectrum-config.schema.ts +156 -0
  245. package/src/schema/spectrum.schema.ts +89 -0
  246. package/src/schema/station-info.schema.ts +5 -0
  247. package/src/schema/system.schema.ts +29 -0
  248. package/src/schema/transmission.schema.ts +2 -7
  249. package/src/schema/tx-audio-input.schema.ts +22 -0
  250. package/src/schema/voice.schema.ts +1 -1
  251. package/src/schema/websocket.schema.ts +601 -13
  252. package/test/image-radio-schema.test.ts +107 -0
  253. package/test/plugin-slot-schema.test.ts +31 -0
  254. package/test/radio-capability-schema.test.ts +44 -0
  255. package/CLAUDE.md +0 -41
@@ -1,5 +1,69 @@
1
1
  import { z } from 'zod';
2
2
 
3
+ export const AudioDeviceBackendSchema = z.enum([
4
+ 'rtaudio',
5
+ 'android',
6
+ 'icom-wlan',
7
+ 'openwebrx',
8
+ 'tci',
9
+ 'network',
10
+ ]);
11
+
12
+ export const AudioDeviceKindSchema = z.enum([
13
+ 'usb',
14
+ 'wired-headset',
15
+ 'wired-headphones',
16
+ 'analog-line',
17
+ 'builtin-mic',
18
+ 'builtin-speaker',
19
+ 'network',
20
+ 'virtual',
21
+ 'unknown',
22
+ ]);
23
+
24
+ export const AudioDeviceTransportSchema = z.enum([
25
+ 'usb',
26
+ 'analog',
27
+ 'builtin',
28
+ 'network',
29
+ 'virtual',
30
+ 'unknown',
31
+ ]);
32
+
33
+ export const AudioDeviceConnectorSchema = z.enum([
34
+ 'usb',
35
+ '3.5mm',
36
+ 'builtin',
37
+ 'network',
38
+ 'virtual',
39
+ 'unknown',
40
+ ]);
41
+
42
+ export const AudioDeviceRouteStateSchema = z.enum([
43
+ 'idle',
44
+ 'opening',
45
+ 'verified',
46
+ 'lost',
47
+ 'unavailable',
48
+ 'failed',
49
+ ]);
50
+
51
+ export const AudioDeviceCapabilitiesSchema = z.object({
52
+ sampleRates: z.array(z.number().int().positive()).optional(),
53
+ bufferSizes: z.array(z.number().int().positive()).optional(),
54
+ channelCounts: z.array(z.number().int().positive()).optional(),
55
+ sampleFormats: z.array(z.enum(['float32', 'int16'])).optional(),
56
+ channelModes: z.array(z.enum(['mono', 'left', 'right', 'both'])).optional(),
57
+ sampleRateConfigurable: z.boolean().optional(),
58
+ bufferSizeConfigurable: z.boolean().optional(),
59
+ sampleFormatConfigurable: z.boolean().optional(),
60
+ channelModeConfigurable: z.boolean().optional(),
61
+ outputRouteAck: z.boolean().optional(),
62
+ fixedSampleRate: z.number().int().positive().optional(),
63
+ fixedBufferSize: z.number().int().positive().optional(),
64
+ fixedChannelCount: z.number().int().positive().optional(),
65
+ });
66
+
3
67
  // 音频设备信息
4
68
  export const AudioDeviceSchema = z.object({
5
69
  id: z.string(),
@@ -12,6 +76,26 @@ export const AudioDeviceSchema = z.object({
12
76
  availability: z.enum(['available', 'cached', 'active']).optional(),
13
77
  isActiveByTx5dr: z.boolean().optional(),
14
78
  lastSeenAt: z.number().int().positive().optional(),
79
+ backend: AudioDeviceBackendSchema.optional(),
80
+ kind: AudioDeviceKindSchema.optional(),
81
+ routeKey: z.string().min(1).optional(),
82
+ transport: AudioDeviceTransportSchema.optional(),
83
+ connector: AudioDeviceConnectorSchema.optional(),
84
+ clientConnected: z.boolean().optional(),
85
+ routeVerified: z.boolean().optional(),
86
+ routeState: AudioDeviceRouteStateSchema.optional(),
87
+ failureReason: z.string().min(1).optional(),
88
+ capabilities: AudioDeviceCapabilitiesSchema.optional(),
89
+ /** Human-readable secondary label (radio identity · VID:PID · USB path). */
90
+ detail: z.string().optional(),
91
+ /** Stable hardware identity when available (e.g. usb:1-7.2.4). */
92
+ hardwareId: z.string().optional(),
93
+ vendorId: z.string().optional(),
94
+ productId: z.string().optional(),
95
+ serialNumber: z.string().optional(),
96
+ usbPath: z.string().optional(),
97
+ alsaCard: z.number().int().nonnegative().optional(),
98
+ alsaCardId: z.string().optional(),
15
99
  });
16
100
 
17
101
  export const AudioDeviceResolutionStatusSchema = z.enum([
@@ -23,6 +107,9 @@ export const AudioDeviceResolutionStatusSchema = z.enum([
23
107
 
24
108
  export const AudioDeviceResolutionSchema = z.object({
25
109
  configuredDeviceName: z.string().nullable(),
110
+ configuredRouteKey: z.string().nullable().optional(),
111
+ configuredDeviceId: z.string().nullable().optional(),
112
+ configuredHardwareId: z.string().nullable().optional(),
26
113
  configuredDevice: AudioDeviceSchema.nullable(),
27
114
  effectiveDevice: AudioDeviceSchema.nullable(),
28
115
  status: AudioDeviceResolutionStatusSchema,
@@ -37,6 +124,13 @@ export const AudioDeviceResolutionSetSchema = z.object({
37
124
  export const AudioOutputSampleFormatSchema = z.enum(['float32', 'int16']);
38
125
  export const AudioOutputChannelModeSchema = z.enum(['mono', 'left', 'right', 'both']);
39
126
 
127
+ /**
128
+ * How USB/ACC capture PCM should be interpreted before the internal 12 kHz AF ring.
129
+ * - af: already-demodulated SSB baseband (default)
130
+ * - icom-12k-if: Icom ACC/USB Output Select=IF (~12 kHz IF); software SSB demod required
131
+ */
132
+ export const AudioInputSignalTypeSchema = z.enum(['af', 'icom-12k-if']);
133
+
40
134
  // 音频设备列表响应
41
135
  export const AudioDevicesResponseSchema = z.object({
42
136
  inputDevices: z.array(AudioDeviceSchema),
@@ -48,13 +142,25 @@ export const AudioDevicesResponseSchema = z.object({
48
142
  // 音频设备设置请求
49
143
  export const AudioDeviceSettingsSchema = z.object({
50
144
  inputDeviceName: z.string().optional(), // 使用设备名称而非ID
51
- outputDeviceName: z.string().optional(), // 使用设备名称而非ID
145
+ outputDeviceName: z.string().optional(), // 使用设备名称而非ID
146
+ inputRouteKey: z.string().min(1).nullable().optional(),
147
+ outputRouteKey: z.string().min(1).nullable().optional(),
148
+ /** Runtime device id (e.g. input-130). Preferred over name when unique. */
149
+ inputDeviceId: z.string().optional(),
150
+ outputDeviceId: z.string().optional(),
151
+ /** Stable hardware id when available (e.g. usb:1-7.2.4). Preferred over runtime id. */
152
+ inputHardwareId: z.string().optional(),
153
+ outputHardwareId: z.string().optional(),
52
154
  inputSampleRate: z.number().optional(),
53
155
  outputSampleRate: z.number().optional(),
54
156
  inputBufferSize: z.number().optional(),
55
157
  outputBufferSize: z.number().optional(),
56
158
  outputSampleFormat: AudioOutputSampleFormatSchema.optional(),
57
159
  outputChannelMode: AudioOutputChannelModeSchema.optional(),
160
+ /** Capture signal interpretation. Defaults to demodulated AF baseband. */
161
+ inputSignalType: AudioInputSignalTypeSchema.optional(),
162
+ /** IF center frequency in Hz when inputSignalType is icom-12k-if (default 12000). */
163
+ ifCenterHz: z.number().positive().max(24000).optional(),
58
164
  sampleRate: z.number().optional(),
59
165
  bufferSize: z.number().optional(),
60
166
  });
@@ -69,7 +175,7 @@ export const AudioDeviceSettingsResponseSchema = z.object({
69
175
 
70
176
  export const AudioSettingsResolveRequestSchema = z.object({
71
177
  audio: AudioDeviceSettingsSchema,
72
- radioType: z.enum(['none', 'network', 'serial', 'icom-wlan']).optional(),
178
+ radioType: z.enum(['none', 'network', 'serial', 'icom-wlan', 'tci']).optional(),
73
179
  });
74
180
 
75
181
  export const AudioSettingsResolveResponseSchema = z.object({
@@ -110,11 +216,18 @@ export const VolumeGainSchema = z.object({
110
216
 
111
217
  // 导出类型
112
218
  export type AudioDevice = z.infer<typeof AudioDeviceSchema>;
219
+ export type AudioDeviceBackend = z.infer<typeof AudioDeviceBackendSchema>;
220
+ export type AudioDeviceKind = z.infer<typeof AudioDeviceKindSchema>;
221
+ export type AudioDeviceTransport = z.infer<typeof AudioDeviceTransportSchema>;
222
+ export type AudioDeviceConnector = z.infer<typeof AudioDeviceConnectorSchema>;
223
+ export type AudioDeviceRouteState = z.infer<typeof AudioDeviceRouteStateSchema>;
224
+ export type AudioDeviceCapabilities = z.infer<typeof AudioDeviceCapabilitiesSchema>;
113
225
  export type AudioDeviceResolutionStatus = z.infer<typeof AudioDeviceResolutionStatusSchema>;
114
226
  export type AudioDeviceResolution = z.infer<typeof AudioDeviceResolutionSchema>;
115
227
  export type AudioDeviceResolutionSet = z.infer<typeof AudioDeviceResolutionSetSchema>;
116
228
  export type AudioOutputSampleFormat = z.infer<typeof AudioOutputSampleFormatSchema>;
117
229
  export type AudioOutputChannelMode = z.infer<typeof AudioOutputChannelModeSchema>;
230
+ export type AudioInputSignalType = z.infer<typeof AudioInputSignalTypeSchema>;
118
231
  export type AudioDevicesResponse = z.infer<typeof AudioDevicesResponseSchema>;
119
232
  export type AudioDeviceSettings = z.infer<typeof AudioDeviceSettingsSchema>;
120
233
  export type AudioDeviceSettingsResponse = z.infer<typeof AudioDeviceSettingsResponseSchema>;
@@ -82,6 +82,19 @@ export const LoginResponseSchema = z.object({
82
82
 
83
83
  export type LoginResponse = z.infer<typeof LoginResponseSchema>;
84
84
 
85
+ export const BrowserLoginCodeResponseSchema = z.object({
86
+ code: z.string().min(1),
87
+ expiresAt: z.number(),
88
+ });
89
+
90
+ export type BrowserLoginCodeResponse = z.infer<typeof BrowserLoginCodeResponseSchema>;
91
+
92
+ export const ExchangeBrowserLoginCodeRequestSchema = z.object({
93
+ code: z.string().min(1),
94
+ });
95
+
96
+ export type ExchangeBrowserLoginCodeRequest = z.infer<typeof ExchangeBrowserLoginCodeRequestSchema>;
97
+
85
98
  // ===== Token 管理(Admin API) =====
86
99
 
87
100
  export const LoginCredentialSummarySchema = z.object({
@@ -211,11 +224,76 @@ export const UpdateAuthConfigRequestSchema = z.object({
211
224
 
212
225
  export type UpdateAuthConfigRequest = z.infer<typeof UpdateAuthConfigRequestSchema>;
213
226
 
227
+ export const RemoteAccessPresetSchema = z.enum(['local', 'lan', 'public']);
228
+ export type RemoteAccessPreset = z.infer<typeof RemoteAccessPresetSchema>;
229
+
230
+ export function isPrivateRemoteAccessHostname(hostname: string): boolean {
231
+ if (['localhost', '127.0.0.1', '::1'].includes(hostname)) return true;
232
+ if (hostname.includes(':')) {
233
+ const normalized = hostname.toLowerCase();
234
+ return normalized.startsWith('fc') || normalized.startsWith('fd') || normalized.startsWith('fe8')
235
+ || normalized.startsWith('fe9') || normalized.startsWith('fea') || normalized.startsWith('feb');
236
+ }
237
+ const parts = hostname.split('.').map(Number);
238
+ if (parts.length !== 4 || parts.some(part => !Number.isInteger(part) || part < 0 || part > 255)) return false;
239
+ return parts[0] === 10
240
+ || (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31)
241
+ || (parts[0] === 192 && parts[1] === 168)
242
+ || (parts[0] === 100 && parts[1] >= 64 && parts[1] <= 127)
243
+ || (parts[0] === 169 && parts[1] === 254);
244
+ }
245
+
246
+ export function normalizeRemoteAccessOrigin(value: string): string | null {
247
+ try {
248
+ const parsed = new URL(value.trim());
249
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return null;
250
+ if (parsed.username || parsed.password || parsed.pathname !== '/' || parsed.search || parsed.hash) return null;
251
+ return parsed.origin;
252
+ } catch {
253
+ return null;
254
+ }
255
+ }
256
+
257
+ export function isSecureRemoteAccessOrigin(value: string): boolean {
258
+ const normalized = normalizeRemoteAccessOrigin(value);
259
+ if (!normalized) return false;
260
+ const parsed = new URL(normalized);
261
+ return parsed.protocol === 'https:' || isPrivateRemoteAccessHostname(parsed.hostname);
262
+ }
263
+
264
+ export const RemoteAccessSecurityConfigSchema = z.object({
265
+ preset: RemoteAccessPresetSchema.default('lan'),
266
+ allowedOrigins: z.array(z.string().url().refine(
267
+ value => normalizeRemoteAccessOrigin(value) !== null,
268
+ 'Expected a complete HTTP(S) origin without a path',
269
+ )).max(32).default([]),
270
+ maxConnections: z.number().int().min(1).max(512).default(32),
271
+ maxConnectionsPerIp: z.number().int().min(1).max(128).default(16),
272
+ maxPendingAuth: z.number().int().min(1).max(128).default(32),
273
+ authTimeoutMs: z.number().int().min(3_000).max(60_000).default(10_000),
274
+ handshakeTimeoutMs: z.number().int().min(3_000).max(60_000).default(10_000),
275
+ });
276
+ export type RemoteAccessSecurityConfig = z.infer<typeof RemoteAccessSecurityConfigSchema>;
277
+
278
+ export const UpdateRemoteAccessSecurityRequestSchema = RemoteAccessSecurityConfigSchema.partial().extend({
279
+ allowPublicViewing: z.boolean().optional(),
280
+ });
281
+ export type UpdateRemoteAccessSecurityRequest = z.infer<typeof UpdateRemoteAccessSecurityRequestSchema>;
282
+
283
+ export const RemoteAccessSecurityStatusSchema = RemoteAccessSecurityConfigSchema.extend({
284
+ allowPublicViewing: z.boolean(),
285
+ activeConnections: z.number().int().nonnegative(),
286
+ pendingConnections: z.number().int().nonnegative(),
287
+ });
288
+ export type RemoteAccessSecurityStatus = z.infer<typeof RemoteAccessSecurityStatusSchema>;
289
+
214
290
  // ===== 认证配置(持久化到 auth.json) =====
215
291
 
216
292
  export const AuthConfigSchema = z.object({
217
293
  enabled: z.boolean().default(true),
218
- allowPublicViewing: z.boolean().default(true),
294
+ // Existing installations persist their explicit value; new installations opt in.
295
+ allowPublicViewing: z.boolean().default(false),
296
+ remoteAccess: RemoteAccessSecurityConfigSchema.default({}),
219
297
  jwtSecret: z.string().optional(),
220
298
  jwtExpiresInSeconds: z.number().default(7 * 24 * 3600), // 7 days
221
299
  tokens: z.array(AuthTokenSchema).default([]),
@@ -6,6 +6,9 @@ import { z } from 'zod';
6
6
  export const CWKeyerBackendSchema = z.enum(['cat', 'serial']);
7
7
  export type CWKeyerBackend = z.infer<typeof CWKeyerBackendSchema>;
8
8
 
9
+ export const CWKeyActiveLevelSchema = z.enum(['high', 'low']);
10
+ export type CWKeyActiveLevel = z.infer<typeof CWKeyActiveLevelSchema>;
11
+
9
12
  export const CWKeyerConfigSchema = z.object({
10
13
  /** CW 发报后端:cat=Hamlib 整报文,serial=DTR/RTS 时序键控 */
11
14
  backend: CWKeyerBackendSchema.default('cat'),
@@ -13,6 +16,8 @@ export const CWKeyerConfigSchema = z.object({
13
16
  keyPort: z.string(),
14
17
  /** CW 键控引脚类型 */
15
18
  keyMethod: z.enum(['dtr', 'rts']),
19
+ /** CW 键控有效电平:high=高电平按下,low=低电平按下 */
20
+ keyActiveLevel: CWKeyActiveLevelSchema.default('high'),
16
21
  /** 莫尔斯码速度 (WPM, 5-60) */
17
22
  wpm: z.number().int().min(5).max(60).default(20),
18
23
  });
@@ -0,0 +1,55 @@
1
+ import { z } from 'zod';
2
+
3
+ export const DiagnosticLogSourceIdSchema = z.enum(['server', 'electron-main', 'client-tools']);
4
+ export type DiagnosticLogSourceId = z.infer<typeof DiagnosticLogSourceIdSchema>;
5
+
6
+ export const DiagnosticLogSourceSchema = z.object({
7
+ id: DiagnosticLogSourceIdSchema,
8
+ fileName: z.string().min(1),
9
+ fileCount: z.number().int().positive(),
10
+ totalBytes: z.number().int().nonnegative(),
11
+ availableFromMs: z.number().int().positive().nullable(),
12
+ availableToMs: z.number().int().positive().nullable(),
13
+ });
14
+ export type DiagnosticLogSource = z.infer<typeof DiagnosticLogSourceSchema>;
15
+
16
+ export const DiagnosticLogSourcesResponseSchema = z.object({
17
+ sources: z.array(DiagnosticLogSourceSchema),
18
+ limits: z.object({
19
+ maxRangeMs: z.number().int().positive(),
20
+ maxUncompressedBytes: z.number().int().positive(),
21
+ maxCompressedBytes: z.number().int().positive(),
22
+ feedbackMaxCharacters: z.number().int().positive(),
23
+ }),
24
+ });
25
+ export type DiagnosticLogSourcesResponse = z.infer<typeof DiagnosticLogSourcesResponseSchema>;
26
+
27
+ export const CreateDiagnosticUploadRequestSchema = z.object({
28
+ sourceId: DiagnosticLogSourceIdSchema,
29
+ fromMs: z.number().int().positive(),
30
+ toMs: z.number().int().positive(),
31
+ feedback: z.string().trim().max(2000).optional(),
32
+ }).strict().superRefine((value, context) => {
33
+ if (value.toMs <= value.fromMs) {
34
+ context.addIssue({ code: z.ZodIssueCode.custom, path: ['toMs'], message: 'toMs must be later than fromMs' });
35
+ }
36
+ if (value.toMs - value.fromMs > 7 * 24 * 60 * 60 * 1000) {
37
+ context.addIssue({ code: z.ZodIssueCode.custom, path: ['toMs'], message: 'time range exceeds seven days' });
38
+ }
39
+ });
40
+ export type CreateDiagnosticUploadRequest = z.infer<typeof CreateDiagnosticUploadRequestSchema>;
41
+
42
+ export const DiagnosticUploadReceiptSchema = z.object({
43
+ uploadId: z.string().uuid(),
44
+ sourceId: DiagnosticLogSourceIdSchema,
45
+ requestedFromMs: z.number().int().positive(),
46
+ requestedToMs: z.number().int().positive(),
47
+ includedFromMs: z.number().int().positive(),
48
+ includedToMs: z.number().int().positive(),
49
+ lineCount: z.number().int().positive(),
50
+ uncompressedBytes: z.number().int().positive(),
51
+ compressedBytes: z.number().int().positive(),
52
+ acceptedAt: z.string().datetime(),
53
+ retainedUntil: z.string().datetime(),
54
+ });
55
+ export type DiagnosticUploadReceipt = z.infer<typeof DiagnosticUploadReceiptSchema>;
@@ -276,6 +276,9 @@ export const FT8MessageSchema = z.discriminatedUnion('type', [
276
276
  * This is the main plugin-facing FT8 payload. It combines RF metrics from the
277
277
  * decoder (`snr`, `dt`, `df`), the original unparsed text (`rawMessage`), the
278
278
  * normalized semantic form (`message`) and optional logbook enrichment.
279
+ * `snr` is dB, `dt` is seconds from the expected decode timing, `df` is audio
280
+ * offset in hertz, and `timestamp` is Unix epoch milliseconds. `slotId` ties
281
+ * the decode back to its `SlotInfo`/`SlotPack`.
279
282
  */
280
283
  export const ParsedFT8MessageSchema = z.object({
281
284
  snr: z.number(),
@@ -285,6 +288,11 @@ export const ParsedFT8MessageSchema = z.object({
285
288
  message: FT8MessageSchema,
286
289
  slotId: z.string(),
287
290
  timestamp: z.number(),
291
+ /**
292
+ * 标记该消息为 WSJT-X 部分解码(含 `<...>` 未解码呼号占位符)。
293
+ * 部分解码消息不得作为自动呼叫/回复目标,仅供监控与防御使用。
294
+ */
295
+ isPartialDecode: z.boolean().optional(),
288
296
  logbookAnalysis: LogbookAnalysisSchema.optional(),
289
297
  });
290
298