@tx5dr/plugin-api 1.7.12 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +274 -17
- package/dist/__tests__/adif.test.js +40 -0
- package/dist/__tests__/adif.test.js.map +1 -1
- package/dist/__tests__/capability-context.test.d.ts +2 -0
- package/dist/__tests__/capability-context.test.d.ts.map +1 -0
- package/dist/__tests__/capability-context.test.js +99 -0
- package/dist/__tests__/capability-context.test.js.map +1 -0
- package/dist/__tests__/testing-utils.test.js +217 -15
- package/dist/__tests__/testing-utils.test.js.map +1 -1
- package/dist/capabilities.d.ts +37 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +48 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/context.d.ts +95 -56
- package/dist/context.d.ts.map +1 -1
- package/dist/definition.d.ts +73 -20
- package/dist/definition.d.ts.map +1 -1
- package/dist/definition.js +8 -1
- package/dist/definition.js.map +1 -1
- package/dist/helpers.d.ts +322 -129
- package/dist/helpers.d.ts.map +1 -1
- package/dist/hooks.d.ts +54 -34
- package/dist/hooks.d.ts.map +1 -1
- package/dist/host-dependencies.d.ts +101 -0
- package/dist/host-dependencies.d.ts.map +1 -1
- package/dist/index.d.ts +9 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +164 -9
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +9 -1
- package/dist/runtime.js.map +1 -1
- package/dist/settings.d.ts +31 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/sync.d.ts +98 -6
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +6 -0
- package/dist/sync.js.map +1 -1
- package/dist/testing/index.d.ts +70 -12
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +307 -105
- package/dist/testing/index.js.map +1 -1
- package/dist/utils/adif.d.ts.map +1 -1
- package/dist/utils/adif.js +6 -2
- package/dist/utils/adif.js.map +1 -1
- package/dist/utils/qso-text-fields.d.ts +7 -1
- package/dist/utils/qso-text-fields.d.ts.map +1 -1
- package/dist/utils/qso-text-fields.js +74 -6
- package/dist/utils/qso-text-fields.js.map +1 -1
- package/package.json +4 -4
package/dist/helpers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ParsedFT8Message, SlotInfo, SlotPack,
|
|
1
|
+
import type { ParsedFT8Message, SlotInfo, SlotPack, FrameMessage, ModeDescriptor, EngineMode, PermissionGrant, PluginPanelDescriptor, CapabilityList, CapabilityState, RadioPowerResponse, RadioPowerStateEvent, RadioPowerSupportInfo, RadioPowerTarget } from '@tx5dr/contracts';
|
|
2
2
|
import type { StrategyRuntimeSnapshot } from './runtime.js';
|
|
3
3
|
/**
|
|
4
4
|
* Simple persistent key-value store exposed to plugins.
|
|
@@ -10,11 +10,17 @@ export interface KVStore {
|
|
|
10
10
|
/**
|
|
11
11
|
* Reads a stored value.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
13
|
+
* Stored values are returned by value, so mutating the result does not update
|
|
14
|
+
* persistence until {@link set} is called. When the key is missing, the
|
|
15
|
+
* caller-owned `defaultValue` is returned unchanged.
|
|
14
16
|
*/
|
|
15
17
|
get<T = unknown>(key: string, defaultValue?: T): T;
|
|
16
18
|
/**
|
|
17
|
-
* Persists a
|
|
19
|
+
* Persists a JSON-compatible snapshot under the given key.
|
|
20
|
+
*
|
|
21
|
+
* `undefined` follows JSON object semantics and removes the key. Cycles,
|
|
22
|
+
* BigInt values, functions and Host capabilities are rejected with
|
|
23
|
+
* `PLUGIN_DATA_NOT_SERIALIZABLE`.
|
|
18
24
|
*/
|
|
19
25
|
set(key: string, value: unknown): void;
|
|
20
26
|
/**
|
|
@@ -22,7 +28,7 @@ export interface KVStore {
|
|
|
22
28
|
*/
|
|
23
29
|
delete(key: string): void;
|
|
24
30
|
/**
|
|
25
|
-
* Returns
|
|
31
|
+
* Returns an independent snapshot of all stored entries in this scope.
|
|
26
32
|
*/
|
|
27
33
|
getAll(): Record<string, unknown>;
|
|
28
34
|
/**
|
|
@@ -70,40 +76,134 @@ export interface PluginTimers {
|
|
|
70
76
|
* Remote UDP endpoint metadata for datagrams received by plugin-owned sockets.
|
|
71
77
|
*/
|
|
72
78
|
export interface PluginUdpRemoteInfo {
|
|
79
|
+
/** Source IP address reported by the UDP socket. */
|
|
73
80
|
address: string;
|
|
81
|
+
/** Source UDP port. */
|
|
74
82
|
port: number;
|
|
83
|
+
/** Address family reported by Node.js, typically `IPv4` or `IPv6`. */
|
|
75
84
|
family: string;
|
|
85
|
+
/** Datagram size in bytes. */
|
|
76
86
|
size: number;
|
|
77
87
|
}
|
|
88
|
+
/** Local endpoint used when binding a plugin-owned UDP socket. */
|
|
78
89
|
export interface PluginUdpBindOptions {
|
|
90
|
+
/** Local interface/address. Omit to use the Host default. */
|
|
79
91
|
host?: string;
|
|
92
|
+
/** Local port. Omit or use `0` to let the operating system choose one. */
|
|
80
93
|
port?: number;
|
|
81
94
|
}
|
|
95
|
+
/** Options applied when the Host creates a plugin-owned UDP socket. */
|
|
82
96
|
export interface PluginUdpSocketOptions {
|
|
97
|
+
/** IP family. Defaults to `udp4`. */
|
|
83
98
|
type?: 'udp4' | 'udp6';
|
|
99
|
+
/** Whether multiple sockets may reuse the local address. */
|
|
84
100
|
reuseAddr?: boolean;
|
|
101
|
+
/** Whether the socket may send IPv4 broadcast datagrams. */
|
|
85
102
|
broadcast?: boolean;
|
|
103
|
+
/** Multicast time-to-live applied to outbound multicast packets. */
|
|
86
104
|
multicastTtl?: number;
|
|
87
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Host-owned UDP socket capability.
|
|
108
|
+
*
|
|
109
|
+
* The handle may be stored by the plugin, but its methods are invocation
|
|
110
|
+
* guarded. Close it during unload when possible; Host cleanup also closes all
|
|
111
|
+
* sockets owned by the plugin instance.
|
|
112
|
+
*/
|
|
88
113
|
export interface PluginUdpSocket {
|
|
114
|
+
/** Binds the socket and resolves when it is ready to receive datagrams. */
|
|
89
115
|
bind(options?: PluginUdpBindOptions): Promise<void>;
|
|
116
|
+
/** Sends one datagram to the exact remote host and port. */
|
|
90
117
|
send(data: Uint8Array | string, port: number, host: string): Promise<void>;
|
|
118
|
+
/** Registers the callback used for received datagrams. */
|
|
91
119
|
onMessage(handler: (data: Uint8Array, remote: PluginUdpRemoteInfo) => void | Promise<void>): void;
|
|
120
|
+
/** Registers the callback used for socket-level errors. */
|
|
92
121
|
onError(handler: (error: Error) => void): void;
|
|
122
|
+
/** Closes the socket. Calling it again is safe. */
|
|
93
123
|
close(): Promise<void>;
|
|
94
124
|
}
|
|
125
|
+
/** Factory and bulk-cleanup surface for UDP sockets owned by one plugin instance. */
|
|
95
126
|
export interface PluginUdpControl {
|
|
127
|
+
/** Creates an unbound socket with the requested options. */
|
|
96
128
|
createSocket(options?: PluginUdpSocketOptions): PluginUdpSocket;
|
|
129
|
+
/** Closes every UDP socket created through this control. */
|
|
97
130
|
closeAll(): Promise<void>;
|
|
98
131
|
}
|
|
132
|
+
/** Network capability exposed when the plugin declares `network`. */
|
|
99
133
|
export interface PluginNetworkControl {
|
|
134
|
+
/** UDP socket factory. HTTP requests use the sibling `ctx.fetch` capability. */
|
|
100
135
|
readonly udp: PluginUdpControl;
|
|
101
136
|
}
|
|
102
137
|
/**
|
|
103
|
-
*
|
|
138
|
+
* A message delivered through the plugin-to-plugin event bus.
|
|
139
|
+
*
|
|
140
|
+
* Every message carries metadata about its publisher so subscribers can
|
|
141
|
+
* apply routing or filtering logic based on the source plugin.
|
|
142
|
+
*/
|
|
143
|
+
export interface PluginEventBusMessage {
|
|
144
|
+
/** The topic this message was published to. */
|
|
145
|
+
topic: string;
|
|
146
|
+
/**
|
|
147
|
+
* Structured-clone-compatible payload. The host does not interpret its
|
|
148
|
+
* business schema, but delivers an independent value to each subscriber.
|
|
149
|
+
*/
|
|
150
|
+
payload: unknown;
|
|
151
|
+
/** Epoch milliseconds when the host dispatched the message. */
|
|
152
|
+
timestamp: number;
|
|
153
|
+
/** Identity of the plugin instance that published this message. */
|
|
154
|
+
publisher: {
|
|
155
|
+
/** Name of the publishing plugin (from its `PluginDefinition.name`). */
|
|
156
|
+
pluginName: string;
|
|
157
|
+
/** Whether the publisher is a global or per-operator instance. */
|
|
158
|
+
instanceScope: 'operator' | 'global';
|
|
159
|
+
/** Operator ID when the publisher is an operator-scoped instance. */
|
|
160
|
+
operatorId?: string;
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Permission-gated pub/sub bus for in-process plugin-to-plugin communication.
|
|
165
|
+
*
|
|
166
|
+
* Topics are plain strings shared across all plugin instances within the same
|
|
167
|
+
* host process. Handlers are started synchronously in subscription order.
|
|
168
|
+
* Async handlers run independently; their errors are captured and logged by
|
|
169
|
+
* the host rather than propagated to the publisher.
|
|
170
|
+
*
|
|
171
|
+
* **Lifecycle**: the host automatically removes all subscriptions owned by a
|
|
172
|
+
* plugin instance when it unloads. Individual subscriptions can be cancelled
|
|
173
|
+
* earlier by calling the function returned from {@link subscribe}.
|
|
104
174
|
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
175
|
+
* **Topic naming**: use dot-separated, plugin-prefixed names to avoid
|
|
176
|
+
* collisions — for example `my-plugin.status.changed` or
|
|
177
|
+
* `callsign-filter.match.found`.
|
|
178
|
+
*/
|
|
179
|
+
export interface PluginEventBus {
|
|
180
|
+
/**
|
|
181
|
+
* Publishes a message to all current subscribers of the given topic.
|
|
182
|
+
*
|
|
183
|
+
* This is a fire-and-forget operation. The host guarantees that subscriber
|
|
184
|
+
* exceptions never propagate back to the caller. The call itself throws
|
|
185
|
+
* synchronously when the payload is not structured-clone compatible or
|
|
186
|
+
* contains a Host capability.
|
|
187
|
+
*
|
|
188
|
+
* @param topic - Exact topic string to publish to.
|
|
189
|
+
* @param payload - Optional structured-clone-compatible data. Keep payloads reasonably small.
|
|
190
|
+
*/
|
|
191
|
+
publish(topic: string, payload?: unknown): void;
|
|
192
|
+
/**
|
|
193
|
+
* Subscribes to messages on the given topic.
|
|
194
|
+
*
|
|
195
|
+
* The same handler function instance will only be added once per topic.
|
|
196
|
+
* Different closures with identical logic are treated as distinct subscribers.
|
|
197
|
+
*
|
|
198
|
+
* @param topic - Exact topic string to listen on.
|
|
199
|
+
* @param handler - Callback invoked for each matching message. May return a
|
|
200
|
+
* `Promise`; the host catches rejections and logs them.
|
|
201
|
+
* @returns An unsubscribe function. Calling it more than once is a no-op.
|
|
202
|
+
*/
|
|
203
|
+
subscribe(topic: string, handler: (message: PluginEventBusMessage) => void | Promise<void>): () => void;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Read-only summary of another operator in the same Host.
|
|
107
207
|
*/
|
|
108
208
|
export interface OtherOperatorSnapshot {
|
|
109
209
|
/** Unique operator identifier used by the host. */
|
|
@@ -120,8 +220,15 @@ export interface OtherOperatorSnapshot {
|
|
|
120
220
|
readonly isTransmitting: boolean;
|
|
121
221
|
/** Current transmit cycle selection where `0` is even and `1` is odd. */
|
|
122
222
|
readonly transmitCycles: number[];
|
|
223
|
+
/** Current automation runtime snapshot when available. */
|
|
224
|
+
readonly automation?: StrategyRuntimeSnapshot | null;
|
|
123
225
|
}
|
|
124
|
-
|
|
226
|
+
/**
|
|
227
|
+
* Read-only state and query surface for the current operator-scoped plugin
|
|
228
|
+
* instance. Mutations are submitted through `ctx.operatorCommands` when the
|
|
229
|
+
* plugin declares `operator:transmit-control`.
|
|
230
|
+
*/
|
|
231
|
+
export interface OperatorSnapshot {
|
|
125
232
|
/** Unique operator identifier used by the host. */
|
|
126
233
|
readonly id: string;
|
|
127
234
|
/** Whether this operator is currently transmitting or otherwise armed. */
|
|
@@ -140,39 +247,6 @@ export interface OperatorControl {
|
|
|
140
247
|
readonly automation: StrategyRuntimeSnapshot | null;
|
|
141
248
|
/** Returns read-only snapshots for operators other than the current instance. */
|
|
142
249
|
getOtherOperators(): OtherOperatorSnapshot[];
|
|
143
|
-
/** Enables transmission/automation for the current operator. */
|
|
144
|
-
startTransmitting(): void;
|
|
145
|
-
/** Disables transmission/automation for the current operator. */
|
|
146
|
-
stopTransmitting(): void;
|
|
147
|
-
/**
|
|
148
|
-
* Requests that the operator call the specified target station.
|
|
149
|
-
*
|
|
150
|
-
* Passing `lastMessage` helps the host preserve the triggering context.
|
|
151
|
-
*/
|
|
152
|
-
call(callsign: string, lastMessage?: {
|
|
153
|
-
message: FrameMessage;
|
|
154
|
-
slotInfo: SlotInfo;
|
|
155
|
-
}): void;
|
|
156
|
-
/**
|
|
157
|
-
* Requests host-managed reply behavior for a decoded message.
|
|
158
|
-
*
|
|
159
|
-
* This is equivalent to an operator selecting a decode in the RX view while
|
|
160
|
-
* keeping the API independent from any specific UDP/control protocol.
|
|
161
|
-
*/
|
|
162
|
-
replyToDecode(decode: {
|
|
163
|
-
callsign: string;
|
|
164
|
-
lastMessage: {
|
|
165
|
-
message: FrameMessage;
|
|
166
|
-
slotInfo: SlotInfo;
|
|
167
|
-
};
|
|
168
|
-
modifiers?: number;
|
|
169
|
-
}): void;
|
|
170
|
-
/**
|
|
171
|
-
* Updates the operator's transmit cycle preference.
|
|
172
|
-
*
|
|
173
|
-
* Pass a single value or an array to support alternating or multi-cycle modes.
|
|
174
|
-
*/
|
|
175
|
-
setTransmitCycles(cycles: number | number[]): void;
|
|
176
250
|
/**
|
|
177
251
|
* Checks whether this operator has previously worked the given callsign.
|
|
178
252
|
*/
|
|
@@ -184,40 +258,81 @@ export interface OperatorControl {
|
|
|
184
258
|
* working the target callsign.
|
|
185
259
|
*/
|
|
186
260
|
isTargetBeingWorkedByOthers(targetCallsign: string): boolean;
|
|
187
|
-
/** Clears host-managed decoded-message views when available. */
|
|
188
|
-
clearDecodes(window?: number): void;
|
|
189
|
-
/** Stops current transmission/automation. */
|
|
190
|
-
haltTransmission(options?: {
|
|
191
|
-
autoOnly?: boolean;
|
|
192
|
-
}): void;
|
|
193
|
-
/** Stores the current free-text message without necessarily transmitting it. */
|
|
194
|
-
setFreeText(text: string): void;
|
|
195
|
-
/** Requests transmission of free text. If text is provided it is stored first. */
|
|
196
|
-
sendFreeText(text?: string): void;
|
|
197
|
-
/** Applies a temporary session grid/location override when the host supports it. */
|
|
198
|
-
setTemporaryLocation(location: string): void;
|
|
199
|
-
/** Requests callsign highlighting in host decode views when available. */
|
|
200
|
-
highlightCallsign(rule: {
|
|
201
|
-
callsign: string;
|
|
202
|
-
background?: string | null;
|
|
203
|
-
foreground?: string | null;
|
|
204
|
-
lastOnly?: boolean;
|
|
205
|
-
}): void;
|
|
206
|
-
/**
|
|
207
|
-
* Records a completed QSO through the host logbook pipeline.
|
|
208
|
-
*/
|
|
209
|
-
recordQSO(record: QSORecord): void;
|
|
210
|
-
/**
|
|
211
|
-
* Pushes updated slot text content to the frontend operator view.
|
|
212
|
-
*/
|
|
213
|
-
notifySlotsUpdated(slots: OperatorSlots): void;
|
|
214
|
-
/**
|
|
215
|
-
* Pushes a strategy state change notification to the frontend operator view.
|
|
216
|
-
*/
|
|
217
|
-
notifyStateChanged(state: string): void;
|
|
218
261
|
}
|
|
219
262
|
/**
|
|
220
|
-
*
|
|
263
|
+
* Declarative operator mutations accepted by the host transmission framework.
|
|
264
|
+
*
|
|
265
|
+
* The command set deliberately contains no PTT, audio, mixer, encoder, raw
|
|
266
|
+
* transmit or emergency-stop primitive. Plugins can request product actions;
|
|
267
|
+
* only the host coordinators may translate them into a physical RF lifecycle.
|
|
268
|
+
*/
|
|
269
|
+
export type PluginOperatorCommand = {
|
|
270
|
+
type: 'start-automation';
|
|
271
|
+
} | {
|
|
272
|
+
type: 'stop-automation';
|
|
273
|
+
} | {
|
|
274
|
+
type: 'request-call';
|
|
275
|
+
callsign: string;
|
|
276
|
+
lastMessage?: {
|
|
277
|
+
message: FrameMessage;
|
|
278
|
+
slotInfo: SlotInfo;
|
|
279
|
+
};
|
|
280
|
+
} | {
|
|
281
|
+
type: 'reply-to-decode';
|
|
282
|
+
callsign: string;
|
|
283
|
+
lastMessage: {
|
|
284
|
+
message: FrameMessage;
|
|
285
|
+
slotInfo: SlotInfo;
|
|
286
|
+
};
|
|
287
|
+
modifiers?: number;
|
|
288
|
+
} | {
|
|
289
|
+
type: 'set-transmit-cycles';
|
|
290
|
+
cycles: number | number[];
|
|
291
|
+
} | {
|
|
292
|
+
type: 'remove-contribution';
|
|
293
|
+
} | {
|
|
294
|
+
type: 'clear-decodes';
|
|
295
|
+
window?: number;
|
|
296
|
+
} | {
|
|
297
|
+
type: 'set-free-text';
|
|
298
|
+
text: string;
|
|
299
|
+
} | {
|
|
300
|
+
type: 'send-free-text';
|
|
301
|
+
text?: string;
|
|
302
|
+
} | {
|
|
303
|
+
type: 'set-temporary-location';
|
|
304
|
+
location: string;
|
|
305
|
+
} | {
|
|
306
|
+
type: 'highlight-callsign';
|
|
307
|
+
callsign: string;
|
|
308
|
+
background?: string | null;
|
|
309
|
+
foreground?: string | null;
|
|
310
|
+
lastOnly?: boolean;
|
|
311
|
+
};
|
|
312
|
+
/** Settlement returned after the Host accepts an operator command. */
|
|
313
|
+
export interface PluginOperatorCommandResult {
|
|
314
|
+
/** Host command epoch allocated before any asynchronous work begins. */
|
|
315
|
+
epoch: number;
|
|
316
|
+
/** `superseded` means a newer host command revoked this request. */
|
|
317
|
+
outcome: 'completed' | 'superseded';
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Capability-scoped command port for plugins with
|
|
321
|
+
* `operator:transmit-control` and API v2.
|
|
322
|
+
*
|
|
323
|
+
* The property is omitted from contexts without that capability. Every submit
|
|
324
|
+
* is invocation-guarded and enters the host's per-operator intent lane.
|
|
325
|
+
*/
|
|
326
|
+
export interface OperatorCommandPort {
|
|
327
|
+
/**
|
|
328
|
+
* Submits one high-level operator command through the Host intent lane.
|
|
329
|
+
* Rejects when the invocation expired, the plugin safety gate is disabled,
|
|
330
|
+
* or the current physical lifecycle cannot accept the command.
|
|
331
|
+
*/
|
|
332
|
+
submit(command: PluginOperatorCommand): Promise<PluginOperatorCommandResult>;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Read-only operating-mode projection that is safe for plugins.
|
|
221
336
|
*/
|
|
222
337
|
export interface RadioOperatingMode {
|
|
223
338
|
/**
|
|
@@ -241,7 +356,8 @@ export interface RadioOperatingMode {
|
|
|
241
356
|
*/
|
|
242
357
|
readonly descriptor: ModeDescriptor;
|
|
243
358
|
}
|
|
244
|
-
|
|
359
|
+
/** Read-only frequency, band, mode and connection state for the active radio. */
|
|
360
|
+
export interface RadioView {
|
|
245
361
|
/** Current tuned radio frequency in Hz. */
|
|
246
362
|
readonly frequency: number;
|
|
247
363
|
/** Human-readable current band label, for example `20m`. */
|
|
@@ -250,31 +366,52 @@ export interface RadioControl {
|
|
|
250
366
|
readonly mode: RadioOperatingMode;
|
|
251
367
|
/** Whether the radio transport is currently connected. */
|
|
252
368
|
readonly isConnected: boolean;
|
|
253
|
-
/** Negotiated radio capability controls. Requires radio plugin permissions. */
|
|
254
|
-
readonly capabilities: RadioCapabilitiesControl;
|
|
255
|
-
/** Physical radio power controls. Requires radio plugin permissions. */
|
|
256
|
-
readonly power: RadioPowerControl;
|
|
257
|
-
/**
|
|
258
|
-
* Requests a frequency change.
|
|
259
|
-
*
|
|
260
|
-
* The host remains responsible for serializing hardware access and enforcing
|
|
261
|
-
* any safety or capability constraints.
|
|
262
|
-
*/
|
|
263
|
-
setFrequency(freq: number): Promise<void>;
|
|
264
369
|
}
|
|
265
370
|
/**
|
|
266
371
|
* Access to the host-managed radio capability negotiation system.
|
|
267
372
|
*/
|
|
268
|
-
export interface
|
|
269
|
-
/** Returns the current capability descriptor/state snapshot.
|
|
373
|
+
export interface RadioCapabilitiesView {
|
|
374
|
+
/** Returns the current capability descriptor/state snapshot. */
|
|
270
375
|
getSnapshot(): CapabilityList;
|
|
271
|
-
/** Returns a single capability state from the current snapshot, or null.
|
|
376
|
+
/** Returns a single capability state from the current snapshot, or null. */
|
|
272
377
|
getState(id: string): CapabilityState | null;
|
|
273
|
-
/** Refreshes readable capability values and returns the updated snapshot.
|
|
378
|
+
/** Refreshes readable capability values and returns the updated snapshot. */
|
|
274
379
|
refresh(): Promise<CapabilityList>;
|
|
275
|
-
/** Writes a capability value or triggers an action capability. Requires `radio:control`. */
|
|
276
|
-
write(payload: WriteCapabilityPayload): Promise<void>;
|
|
277
380
|
}
|
|
381
|
+
/** Declarative radio mutations accepted by the host radio coordinator. */
|
|
382
|
+
export type PluginRadioCommand = {
|
|
383
|
+
type: 'set-frequency';
|
|
384
|
+
frequency: number;
|
|
385
|
+
} | {
|
|
386
|
+
/** Atomically changes band and optionally starts the radio's tuner while RF is idle. */
|
|
387
|
+
type: 'switch-band';
|
|
388
|
+
frequency: number;
|
|
389
|
+
autoTune?: boolean;
|
|
390
|
+
};
|
|
391
|
+
/**
|
|
392
|
+
* Capability-scoped radio command port.
|
|
393
|
+
*
|
|
394
|
+
* This port exists only for plugins with `radio:control`. It deliberately does
|
|
395
|
+
* not expose a radio connection, PTT primitive, mode switch, audio output or
|
|
396
|
+
* any other physical device object.
|
|
397
|
+
*/
|
|
398
|
+
export interface RadioCommandPort {
|
|
399
|
+
/** Submits a frequency/band command after Host physical-idle validation. */
|
|
400
|
+
submit(command: PluginRadioCommand): Promise<void>;
|
|
401
|
+
}
|
|
402
|
+
/** Explicit tuner operations; no arbitrary capability identifier is accepted. */
|
|
403
|
+
export type PluginRadioTunerCommand = {
|
|
404
|
+
type: 'set-enabled';
|
|
405
|
+
enabled: boolean;
|
|
406
|
+
} | {
|
|
407
|
+
type: 'start-manual-tune';
|
|
408
|
+
};
|
|
409
|
+
/** Capability-scoped tuner command port for `radio:tuner-control` plugins. */
|
|
410
|
+
export interface RadioTunerCommandPort {
|
|
411
|
+
/** Submits one explicit tuner operation after Host safety validation. */
|
|
412
|
+
submit(command: PluginRadioTunerCommand): Promise<void>;
|
|
413
|
+
}
|
|
414
|
+
/** Optional target profile and startup behavior for a radio power command. */
|
|
278
415
|
export interface RadioPowerSetOptions {
|
|
279
416
|
/** Profile to target. Defaults to the active profile. */
|
|
280
417
|
profileId?: string;
|
|
@@ -284,13 +421,25 @@ export interface RadioPowerSetOptions {
|
|
|
284
421
|
/**
|
|
285
422
|
* Access to physical radio power management.
|
|
286
423
|
*/
|
|
287
|
-
export interface
|
|
288
|
-
/** Returns power support information for the active or specified profile.
|
|
424
|
+
export interface RadioPowerView {
|
|
425
|
+
/** Returns power support information for the active or specified profile. */
|
|
289
426
|
getSupport(profileId?: string): Promise<RadioPowerSupportInfo>;
|
|
290
|
-
/** Returns the last known power transition state for the active or specified profile.
|
|
427
|
+
/** Returns the last known power transition state for the active or specified profile. */
|
|
291
428
|
getState(profileId?: string): RadioPowerStateEvent | null;
|
|
292
|
-
|
|
293
|
-
|
|
429
|
+
}
|
|
430
|
+
/** Declarative power-state transition accepted by `ctx.radioPowerCommands`. */
|
|
431
|
+
export type PluginRadioPowerCommand = {
|
|
432
|
+
/** Command discriminator. */
|
|
433
|
+
type: 'set-power';
|
|
434
|
+
/** Requested physical/controller power target. */
|
|
435
|
+
state: RadioPowerTarget;
|
|
436
|
+
/** Optional profile selection and automatic engine startup behavior. */
|
|
437
|
+
options?: RadioPowerSetOptions;
|
|
438
|
+
};
|
|
439
|
+
/** Capability-scoped physical power command port for `radio:power` plugins. */
|
|
440
|
+
export interface RadioPowerCommandPort {
|
|
441
|
+
/** Requests a power transition and resolves with the Host's final state. */
|
|
442
|
+
submit(command: PluginRadioPowerCommand): Promise<RadioPowerResponse>;
|
|
294
443
|
}
|
|
295
444
|
/**
|
|
296
445
|
* Filter criteria for querying QSO records from the logbook.
|
|
@@ -333,35 +482,37 @@ export interface QSOQueryFilter {
|
|
|
333
482
|
/**
|
|
334
483
|
* Callsign-bound view over a single logbook.
|
|
335
484
|
*
|
|
336
|
-
* The host resolves
|
|
337
|
-
* the handle valid
|
|
485
|
+
* The host resolves an already registered concrete logbook on each operation,
|
|
486
|
+
* which keeps the handle valid across reloads without implicitly creating data.
|
|
338
487
|
*/
|
|
339
|
-
export interface
|
|
488
|
+
export interface CallsignLogbookReadAccess {
|
|
340
489
|
/** Normalized callsign that scopes this accessor. */
|
|
341
490
|
readonly callsign: string;
|
|
342
|
-
/** Returns the resolved logbook id, or null when no logbook
|
|
491
|
+
/** Returns the resolved logbook id, or null when no logbook is registered. */
|
|
343
492
|
getLogBookId(): Promise<string | null>;
|
|
344
493
|
/** Queries QSO records matching the given filter. */
|
|
345
494
|
queryQSOs(filter: QSOQueryFilter): Promise<import('@tx5dr/contracts').QSORecord[]>;
|
|
346
495
|
/** Counts QSO records matching the given filter. */
|
|
347
496
|
countQSOs(filter?: QSOQueryFilter): Promise<number>;
|
|
348
|
-
/** Adds a new QSO record to this callsign's logbook. */
|
|
349
|
-
addQSO(record: import('@tx5dr/contracts').QSORecord): Promise<void>;
|
|
350
|
-
/** Updates partial fields of an existing QSO record. */
|
|
351
|
-
updateQSO(qsoId: string, updates: Partial<import('@tx5dr/contracts').QSORecord>): Promise<void>;
|
|
352
497
|
/** Returns current statistics for this callsign's logbook. */
|
|
353
498
|
getStatistics(): Promise<import('@tx5dr/contracts').LogBookStatistics | null>;
|
|
499
|
+
}
|
|
500
|
+
/** Durable mutation operations scoped to one normalized station callsign. */
|
|
501
|
+
export interface CallsignLogbookCommandPort {
|
|
502
|
+
/** Normalized callsign that scopes this accessor. */
|
|
503
|
+
readonly callsign: string;
|
|
504
|
+
/** Adds a QSO and resolves with the final record after durable commit. */
|
|
505
|
+
addQSO(record: import('@tx5dr/contracts').QSORecord): Promise<import('@tx5dr/contracts').QSORecord>;
|
|
506
|
+
/** Updates a QSO and resolves with the final record after durable commit. */
|
|
507
|
+
updateQSO(qsoId: string, updates: Partial<import('@tx5dr/contracts').QSORecord>): Promise<import('@tx5dr/contracts').QSORecord>;
|
|
354
508
|
/** Notifies the frontend that this callsign's logbook changed. */
|
|
355
509
|
notifyUpdated(operatorId?: string): Promise<void>;
|
|
356
510
|
}
|
|
357
|
-
/**
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
* without host-side special handling.
|
|
363
|
-
*/
|
|
364
|
-
export interface LogbookAccess {
|
|
511
|
+
/** Combined read/write callsign-bound logbook capability. */
|
|
512
|
+
export interface CallsignLogbookAccess extends CallsignLogbookReadAccess, CallsignLogbookCommandPort {
|
|
513
|
+
}
|
|
514
|
+
/** Read-only worked-status and QSO query capability for `logbook:read`. */
|
|
515
|
+
export interface LogbookReadAccess {
|
|
365
516
|
/** Checks whether the callsign has already been worked. */
|
|
366
517
|
hasWorked(callsign: string, options?: {
|
|
367
518
|
anyBand?: boolean;
|
|
@@ -374,14 +525,24 @@ export interface LogbookAccess {
|
|
|
374
525
|
queryQSOs(filter: QSOQueryFilter): Promise<import('@tx5dr/contracts').QSORecord[]>;
|
|
375
526
|
/** Counts QSO records matching the given filter. */
|
|
376
527
|
countQSOs(filter?: QSOQueryFilter): Promise<number>;
|
|
377
|
-
/** Returns a callsign-bound accessor suitable for global plugin instances. */
|
|
378
|
-
forCallsign(callsign: string):
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
528
|
+
/** Returns a read-only callsign-bound accessor suitable for global plugin instances. */
|
|
529
|
+
forCallsign(callsign: string): CallsignLogbookReadAccess;
|
|
530
|
+
}
|
|
531
|
+
/** Durable mutation operations exposed by the `logbook:write` permission. */
|
|
532
|
+
export interface LogbookCommandPort {
|
|
533
|
+
/** Adds a QSO and resolves with the final record after durable commit. */
|
|
534
|
+
addQSO(record: import('@tx5dr/contracts').QSORecord): Promise<import('@tx5dr/contracts').QSORecord>;
|
|
535
|
+
/** Updates a QSO and resolves with the final record after durable commit. */
|
|
536
|
+
updateQSO(qsoId: string, updates: Partial<import('@tx5dr/contracts').QSORecord>): Promise<import('@tx5dr/contracts').QSORecord>;
|
|
383
537
|
/** Notifies the frontend to refresh logbook data (call after batch writes). */
|
|
384
538
|
notifyUpdated(): Promise<void>;
|
|
539
|
+
/** Returns a callsign-bound durable mutation port for global plugin instances. */
|
|
540
|
+
forCallsign(callsign: string): CallsignLogbookCommandPort;
|
|
541
|
+
}
|
|
542
|
+
/** @deprecated Prefer capability-specific LogbookReadAccess and LogbookCommandPort. */
|
|
543
|
+
export interface LogbookAccess extends LogbookReadAccess, LogbookCommandPort {
|
|
544
|
+
/** Returns a combined read/write accessor for the requested station callsign. */
|
|
545
|
+
forCallsign(callsign: string): CallsignLogbookAccess;
|
|
385
546
|
}
|
|
386
547
|
/**
|
|
387
548
|
* Optional constraints used when asking the host for a quieter transmit offset.
|
|
@@ -431,7 +592,9 @@ export interface BandAccess {
|
|
|
431
592
|
* decode environment.
|
|
432
593
|
*
|
|
433
594
|
* Returns `null` when the host cannot evaluate the slot or when no suitable
|
|
434
|
-
* idle window is found.
|
|
595
|
+
* idle window is found. A successful result also reserves that offset for the
|
|
596
|
+
* current operator and analyzed slot so later operators avoid selecting the
|
|
597
|
+
* same window.
|
|
435
598
|
*/
|
|
436
599
|
findIdleTransmitFrequency(options?: IdleTransmitFrequencyOptions): number | null;
|
|
437
600
|
/**
|
|
@@ -473,7 +636,8 @@ export interface PanelMeta {
|
|
|
473
636
|
*/
|
|
474
637
|
export interface UIBridge {
|
|
475
638
|
/**
|
|
476
|
-
* Publishes
|
|
639
|
+
* Publishes a JSON-compatible snapshot for the given declarative panel id.
|
|
640
|
+
* Mutating the caller's object after this call does not alter panel state.
|
|
477
641
|
*/
|
|
478
642
|
send(panelId: string, data: unknown): void;
|
|
479
643
|
/**
|
|
@@ -502,7 +666,7 @@ export interface UIBridge {
|
|
|
502
666
|
*/
|
|
503
667
|
registerPageHandler(handler: PluginUIHandler): void;
|
|
504
668
|
/**
|
|
505
|
-
* Pushes a
|
|
669
|
+
* Pushes a JSON-compatible data snapshot to the specific page session.
|
|
506
670
|
*
|
|
507
671
|
* Prefer this API whenever the plugin already knows the target session id
|
|
508
672
|
* (for example from {@link PluginUIRequestContext.pageSessionId} or
|
|
@@ -517,7 +681,7 @@ export interface UIBridge {
|
|
|
517
681
|
*/
|
|
518
682
|
listActivePageSessions(pageId: string): PluginUIPageSessionInfo[];
|
|
519
683
|
/**
|
|
520
|
-
* Pushes a
|
|
684
|
+
* Pushes a JSON-compatible data snapshot to an iframe UI page by page id.
|
|
521
685
|
*
|
|
522
686
|
* This compatibility helper only succeeds when exactly one active session of
|
|
523
687
|
* the current plugin instance matches the page id. If multiple sessions are
|
|
@@ -529,8 +693,9 @@ export interface UIBridge {
|
|
|
529
693
|
* Handler for custom messages sent from iframe UI pages.
|
|
530
694
|
*
|
|
531
695
|
* Plugins register a handler via `ctx.ui.registerPageHandler()` to receive
|
|
532
|
-
*
|
|
533
|
-
*
|
|
696
|
+
* application-defined invoke requests from their iframe-based UIs. The Host
|
|
697
|
+
* does not interpret the business schema, but it enforces the page/session
|
|
698
|
+
* authorization and JSON data boundary in both directions.
|
|
534
699
|
*/
|
|
535
700
|
export interface PluginUIHandler {
|
|
536
701
|
/**
|
|
@@ -538,49 +703,77 @@ export interface PluginUIHandler {
|
|
|
538
703
|
*
|
|
539
704
|
* @param pageId - The page that sent the message.
|
|
540
705
|
* @param action - Developer-defined action identifier.
|
|
541
|
-
* @param data -
|
|
706
|
+
* @param data - JSON-compatible snapshot from the iframe; validate it as
|
|
707
|
+
* untrusted input before use.
|
|
542
708
|
* @param requestContext - Host-authenticated page context, including any
|
|
543
709
|
* bound resource for this page session.
|
|
544
|
-
* @returns
|
|
710
|
+
* @returns A JSON-compatible response snapshot sent back to the iframe.
|
|
545
711
|
*/
|
|
546
712
|
onMessage(pageId: string, action: string, data: unknown, requestContext: PluginUIRequestContext): Promise<unknown>;
|
|
547
713
|
}
|
|
714
|
+
/** Host-authenticated user identity attached to an iframe invoke request. */
|
|
548
715
|
export interface PluginUIRequestUser {
|
|
716
|
+
/** Stable token/session identifier; not the raw credential. */
|
|
549
717
|
readonly tokenId: string;
|
|
718
|
+
/** Effective role at the time the Host authorizes the request. */
|
|
550
719
|
readonly role: 'viewer' | 'operator' | 'admin';
|
|
720
|
+
/** Operator IDs the current user is allowed to access. */
|
|
551
721
|
readonly operatorIds: string[];
|
|
722
|
+
/** Fine-grained grants associated with the authenticated user, when present. */
|
|
552
723
|
readonly permissionGrants?: PermissionGrant[];
|
|
553
724
|
}
|
|
725
|
+
/** Resource identity resolved and authorized from the page descriptor binding. */
|
|
554
726
|
export interface PluginUIBoundResource {
|
|
727
|
+
/** Kind declared by `resourceBinding`. */
|
|
555
728
|
readonly kind: 'callsign' | 'operator';
|
|
729
|
+
/** Normalized callsign or authorized operator ID. */
|
|
556
730
|
readonly value: string;
|
|
557
731
|
}
|
|
732
|
+
/** Plugin instance selected by the Host for this page request. */
|
|
558
733
|
export type PluginUIInstanceTarget = {
|
|
559
734
|
readonly kind: 'global';
|
|
560
735
|
} | {
|
|
561
736
|
readonly kind: 'operator';
|
|
562
737
|
readonly operatorId: string;
|
|
563
738
|
};
|
|
739
|
+
/** Read-only identity of one active plugin iframe page session. */
|
|
564
740
|
export interface PluginUIPageSessionInfo {
|
|
741
|
+
/** Unique ID used for exact session pushes. */
|
|
565
742
|
readonly sessionId: string;
|
|
743
|
+
/** `PluginDefinition.ui.pages` entry rendered by this session. */
|
|
566
744
|
readonly pageId: string;
|
|
745
|
+
/** Host-authorized resource binding, when the page declares one. */
|
|
567
746
|
readonly resource?: PluginUIBoundResource;
|
|
568
747
|
}
|
|
748
|
+
/** Page-session identity plus an exact push channel back to that iframe. */
|
|
569
749
|
export interface PluginUIPageContext extends PluginUIPageSessionInfo {
|
|
750
|
+
/** Sends a JSON-compatible snapshot to this exact page session. */
|
|
570
751
|
push(action: string, data?: unknown): void;
|
|
571
752
|
}
|
|
753
|
+
/**
|
|
754
|
+
* Host-authenticated context passed to an iframe page handler.
|
|
755
|
+
*
|
|
756
|
+
* Treat `data` from the iframe as untrusted input. Use this context, rather
|
|
757
|
+
* than caller-supplied IDs, for authorization and storage scoping.
|
|
758
|
+
*/
|
|
572
759
|
export interface PluginUIRequestContext {
|
|
760
|
+
/** Same exact page session identifier exposed as `page.sessionId`. */
|
|
573
761
|
readonly pageSessionId: string;
|
|
762
|
+
/** User identity authorized by the Host for this request. */
|
|
574
763
|
readonly user: PluginUIRequestUser;
|
|
764
|
+
/** Bound callsign/operator, when required by the page descriptor. */
|
|
575
765
|
readonly resource?: PluginUIBoundResource;
|
|
766
|
+
/** Global or operator plugin instance receiving the request. */
|
|
576
767
|
readonly instanceTarget: PluginUIInstanceTarget;
|
|
768
|
+
/** Exact page session/push capability, valid only during the current handler invocation. */
|
|
577
769
|
readonly page: PluginUIPageContext;
|
|
578
770
|
/**
|
|
579
771
|
* Page-scoped file storage shared with iframe `tx5dr.file*()` calls.
|
|
580
772
|
*
|
|
581
773
|
* Use this in `registerPageHandler()` handlers to read files uploaded by the
|
|
582
774
|
* current iframe page session without reconstructing host-internal scope
|
|
583
|
-
* paths.
|
|
775
|
+
* paths. Both `page` and `files` are exact-invocation capabilities: do not
|
|
776
|
+
* retain and invoke them after the current `onMessage()` promise settles.
|
|
584
777
|
*/
|
|
585
778
|
readonly files: PluginFileStore;
|
|
586
779
|
}
|
|
@@ -591,9 +784,9 @@ export interface PluginUIRequestContext {
|
|
|
591
784
|
* traversal outside the sandbox is rejected by the host.
|
|
592
785
|
*/
|
|
593
786
|
export interface PluginFileStore {
|
|
594
|
-
/** Writes
|
|
787
|
+
/** Writes a copy of the Buffer, creating or replacing the file. */
|
|
595
788
|
write(path: string, data: Buffer): Promise<void>;
|
|
596
|
-
/** Reads a file. Returns `null` when the path does not exist. */
|
|
789
|
+
/** Reads a file into a new Buffer. Returns `null` when the path does not exist. */
|
|
597
790
|
read(path: string): Promise<Buffer | null>;
|
|
598
791
|
/** Deletes a file. Returns `true` if the file existed and was removed. */
|
|
599
792
|
delete(path: string): Promise<boolean>;
|