tci-client-node 0.3.0 → 0.4.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/CHANGELOG.md +9 -0
- package/README.md +24 -0
- package/dist/audio/index.d.cts +2 -1
- package/dist/audio/index.d.ts +2 -1
- package/dist/dialect/index.cjs +175 -12
- package/dist/dialect/index.cjs.map +1 -1
- package/dist/dialect/index.d.cts +3 -2
- package/dist/dialect/index.d.ts +3 -2
- package/dist/dialect/index.js +175 -12
- package/dist/dialect/index.js.map +1 -1
- package/dist/errors-CT3b8LLw.d.cts +9 -0
- package/dist/errors-CT3b8LLw.d.ts +9 -0
- package/dist/index.cjs +404 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -5
- package/dist/index.d.ts +14 -5
- package/dist/index.js +399 -5
- package/dist/index.js.map +1 -1
- package/dist/meter/index.cjs +348 -0
- package/dist/meter/index.cjs.map +1 -0
- package/dist/meter/index.d.cts +78 -0
- package/dist/meter/index.d.ts +78 -0
- package/dist/meter/index.js +317 -0
- package/dist/meter/index.js.map +1 -0
- package/dist/protocol/index.d.cts +36 -2
- package/dist/protocol/index.d.ts +36 -2
- package/dist/testing/index.cjs +32 -0
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +19 -1
- package/dist/testing/index.d.ts +19 -1
- package/dist/testing/index.js +32 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/types-C0qVJVSO.d.ts +74 -0
- package/dist/types-C4MtLKoy.d.cts +74 -0
- package/dist/{types-1YXGwrgI.d.cts → types-CYK_NOTz.d.cts} +2 -0
- package/dist/{types-BtPh4Dbd.d.ts → types-DVIPU-VR.d.ts} +2 -0
- package/package.json +11 -2
- package/dist/index-lbK6NGY4.d.cts +0 -42
- package/dist/index-paj1AOJY.d.ts +0 -42
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
- Add owned RX/TX meter stream sessions and progressive capability states.
|
|
6
|
+
- Add dialect adapters for ExpertSDR, Thetis, AetherSDR, and generic observed servers.
|
|
7
|
+
- Parse and coalesce legacy, channel, and extended RX sensor frames.
|
|
8
|
+
- Expose standard TX RMS/peak power and SWR plus AetherSDR's native dBFS ALC extension.
|
|
9
|
+
- Validate malformed meter frames and perform best-effort subscription cleanup.
|
|
10
|
+
- Extend the mock TCI server with typed RX/TX meter helpers.
|
|
11
|
+
|
|
3
12
|
## 0.3.0
|
|
4
13
|
|
|
5
14
|
- Add standard TCI IQ capability discovery and dialect-specific sample-rate lists.
|
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ TCI is a WebSocket protocol: text commands are used for CAT-style radio control,
|
|
|
15
15
|
- RX and TX sensor state parsing
|
|
16
16
|
- RX audio, TX audio, TX_CHRONO, and line-out stream frame parsing/building
|
|
17
17
|
- Standard IQ capability discovery, DDS tracking, sample-rate readback, and owned IQ stream sessions
|
|
18
|
+
- Owned RX/TX meter sessions with typed dBm, RMS/peak power, SWR, and dialect extensions
|
|
18
19
|
- Serial command queue with timeout, cancellation, and interleaved broadcast handling
|
|
19
20
|
- Mock TCI server and fake WebSocket transport for integration tests
|
|
20
21
|
|
|
@@ -89,6 +90,28 @@ if (capabilities.supported) {
|
|
|
89
90
|
|
|
90
91
|
`openIqStream()` resolves on the first valid IQ frame rather than relying on an `IQ_START` echo, because some compatible servers intentionally do not echo that command. The returned session owns the subscription and sends best-effort `IQ_STOP` when closed.
|
|
91
92
|
|
|
93
|
+
## Meter Streams
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
const meters = await client.openMeterStream({ receiver: 0, channel: 0, trx: 0, intervalMs: 300 });
|
|
97
|
+
|
|
98
|
+
meters.on('rxFrame', (frame) => {
|
|
99
|
+
console.log(frame.levelDbm, frame.averageLevelDbm, frame.peakBinDbm);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
meters.on('txFrame', (frame) => {
|
|
103
|
+
console.log(frame.rmsPowerWatts, frame.peakPowerWatts, frame.swr, frame.alc);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
meters.on('capabilitiesChanged', (capabilities) => {
|
|
107
|
+
console.log(capabilities.rxLevel, capabilities.txRmsPower);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
await meters.close();
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Meter support progresses from dialect declarations and enable acknowledgements to `observed` only after a valid field arrives. `RX_SENSORS`, `RX_CHANNEL_SENSORS`, and Thetis `RX_CHANNEL_SENSORS_EX` are coalesced into one richest RX frame. AetherSDR's trailing TX ALC field is exposed with its native `dbfs` unit. The session sends best-effort RX/TX disable commands when closed and never waits for a TX frame while the radio is idle.
|
|
114
|
+
|
|
92
115
|
`connect()` resolves only after a valid `READY;` initialization sequence. A WebSocket that opens but does not provide enough TCI initialization evidence is rejected. Use `dialect: 'thetis-2.0'` or another dialect ID only when an incomplete server cannot be identified automatically.
|
|
93
116
|
|
|
94
117
|
## Dialects
|
|
@@ -115,6 +138,7 @@ The compatibility `setDrive()` method remains available and treats a clamped val
|
|
|
115
138
|
- `tci-client-node/audio`: stream frame parser/builder and sample conversion helpers.
|
|
116
139
|
- `tci-client-node/dialect`: dialect interfaces, built-ins, detection, and registry.
|
|
117
140
|
- `tci-client-node/transport`: transport interface and default WebSocket transport.
|
|
141
|
+
- `tci-client-node/meter`: meter adapters, capability types, frames, and session API.
|
|
118
142
|
- `tci-client-node/testing`: `MockTciServer` and `FakeWebSocket` helpers for tests.
|
|
119
143
|
|
|
120
144
|
## Audio Frames
|
package/dist/audio/index.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { j as TciStreamLengthSemantics } from '../types-
|
|
1
|
+
import { j as TciStreamLengthSemantics } from '../types-CYK_NOTz.cjs';
|
|
2
2
|
import '../text-BwCWY1k1.cjs';
|
|
3
|
+
import '../types-C4MtLKoy.cjs';
|
|
3
4
|
|
|
4
5
|
declare const TCI_STREAM_HEADER_BYTES: number;
|
|
5
6
|
declare enum TciStreamType {
|
package/dist/audio/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { j as TciStreamLengthSemantics } from '../types-
|
|
1
|
+
import { j as TciStreamLengthSemantics } from '../types-DVIPU-VR.js';
|
|
2
2
|
import '../text-BwCWY1k1.js';
|
|
3
|
+
import '../types-C0qVJVSO.js';
|
|
3
4
|
|
|
4
5
|
declare const TCI_STREAM_HEADER_BYTES: number;
|
|
5
6
|
declare enum TciStreamType {
|
package/dist/dialect/index.cjs
CHANGED
|
@@ -36,6 +36,166 @@ __export(dialect_exports, {
|
|
|
36
36
|
});
|
|
37
37
|
module.exports = __toCommonJS(dialect_exports);
|
|
38
38
|
|
|
39
|
+
// src/meter/types.ts
|
|
40
|
+
var UNKNOWN_TCI_METER_CAPABILITIES = {
|
|
41
|
+
rxLevel: "unknown",
|
|
42
|
+
rxAverageLevel: "unknown",
|
|
43
|
+
rxPeakBin: "unknown",
|
|
44
|
+
txMicLevel: "unknown",
|
|
45
|
+
txRmsPower: "unknown",
|
|
46
|
+
txPeakPower: "unknown",
|
|
47
|
+
txSwr: "unknown",
|
|
48
|
+
txAlcDbfs: "unknown"
|
|
49
|
+
};
|
|
50
|
+
function cloneMeterCapabilities(capabilities) {
|
|
51
|
+
return { ...capabilities };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/meter/adapters.ts
|
|
55
|
+
var StandardTciMeterAdapter = class {
|
|
56
|
+
declaredCapabilities;
|
|
57
|
+
options;
|
|
58
|
+
constructor(options = {}) {
|
|
59
|
+
this.options = options;
|
|
60
|
+
this.declaredCapabilities = {
|
|
61
|
+
...UNKNOWN_TCI_METER_CAPABILITIES,
|
|
62
|
+
rxLevel: "declared",
|
|
63
|
+
txMicLevel: "declared",
|
|
64
|
+
txRmsPower: "declared",
|
|
65
|
+
txPeakPower: "declared",
|
|
66
|
+
txSwr: "declared",
|
|
67
|
+
rxAverageLevel: options.supportsRxExtended ? "declared" : "unknown",
|
|
68
|
+
rxPeakBin: options.supportsRxExtended ? "declared" : "unknown",
|
|
69
|
+
txAlcDbfs: options.txAlcUnit === "dbfs" ? "declared" : "unknown",
|
|
70
|
+
...options.capabilities
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
normalizeInterval(intervalMs) {
|
|
74
|
+
const requestedMs = Math.round(intervalMs);
|
|
75
|
+
if (this.options.interval?.fixedMs !== void 0) {
|
|
76
|
+
return { requestedMs, appliedMs: this.options.interval.fixedMs };
|
|
77
|
+
}
|
|
78
|
+
const minMs = this.options.interval?.minMs ?? requestedMs;
|
|
79
|
+
const maxMs = this.options.interval?.maxMs ?? requestedMs;
|
|
80
|
+
const normalized = Math.max(minMs, Math.min(maxMs, requestedMs));
|
|
81
|
+
return {
|
|
82
|
+
requestedMs,
|
|
83
|
+
appliedMs: this.options.interval?.reportsApplied === false ? void 0 : normalized
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
buildEnableCommand(kind, enabled, intervalMs) {
|
|
87
|
+
return {
|
|
88
|
+
name: kind === "rx" ? "RX_SENSORS_ENABLE" : "TX_SENSORS_ENABLE",
|
|
89
|
+
args: enabled ? [true, intervalMs] : [false]
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
decode(command, receivedAtMs) {
|
|
93
|
+
switch (command.name) {
|
|
94
|
+
case "rx_sensors":
|
|
95
|
+
return decodeRx(command, receivedAtMs, "rx_sensors");
|
|
96
|
+
case "rx_channel_sensors":
|
|
97
|
+
return decodeRx(command, receivedAtMs, "rx_channel_sensors");
|
|
98
|
+
case "rx_channel_sensors_ex":
|
|
99
|
+
return decodeRx(command, receivedAtMs, "rx_channel_sensors_ex");
|
|
100
|
+
case "tx_sensors":
|
|
101
|
+
return decodeTx(command, receivedAtMs, this.options.txAlcUnit);
|
|
102
|
+
default:
|
|
103
|
+
return void 0;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
function createUnknownTciMeterAdapter() {
|
|
108
|
+
return new StandardTciMeterAdapter({
|
|
109
|
+
capabilities: cloneMeterCapabilities(UNKNOWN_TCI_METER_CAPABILITIES),
|
|
110
|
+
interval: { reportsApplied: false }
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function decodeRx(command, receivedAtMs, source) {
|
|
114
|
+
const receiver = integer(command.args[0]);
|
|
115
|
+
const hasChannel = source !== "rx_sensors";
|
|
116
|
+
const channel = hasChannel ? integer(command.args[1]) : 0;
|
|
117
|
+
const levelIndex = hasChannel ? 2 : 1;
|
|
118
|
+
const levelDbm = finite(command.args[levelIndex]);
|
|
119
|
+
if (receiver === void 0 || receiver < 0 || channel === void 0 || channel < 0 || levelDbm === void 0) {
|
|
120
|
+
return { issue: `Invalid ${command.originalName} meter frame: ${command.raw}` };
|
|
121
|
+
}
|
|
122
|
+
const frame = { receiver, channel, levelDbm, source, receivedAtMs };
|
|
123
|
+
if (source === "rx_channel_sensors_ex") {
|
|
124
|
+
const averageLevelDbm = finite(command.args[3]);
|
|
125
|
+
const peakBinDbm = finite(command.args[4]);
|
|
126
|
+
if (averageLevelDbm === void 0 || peakBinDbm === void 0) {
|
|
127
|
+
return { issue: `Invalid ${command.originalName} extended meter frame: ${command.raw}` };
|
|
128
|
+
}
|
|
129
|
+
frame.averageLevelDbm = averageLevelDbm;
|
|
130
|
+
frame.peakBinDbm = peakBinDbm;
|
|
131
|
+
if (command.args.length > 5) frame.extraArgs = command.args.slice(5);
|
|
132
|
+
} else if (command.args.length > levelIndex + 1) {
|
|
133
|
+
frame.extraArgs = command.args.slice(levelIndex + 1);
|
|
134
|
+
}
|
|
135
|
+
return { decoded: { kind: "rx", frame } };
|
|
136
|
+
}
|
|
137
|
+
function decodeTx(command, receivedAtMs, alcUnit) {
|
|
138
|
+
const trx = integer(command.args[0]);
|
|
139
|
+
if (trx === void 0 || trx < 0) {
|
|
140
|
+
return { issue: `Invalid ${command.originalName} transmitter index: ${command.raw}` };
|
|
141
|
+
}
|
|
142
|
+
const micLevelDbm = optionalFinite(command.args[1]);
|
|
143
|
+
const rmsPowerWatts = optionalFinite(command.args[2]);
|
|
144
|
+
const peakPowerWatts = optionalFinite(command.args[3]);
|
|
145
|
+
const swr = optionalFinite(command.args[4]);
|
|
146
|
+
const alcValue = alcUnit ? optionalFinite(command.args[5]) : void 0;
|
|
147
|
+
if (micLevelDbm.invalid || rmsPowerWatts.invalid || peakPowerWatts.invalid || swr.invalid || alcValue?.invalid) {
|
|
148
|
+
return { issue: `Invalid ${command.originalName} numeric meter frame: ${command.raw}` };
|
|
149
|
+
}
|
|
150
|
+
if (rmsPowerWatts.value !== void 0 && rmsPowerWatts.value < 0 || peakPowerWatts.value !== void 0 && peakPowerWatts.value < 0 || swr.value !== void 0 && swr.value < 1) {
|
|
151
|
+
return { issue: `Out-of-range ${command.originalName} meter frame: ${command.raw}` };
|
|
152
|
+
}
|
|
153
|
+
if (micLevelDbm.value === void 0 && rmsPowerWatts.value === void 0 && peakPowerWatts.value === void 0 && swr.value === void 0 && alcValue?.value === void 0) {
|
|
154
|
+
return { issue: `Empty ${command.originalName} meter frame: ${command.raw}` };
|
|
155
|
+
}
|
|
156
|
+
const frame = {
|
|
157
|
+
trx,
|
|
158
|
+
micLevelDbm: micLevelDbm.value,
|
|
159
|
+
rmsPowerWatts: rmsPowerWatts.value,
|
|
160
|
+
peakPowerWatts: peakPowerWatts.value,
|
|
161
|
+
swr: swr.value,
|
|
162
|
+
receivedAtMs
|
|
163
|
+
};
|
|
164
|
+
if (alcUnit && alcValue?.value !== void 0) frame.alc = { value: alcValue.value, unit: alcUnit };
|
|
165
|
+
const knownArgs = alcUnit ? 6 : 5;
|
|
166
|
+
if (command.args.length > knownArgs) frame.extraArgs = command.args.slice(knownArgs);
|
|
167
|
+
return { decoded: { kind: "tx", frame } };
|
|
168
|
+
}
|
|
169
|
+
function finite(value) {
|
|
170
|
+
if (value === void 0 || value === "") return void 0;
|
|
171
|
+
const parsed = Number(value);
|
|
172
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
173
|
+
}
|
|
174
|
+
function integer(value) {
|
|
175
|
+
const parsed = finite(value);
|
|
176
|
+
return parsed !== void 0 && Number.isInteger(parsed) ? parsed : void 0;
|
|
177
|
+
}
|
|
178
|
+
function optionalFinite(value) {
|
|
179
|
+
if (value === void 0 || value === "") return { invalid: false };
|
|
180
|
+
const parsed = Number(value);
|
|
181
|
+
return Number.isFinite(parsed) ? { value: parsed, invalid: false } : { invalid: true };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// src/meter/TciMeterStreamSession.ts
|
|
185
|
+
var import_eventemitter3 = require("eventemitter3");
|
|
186
|
+
|
|
187
|
+
// src/errors.ts
|
|
188
|
+
var TciError = class extends Error {
|
|
189
|
+
code;
|
|
190
|
+
details;
|
|
191
|
+
constructor(code, message, details) {
|
|
192
|
+
super(message);
|
|
193
|
+
this.name = "TciError";
|
|
194
|
+
this.code = code;
|
|
195
|
+
this.details = details;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
39
199
|
// src/dialect/builtins.ts
|
|
40
200
|
var StandardTciDialect = class {
|
|
41
201
|
id;
|
|
@@ -45,6 +205,7 @@ var StandardTciDialect = class {
|
|
|
45
205
|
supportsTxAudioSource;
|
|
46
206
|
supportsIqStream;
|
|
47
207
|
iqSampleRates;
|
|
208
|
+
meterAdapter;
|
|
48
209
|
driveHasTrx;
|
|
49
210
|
detector;
|
|
50
211
|
resolver;
|
|
@@ -56,6 +217,7 @@ var StandardTciDialect = class {
|
|
|
56
217
|
this.supportsTxAudioSource = options.supportsTxAudioSource;
|
|
57
218
|
this.supportsIqStream = options.supportsIqStream;
|
|
58
219
|
this.iqSampleRates = [...options.iqSampleRates];
|
|
220
|
+
this.meterAdapter = options.meterAdapter;
|
|
59
221
|
this.driveHasTrx = options.driveHasTrx;
|
|
60
222
|
this.detector = options.detect;
|
|
61
223
|
this.resolver = options.resolve;
|
|
@@ -106,6 +268,7 @@ var expertSdr14Dialect = new StandardTciDialect({
|
|
|
106
268
|
supportsIqStream: true,
|
|
107
269
|
iqSampleRates: [48e3, 96e3, 192e3, 384e3],
|
|
108
270
|
driveHasTrx: false,
|
|
271
|
+
meterAdapter: new StandardTciMeterAdapter({ interval: { reportsApplied: false } }),
|
|
109
272
|
detect: (context) => {
|
|
110
273
|
const parsed = version(context);
|
|
111
274
|
if (!parsed || compareTciVersion(parsed, [1, 4]) > 0) return { score: 0, evidence: [] };
|
|
@@ -121,6 +284,7 @@ var expertSdrLegacyDialect = new StandardTciDialect({
|
|
|
121
284
|
supportsIqStream: true,
|
|
122
285
|
iqSampleRates: [48e3, 96e3, 192e3, 384e3],
|
|
123
286
|
driveHasTrx: true,
|
|
287
|
+
meterAdapter: new StandardTciMeterAdapter({ interval: { reportsApplied: false } }),
|
|
124
288
|
detect: (context) => {
|
|
125
289
|
const parsed = version(context);
|
|
126
290
|
if (!parsed || compareTciVersion(parsed, [1, 5]) < 0 || compareTciVersion(parsed, [1, 9]) >= 0) return { score: 0, evidence: [] };
|
|
@@ -136,6 +300,7 @@ var expertSdrModernDialect = new StandardTciDialect({
|
|
|
136
300
|
supportsIqStream: true,
|
|
137
301
|
iqSampleRates: [48e3, 96e3, 192e3, 384e3],
|
|
138
302
|
driveHasTrx: true,
|
|
303
|
+
meterAdapter: new StandardTciMeterAdapter({ interval: { reportsApplied: false } }),
|
|
139
304
|
detect: (context) => {
|
|
140
305
|
const parsed = version(context);
|
|
141
306
|
if (!parsed || compareTciVersion(parsed, [1, 9]) < 0) return { score: 0, evidence: [] };
|
|
@@ -156,6 +321,10 @@ var thetisDialect = new StandardTciDialect({
|
|
|
156
321
|
supportsIqStream: true,
|
|
157
322
|
iqSampleRates: [48e3, 96e3, 192e3, 384e3],
|
|
158
323
|
driveHasTrx: true,
|
|
324
|
+
meterAdapter: new StandardTciMeterAdapter({
|
|
325
|
+
interval: { minMs: 30, maxMs: 1e3 },
|
|
326
|
+
supportsRxExtended: true
|
|
327
|
+
}),
|
|
159
328
|
detect: (context) => {
|
|
160
329
|
const evidence = [];
|
|
161
330
|
let score = 0;
|
|
@@ -184,6 +353,10 @@ var aetherSdrDialect = new StandardTciDialect({
|
|
|
184
353
|
supportsIqStream: true,
|
|
185
354
|
iqSampleRates: [24e3, 48e3, 96e3, 192e3],
|
|
186
355
|
driveHasTrx: true,
|
|
356
|
+
meterAdapter: new StandardTciMeterAdapter({
|
|
357
|
+
interval: { fixedMs: 200 },
|
|
358
|
+
txAlcUnit: "dbfs"
|
|
359
|
+
}),
|
|
187
360
|
detect: (context) => {
|
|
188
361
|
if (!/^aethersdr$/i.test(context.identity.device ?? "")) return { score: 0, evidence: [] };
|
|
189
362
|
const evidence = [`AetherSDR device identity: ${context.identity.device}`];
|
|
@@ -205,6 +378,7 @@ var genericObservedDialect = new StandardTciDialect({
|
|
|
205
378
|
supportsIqStream: false,
|
|
206
379
|
iqSampleRates: [],
|
|
207
380
|
driveHasTrx: true,
|
|
381
|
+
meterAdapter: createUnknownTciMeterAdapter(),
|
|
208
382
|
detect: (context) => ({
|
|
209
383
|
score: context.commandNames.has("ready") ? 10 : 0,
|
|
210
384
|
evidence: ["No vendor-specific match; using observed command shapes"],
|
|
@@ -222,6 +396,7 @@ var genericObservedDialect = new StandardTciDialect({
|
|
|
222
396
|
supportsIqStream: context.commandNames.has("iq_samplerate"),
|
|
223
397
|
iqSampleRates: context.commandNames.has("iq_samplerate") ? [48e3] : [],
|
|
224
398
|
driveHasTrx,
|
|
399
|
+
meterAdapter: createUnknownTciMeterAdapter(),
|
|
225
400
|
detect: genericObservedDialect.detect.bind(genericObservedDialect)
|
|
226
401
|
});
|
|
227
402
|
}
|
|
@@ -252,18 +427,6 @@ function formatVersion(value) {
|
|
|
252
427
|
return value.join(".");
|
|
253
428
|
}
|
|
254
429
|
|
|
255
|
-
// src/errors.ts
|
|
256
|
-
var TciError = class extends Error {
|
|
257
|
-
code;
|
|
258
|
-
details;
|
|
259
|
-
constructor(code, message, details) {
|
|
260
|
-
super(message);
|
|
261
|
-
this.name = "TciError";
|
|
262
|
-
this.code = code;
|
|
263
|
-
this.details = details;
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
|
|
267
430
|
// src/dialect/registry.ts
|
|
268
431
|
var TciDialectRegistry = class {
|
|
269
432
|
dialects = /* @__PURE__ */ new Map();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/dialect/index.ts","../../src/dialect/builtins.ts","../../src/errors.ts","../../src/dialect/registry.ts","../../src/dialect/handshake.ts"],"sourcesContent":["export * from './types.js';\nexport * from './builtins.js';\nexport * from './registry.js';\nexport * from './handshake.js';\n","import type {\n TciDialect,\n TciDialectDetectionContext,\n TciDialectScore,\n TciDriveState,\n TciStreamLengthSemantics,\n} from './types.js';\n\ntype VersionTuple = readonly number[];\n\ninterface StandardDialectOptions {\n id: TciDialect['id'];\n label: string;\n streamLengthSemantics: TciStreamLengthSemantics;\n supportsStreamChannels: boolean;\n supportsTxAudioSource: boolean;\n supportsIqStream: boolean;\n iqSampleRates: readonly number[];\n driveHasTrx: boolean;\n detect: (context: TciDialectDetectionContext) => TciDialectScore;\n resolve?: (context: TciDialectDetectionContext) => TciDialect;\n}\n\nclass StandardTciDialect implements TciDialect {\n readonly id: TciDialect['id'];\n readonly label: string;\n readonly streamLengthSemantics: TciStreamLengthSemantics;\n readonly supportsStreamChannels: boolean;\n readonly supportsTxAudioSource: boolean;\n readonly supportsIqStream: boolean;\n readonly iqSampleRates: readonly number[];\n private readonly driveHasTrx: boolean;\n private readonly detector: StandardDialectOptions['detect'];\n private readonly resolver?: StandardDialectOptions['resolve'];\n\n constructor(options: StandardDialectOptions) {\n this.id = options.id;\n this.label = options.label;\n this.streamLengthSemantics = options.streamLengthSemantics;\n this.supportsStreamChannels = options.supportsStreamChannels;\n this.supportsTxAudioSource = options.supportsTxAudioSource;\n this.supportsIqStream = options.supportsIqStream;\n this.iqSampleRates = [...options.iqSampleRates];\n this.driveHasTrx = options.driveHasTrx;\n this.detector = options.detect;\n this.resolver = options.resolve;\n }\n\n detect(context: TciDialectDetectionContext): TciDialectScore {\n return this.detector(context);\n }\n\n resolve(context: TciDialectDetectionContext): TciDialect {\n return this.resolver?.(context) ?? this;\n }\n\n buildDriveSetArgs(trx: number, value: number): readonly unknown[] {\n return this.driveHasTrx ? [trx, value] : [value];\n }\n\n buildDriveReadArgs(trx: number): readonly unknown[] {\n return this.driveHasTrx ? [trx] : [];\n }\n\n parseDrive(args: readonly string[], defaultTrx: number): TciDriveState | undefined {\n return parseDriveState(args, defaultTrx, this.driveHasTrx);\n }\n\n buildTuneDriveSetArgs(trx: number, value: number): readonly unknown[] {\n return this.driveHasTrx ? [trx, value] : [value];\n }\n\n buildTuneDriveReadArgs(trx: number): readonly unknown[] {\n return this.driveHasTrx ? [trx] : [];\n }\n\n parseTuneDrive(args: readonly string[], defaultTrx: number): TciDriveState | undefined {\n return parseDriveState(args, defaultTrx, this.driveHasTrx);\n }\n}\n\nfunction parseDriveState(args: readonly string[], defaultTrx: number, hasTrx: boolean): TciDriveState | undefined {\n const trx = hasTrx ? Number(args[0]) : defaultTrx;\n const value = Number(args[hasTrx ? 1 : 0]);\n if (!Number.isInteger(trx) || !Number.isFinite(value)) return undefined;\n return { trx, value };\n}\n\nfunction version(context: TciDialectDetectionContext): VersionTuple | undefined {\n return parseTciVersion(context.identity.protocolVersion);\n}\n\nfunction programIncludes(context: TciDialectDetectionContext, value: string): boolean {\n return context.identity.programName?.toLowerCase().includes(value) ?? false;\n}\n\nexport const expertSdr14Dialect: TciDialect = new StandardTciDialect({\n id: 'expertsdr-1.4', label: 'ExpertSDR / TCI 1.4', streamLengthSemantics: 'per-channel',\n supportsStreamChannels: false, supportsTxAudioSource: false, supportsIqStream: true,\n iqSampleRates: [48_000, 96_000, 192_000, 384_000], driveHasTrx: false,\n detect: (context) => {\n const parsed = version(context);\n if (!parsed || compareTciVersion(parsed, [1, 4]) > 0) return { score: 0, evidence: [] };\n return { score: 80 + (programIncludes(context, 'expert') ? 10 : 0), evidence: [`protocol ${formatVersion(parsed)} <= 1.4`] };\n },\n});\n\nexport const expertSdrLegacyDialect: TciDialect = new StandardTciDialect({\n id: 'expertsdr-1.5-1.8', label: 'ExpertSDR / TCI 1.5-1.8', streamLengthSemantics: 'per-channel',\n supportsStreamChannels: false, supportsTxAudioSource: false, supportsIqStream: true,\n iqSampleRates: [48_000, 96_000, 192_000, 384_000], driveHasTrx: true,\n detect: (context) => {\n const parsed = version(context);\n if (!parsed || compareTciVersion(parsed, [1, 5]) < 0 || compareTciVersion(parsed, [1, 9]) >= 0) return { score: 0, evidence: [] };\n return { score: 80 + (programIncludes(context, 'expert') ? 10 : 0), evidence: [`protocol ${formatVersion(parsed)} is in 1.5-1.8`] };\n },\n});\n\nexport const expertSdrModernDialect: TciDialect = new StandardTciDialect({\n id: 'expertsdr-1.9-2.0', label: 'ExpertSDR / TCI 1.9-2.0', streamLengthSemantics: 'scalar',\n supportsStreamChannels: true, supportsTxAudioSource: true, supportsIqStream: true,\n iqSampleRates: [48_000, 96_000, 192_000, 384_000], driveHasTrx: true,\n detect: (context) => {\n const parsed = version(context);\n if (!parsed || compareTciVersion(parsed, [1, 9]) < 0) return { score: 0, evidence: [] };\n const future = compareTciVersion(parsed, [2, 0]) > 0;\n return {\n score: 70 + (programIncludes(context, 'expert') ? 15 : 0),\n evidence: [`protocol ${formatVersion(parsed)} uses modern stream negotiation`],\n warnings: future ? [`Unknown future TCI version ${formatVersion(parsed)}; using the modern dialect`] : [],\n };\n },\n});\n\nexport const thetisDialect: TciDialect = new StandardTciDialect({\n id: 'thetis-2.0', label: 'Thetis / TCI 2.0', streamLengthSemantics: 'scalar',\n supportsStreamChannels: true, supportsTxAudioSource: true, supportsIqStream: true,\n iqSampleRates: [48_000, 96_000, 192_000, 384_000], driveHasTrx: true,\n detect: (context) => {\n const evidence: string[] = [];\n let score = 0;\n if (programIncludes(context, 'thetis')) { score += 120; evidence.push('PROTOCOL program is Thetis'); }\n const observed = ['tx_frequency_ex', 'tx_profiles_ex', 'tx_profile_ex', 'calibration_ex']\n .filter((name) => context.commandNames.has(name));\n if (observed.length > 0) { score += 100; evidence.push(`Thetis extension commands: ${observed.join(', ')}`); }\n if (/anan|hermes|orion|saturn/i.test(context.identity.device ?? '')) {\n score += 30;\n evidence.push(`Thetis-family device: ${context.identity.device}`);\n }\n return { score, evidence };\n },\n});\n\nexport const aetherSdrDialect: TciDialect = new StandardTciDialect({\n id: 'aethersdr-1.5', label: 'AetherSDR / TCI 1.5 hybrid', streamLengthSemantics: 'scalar',\n supportsStreamChannels: true, supportsTxAudioSource: true, supportsIqStream: true,\n iqSampleRates: [24_000, 48_000, 96_000, 192_000], driveHasTrx: true,\n detect: (context) => {\n if (!/^aethersdr$/i.test(context.identity.device ?? '')) return { score: 0, evidence: [] };\n const evidence = [`AetherSDR device identity: ${context.identity.device}`];\n const modernAudioCommands = ['audio_stream_sample_type', 'audio_stream_channels', 'audio_stream_samples']\n .filter((name) => context.commandNames.has(name));\n if (modernAudioCommands.length > 0) evidence.push(`Modern audio negotiation: ${modernAudioCommands.join(', ')}`);\n return {\n score: 150,\n evidence,\n warnings: context.identity.protocolVersion === '1.5'\n ? ['AetherSDR reports TCI 1.5 but uses modern scalar audio stream semantics']\n : [],\n };\n },\n});\n\nexport const genericObservedDialect: TciDialect = new StandardTciDialect({\n id: 'generic-observed', label: 'Generic observed TCI', streamLengthSemantics: 'auto',\n supportsStreamChannels: true, supportsTxAudioSource: true, supportsIqStream: false,\n iqSampleRates: [], driveHasTrx: true,\n detect: (context) => ({\n score: context.commandNames.has('ready') ? 10 : 0,\n evidence: ['No vendor-specific match; using observed command shapes'],\n warnings: ['Dialect identity is uncertain'],\n }),\n resolve: (context) => {\n const drive = [...context.commands].reverse().find((command) => command.name === 'drive');\n const driveHasTrx = (drive?.args.length ?? 0) >= 2;\n return new StandardTciDialect({\n id: 'generic-observed',\n label: 'Generic observed TCI',\n streamLengthSemantics: 'auto',\n supportsStreamChannels: context.commandNames.has('audio_stream_channels'),\n supportsTxAudioSource: compareTciVersion(version(context) ?? [0], [2, 0]) >= 0,\n supportsIqStream: context.commandNames.has('iq_samplerate'),\n iqSampleRates: context.commandNames.has('iq_samplerate') ? [48_000] : [],\n driveHasTrx,\n detect: genericObservedDialect.detect.bind(genericObservedDialect),\n });\n },\n});\n\nexport const builtInDialects: readonly TciDialect[] = [\n aetherSdrDialect, thetisDialect, expertSdr14Dialect, expertSdrLegacyDialect, expertSdrModernDialect, genericObservedDialect,\n];\n\nexport function parseTciVersion(value: string | undefined): VersionTuple | undefined {\n if (!value) return undefined;\n const match = value.trim().match(/^(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?/);\n if (!match) return undefined;\n return match.slice(1).filter((part): part is string => part !== undefined).map(Number);\n}\n\nexport function compareTciVersion(left: VersionTuple, right: VersionTuple): number {\n const length = Math.max(left.length, right.length);\n for (let index = 0; index < length; index += 1) {\n const difference = (left[index] ?? 0) - (right[index] ?? 0);\n if (difference !== 0) return difference;\n }\n return 0;\n}\n\nfunction formatVersion(value: VersionTuple): string { return value.join('.'); }\n","export type TciErrorCode =\n | 'connect-timeout'\n | 'handshake-timeout'\n | 'invalid-handshake'\n | 'unknown-dialect'\n | 'command-timeout'\n | 'not-connected'\n | 'disconnected'\n | 'protocol-error'\n | 'invalid-frame'\n | 'cancelled';\n\nexport class TciError extends Error {\n readonly code: TciErrorCode;\n readonly details?: unknown;\n\n constructor(code: TciErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = 'TciError';\n this.code = code;\n this.details = details;\n }\n}\n\nexport function toTciError(error: unknown, fallbackCode: TciErrorCode = 'protocol-error'): TciError {\n if (error instanceof TciError) {\n return error;\n }\n if (error instanceof Error) {\n return new TciError(fallbackCode, error.message, error);\n }\n return new TciError(fallbackCode, String(error), error);\n}\n","import { TciError } from '../errors.js';\nimport { builtInDialects } from './builtins.js';\nimport type {\n TciDialect,\n TciDialectDetection,\n TciDialectDetectionContext,\n TciDialectId,\n TciDialectSelection,\n} from './types.js';\n\nexport class TciDialectRegistry {\n private readonly dialects = new Map<TciDialectId, TciDialect>();\n\n constructor(dialects: readonly TciDialect[] = builtInDialects) {\n for (const dialect of dialects) this.register(dialect);\n }\n\n register(dialect: TciDialect): void { this.dialects.set(dialect.id, dialect); }\n get(id: TciDialectId): TciDialect | undefined { return this.dialects.get(id); }\n list(): TciDialect[] { return [...this.dialects.values()]; }\n\n select(context: TciDialectDetectionContext, selection: TciDialectSelection = 'auto'): TciDialectDetection {\n if (typeof selection === 'object') {\n return { dialect: selection, confidence: 'manual', evidence: ['Custom dialect supplied by caller'], warnings: [] };\n }\n if (selection !== 'auto') {\n const dialect = this.get(selection);\n if (!dialect) throw new TciError('unknown-dialect', `Unknown TCI dialect: ${selection}`);\n return { dialect: dialect.resolve?.(context) ?? dialect, confidence: 'manual', evidence: [`Dialect ${selection} selected by caller`], warnings: [] };\n }\n\n const candidates = this.list()\n .map((dialect) => ({ dialect, result: dialect.detect(context) }))\n .sort((left, right) => right.result.score - left.result.score);\n const selected = candidates[0];\n if (!selected || selected.result.score <= 0) {\n throw new TciError('unknown-dialect', 'Unable to identify the TCI server dialect');\n }\n return {\n dialect: selected.dialect.resolve?.(context) ?? selected.dialect,\n confidence: selected.result.score >= 100 ? 'high' : selected.result.score >= 70 ? 'medium' : 'low',\n evidence: selected.result.evidence,\n warnings: selected.result.warnings ?? [],\n };\n }\n}\n\nexport const defaultTciDialectRegistry = new TciDialectRegistry();\n","import { TciError } from '../errors.js';\nimport type { TciCommand } from '../protocol/text.js';\nimport type { TciProtocolIdentity } from './types.js';\n\nconst IDENTITY_COMMANDS = new Set(['protocol', 'device', 'trx_count', 'channels_count', 'channel_count']);\nconst STATE_COMMANDS = new Set(['vfo', 'modulation', 'modulations_list', 'trx', 'drive']);\n\nexport function parseProtocolIdentity(commands: readonly TciCommand[]): TciProtocolIdentity {\n const protocol = [...commands].reverse().find((command) => command.name === 'protocol');\n const device = [...commands].reverse().find((command) => command.name === 'device');\n const rawProtocolArgs = protocol?.args ?? [];\n const firstLooksLikeVersion = /^\\d+(?:\\.\\d+){0,2}/.test(rawProtocolArgs[0] ?? '');\n return {\n programName: firstLooksLikeVersion ? undefined : rawProtocolArgs[0],\n protocolVersion: firstLooksLikeVersion ? rawProtocolArgs[0] : rawProtocolArgs[1],\n rawProtocolArgs: [...rawProtocolArgs],\n device: device?.args.join(','),\n };\n}\n\nexport function assertValidTciHandshake(commands: readonly TciCommand[]): void {\n const names = new Set(commands.map((command) => command.name));\n if (!names.has('ready')) throw new TciError('handshake-timeout', 'TCI READY was not received');\n const categories = [\n [...IDENTITY_COMMANDS].some((name) => names.has(name)),\n [...STATE_COMMANDS].some((name) => names.has(name)),\n ].filter(Boolean).length;\n const identitySignals = [...IDENTITY_COMMANDS].filter((name) => names.has(name)).length;\n if (categories < 2 && identitySignals < 2) {\n throw new TciError('invalid-handshake', 'WebSocket opened but did not provide enough TCI initialization evidence');\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/dialect/index.ts","../../src/meter/types.ts","../../src/meter/adapters.ts","../../src/meter/TciMeterStreamSession.ts","../../src/errors.ts","../../src/dialect/builtins.ts","../../src/dialect/registry.ts","../../src/dialect/handshake.ts"],"sourcesContent":["export * from './types.js';\nexport * from './builtins.js';\nexport * from './registry.js';\nexport * from './handshake.js';\n","import type { TciCommand } from '../protocol/index.js';\n\nexport type TciMeterSupport =\n | 'unknown'\n | 'declared'\n | 'acknowledged'\n | 'observed'\n | 'unsupported';\n\nexport interface TciMeterCapabilities {\n rxLevel: TciMeterSupport;\n rxAverageLevel: TciMeterSupport;\n rxPeakBin: TciMeterSupport;\n txMicLevel: TciMeterSupport;\n txRmsPower: TciMeterSupport;\n txPeakPower: TciMeterSupport;\n txSwr: TciMeterSupport;\n txAlcDbfs: TciMeterSupport;\n}\n\nexport type TciRxMeterSource = 'rx_sensors' | 'rx_channel_sensors' | 'rx_channel_sensors_ex';\n\nexport interface TciRxMeterFrame {\n receiver: number;\n channel: number;\n levelDbm: number;\n averageLevelDbm?: number;\n peakBinDbm?: number;\n source: TciRxMeterSource;\n receivedAtMs: number;\n extraArgs?: readonly string[];\n}\n\nexport interface TciTxMeterFrame {\n trx: number;\n micLevelDbm?: number;\n rmsPowerWatts?: number;\n peakPowerWatts?: number;\n swr?: number;\n alc?: { value: number; unit: 'dbfs' | 'percent' };\n receivedAtMs: number;\n extraArgs?: readonly string[];\n}\n\nexport interface TciMeterStreamOptions {\n receiver?: number;\n channel?: number;\n trx?: number;\n rx?: boolean;\n tx?: boolean;\n intervalMs?: number;\n}\n\nexport interface TciMeterInterval {\n requestedMs: number;\n appliedMs?: number;\n}\n\nexport interface TciMeterCommand {\n name: string;\n args: readonly unknown[];\n}\n\nexport type TciMeterDecodedFrame =\n | { kind: 'rx'; frame: TciRxMeterFrame }\n | { kind: 'tx'; frame: TciTxMeterFrame };\n\nexport interface TciMeterDecodeResult {\n decoded?: TciMeterDecodedFrame;\n issue?: string;\n}\n\nexport interface TciMeterAdapter {\n readonly declaredCapabilities: TciMeterCapabilities;\n normalizeInterval(intervalMs: number): TciMeterInterval;\n buildEnableCommand(kind: 'rx' | 'tx', enabled: boolean, intervalMs: number): TciMeterCommand;\n decode(command: TciCommand, receivedAtMs: number): TciMeterDecodeResult | undefined;\n}\n\nexport const UNKNOWN_TCI_METER_CAPABILITIES: TciMeterCapabilities = {\n rxLevel: 'unknown',\n rxAverageLevel: 'unknown',\n rxPeakBin: 'unknown',\n txMicLevel: 'unknown',\n txRmsPower: 'unknown',\n txPeakPower: 'unknown',\n txSwr: 'unknown',\n txAlcDbfs: 'unknown',\n};\n\nexport function cloneMeterCapabilities(capabilities: TciMeterCapabilities): TciMeterCapabilities {\n return { ...capabilities };\n}\n","import type { TciCommand } from '../protocol/index.js';\nimport {\n cloneMeterCapabilities,\n UNKNOWN_TCI_METER_CAPABILITIES,\n type TciMeterAdapter,\n type TciMeterCapabilities,\n type TciMeterDecodeResult,\n type TciMeterInterval,\n type TciRxMeterFrame,\n type TciTxMeterFrame,\n} from './types.js';\n\ninterface StandardTciMeterAdapterOptions {\n capabilities?: Partial<TciMeterCapabilities>;\n interval?: { minMs?: number; maxMs?: number; fixedMs?: number; reportsApplied?: boolean };\n supportsRxExtended?: boolean;\n txAlcUnit?: 'dbfs' | 'percent';\n}\n\nexport class StandardTciMeterAdapter implements TciMeterAdapter {\n readonly declaredCapabilities: TciMeterCapabilities;\n private readonly options: StandardTciMeterAdapterOptions;\n\n constructor(options: StandardTciMeterAdapterOptions = {}) {\n this.options = options;\n this.declaredCapabilities = {\n ...UNKNOWN_TCI_METER_CAPABILITIES,\n rxLevel: 'declared',\n txMicLevel: 'declared',\n txRmsPower: 'declared',\n txPeakPower: 'declared',\n txSwr: 'declared',\n rxAverageLevel: options.supportsRxExtended ? 'declared' : 'unknown',\n rxPeakBin: options.supportsRxExtended ? 'declared' : 'unknown',\n txAlcDbfs: options.txAlcUnit === 'dbfs' ? 'declared' : 'unknown',\n ...options.capabilities,\n };\n }\n\n normalizeInterval(intervalMs: number): TciMeterInterval {\n const requestedMs = Math.round(intervalMs);\n if (this.options.interval?.fixedMs !== undefined) {\n return { requestedMs, appliedMs: this.options.interval.fixedMs };\n }\n const minMs = this.options.interval?.minMs ?? requestedMs;\n const maxMs = this.options.interval?.maxMs ?? requestedMs;\n const normalized = Math.max(minMs, Math.min(maxMs, requestedMs));\n return {\n requestedMs,\n appliedMs: this.options.interval?.reportsApplied === false ? undefined : normalized,\n };\n }\n\n buildEnableCommand(kind: 'rx' | 'tx', enabled: boolean, intervalMs: number) {\n return {\n name: kind === 'rx' ? 'RX_SENSORS_ENABLE' : 'TX_SENSORS_ENABLE',\n args: enabled ? [true, intervalMs] : [false],\n };\n }\n\n decode(command: TciCommand, receivedAtMs: number): TciMeterDecodeResult | undefined {\n switch (command.name) {\n case 'rx_sensors':\n return decodeRx(command, receivedAtMs, 'rx_sensors');\n case 'rx_channel_sensors':\n return decodeRx(command, receivedAtMs, 'rx_channel_sensors');\n case 'rx_channel_sensors_ex':\n return decodeRx(command, receivedAtMs, 'rx_channel_sensors_ex');\n case 'tx_sensors':\n return decodeTx(command, receivedAtMs, this.options.txAlcUnit);\n default:\n return undefined;\n }\n }\n}\n\nexport function createUnknownTciMeterAdapter(): TciMeterAdapter {\n return new StandardTciMeterAdapter({\n capabilities: cloneMeterCapabilities(UNKNOWN_TCI_METER_CAPABILITIES),\n interval: { reportsApplied: false },\n });\n}\n\nfunction decodeRx(\n command: TciCommand,\n receivedAtMs: number,\n source: TciRxMeterFrame['source'],\n): TciMeterDecodeResult {\n const receiver = integer(command.args[0]);\n const hasChannel = source !== 'rx_sensors';\n const channel = hasChannel ? integer(command.args[1]) : 0;\n const levelIndex = hasChannel ? 2 : 1;\n const levelDbm = finite(command.args[levelIndex]);\n if (receiver === undefined || receiver < 0 || channel === undefined || channel < 0 || levelDbm === undefined) {\n return { issue: `Invalid ${command.originalName} meter frame: ${command.raw}` };\n }\n\n const frame: TciRxMeterFrame = { receiver, channel, levelDbm, source, receivedAtMs };\n if (source === 'rx_channel_sensors_ex') {\n const averageLevelDbm = finite(command.args[3]);\n const peakBinDbm = finite(command.args[4]);\n if (averageLevelDbm === undefined || peakBinDbm === undefined) {\n return { issue: `Invalid ${command.originalName} extended meter frame: ${command.raw}` };\n }\n frame.averageLevelDbm = averageLevelDbm;\n frame.peakBinDbm = peakBinDbm;\n if (command.args.length > 5) frame.extraArgs = command.args.slice(5);\n } else if (command.args.length > levelIndex + 1) {\n frame.extraArgs = command.args.slice(levelIndex + 1);\n }\n return { decoded: { kind: 'rx', frame } };\n}\n\nfunction decodeTx(\n command: TciCommand,\n receivedAtMs: number,\n alcUnit?: 'dbfs' | 'percent',\n): TciMeterDecodeResult {\n const trx = integer(command.args[0]);\n if (trx === undefined || trx < 0) {\n return { issue: `Invalid ${command.originalName} transmitter index: ${command.raw}` };\n }\n\n const micLevelDbm = optionalFinite(command.args[1]);\n const rmsPowerWatts = optionalFinite(command.args[2]);\n const peakPowerWatts = optionalFinite(command.args[3]);\n const swr = optionalFinite(command.args[4]);\n const alcValue = alcUnit ? optionalFinite(command.args[5]) : undefined;\n if (micLevelDbm.invalid || rmsPowerWatts.invalid || peakPowerWatts.invalid || swr.invalid || alcValue?.invalid) {\n return { issue: `Invalid ${command.originalName} numeric meter frame: ${command.raw}` };\n }\n if ((rmsPowerWatts.value !== undefined && rmsPowerWatts.value < 0)\n || (peakPowerWatts.value !== undefined && peakPowerWatts.value < 0)\n || (swr.value !== undefined && swr.value < 1)) {\n return { issue: `Out-of-range ${command.originalName} meter frame: ${command.raw}` };\n }\n if (micLevelDbm.value === undefined && rmsPowerWatts.value === undefined\n && peakPowerWatts.value === undefined && swr.value === undefined && alcValue?.value === undefined) {\n return { issue: `Empty ${command.originalName} meter frame: ${command.raw}` };\n }\n\n const frame: TciTxMeterFrame = {\n trx,\n micLevelDbm: micLevelDbm.value,\n rmsPowerWatts: rmsPowerWatts.value,\n peakPowerWatts: peakPowerWatts.value,\n swr: swr.value,\n receivedAtMs,\n };\n if (alcUnit && alcValue?.value !== undefined) frame.alc = { value: alcValue.value, unit: alcUnit };\n const knownArgs = alcUnit ? 6 : 5;\n if (command.args.length > knownArgs) frame.extraArgs = command.args.slice(knownArgs);\n return { decoded: { kind: 'tx', frame } };\n}\n\nfunction finite(value: string | undefined): number | undefined {\n if (value === undefined || value === '') return undefined;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction integer(value: string | undefined): number | undefined {\n const parsed = finite(value);\n return parsed !== undefined && Number.isInteger(parsed) ? parsed : undefined;\n}\n\nfunction optionalFinite(value: string | undefined): { value?: number; invalid: boolean } {\n if (value === undefined || value === '') return { invalid: false };\n const parsed = Number(value);\n return Number.isFinite(parsed) ? { value: parsed, invalid: false } : { invalid: true };\n}\n","import { EventEmitter } from 'eventemitter3';\nimport { TciError } from '../errors.js';\nimport type { TciCommand } from '../protocol/index.js';\nimport {\n cloneMeterCapabilities,\n type TciMeterAdapter,\n type TciMeterCapabilities,\n type TciMeterSupport,\n type TciRxMeterFrame,\n type TciTxMeterFrame,\n} from './types.js';\n\nconst RX_COALESCE_MS = 20;\n\nexport interface TciMeterStreamEvents {\n rxFrame: (frame: TciRxMeterFrame) => void;\n txFrame: (frame: TciTxMeterFrame) => void;\n capabilitiesChanged: (capabilities: TciMeterCapabilities) => void;\n error: (error: TciError) => void;\n closed: () => void;\n}\n\ninterface TciMeterStreamCallbacks {\n close: () => Promise<void>;\n}\n\ninterface PendingRxFrame {\n frame: TciRxMeterFrame;\n timer: NodeJS.Timeout;\n}\n\nexport class TciMeterStreamSession extends EventEmitter<TciMeterStreamEvents> {\n readonly receiver: number;\n readonly channel: number;\n readonly trx: number;\n readonly requestedIntervalMs: number;\n readonly appliedIntervalMs?: number;\n readonly rxEnabled: boolean;\n readonly txEnabled: boolean;\n\n private readonly adapter: TciMeterAdapter;\n private readonly callbacks: TciMeterStreamCallbacks;\n private capabilities: TciMeterCapabilities;\n private readonly pendingRx = new Map<string, PendingRxFrame>();\n private closed = false;\n\n constructor(options: {\n receiver: number;\n channel: number;\n trx: number;\n requestedIntervalMs: number;\n appliedIntervalMs?: number;\n rxEnabled: boolean;\n txEnabled: boolean;\n adapter: TciMeterAdapter;\n callbacks: TciMeterStreamCallbacks;\n }) {\n super();\n this.receiver = options.receiver;\n this.channel = options.channel;\n this.trx = options.trx;\n this.requestedIntervalMs = options.requestedIntervalMs;\n this.appliedIntervalMs = options.appliedIntervalMs;\n this.rxEnabled = options.rxEnabled;\n this.txEnabled = options.txEnabled;\n this.adapter = options.adapter;\n this.callbacks = options.callbacks;\n this.capabilities = cloneMeterCapabilities(options.adapter.declaredCapabilities);\n }\n\n getCapabilities(): TciMeterCapabilities {\n return cloneMeterCapabilities(this.capabilities);\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.clearPendingRx();\n try {\n await this.callbacks.close();\n } finally {\n this.emit('closed');\n this.removeAllListeners();\n }\n }\n\n _acceptCommand(command: TciCommand, receivedAtMs: number): void {\n if (this.closed) return;\n this.acceptEnableAcknowledgement(command);\n const result = this.adapter.decode(command, receivedAtMs);\n if (!result) return;\n if (result.issue) {\n this.emit('error', new TciError('protocol-error', result.issue));\n return;\n }\n if (result.decoded?.kind === 'rx') this.acceptRxFrame(result.decoded.frame);\n if (result.decoded?.kind === 'tx') this.acceptTxFrame(result.decoded.frame);\n }\n\n _fail(error: TciError): void {\n if (this.closed) return;\n this.closed = true;\n this.clearPendingRx();\n this.emit('error', error);\n this.emit('closed');\n this.removeAllListeners();\n }\n\n private acceptRxFrame(frame: TciRxMeterFrame): void {\n if (!this.rxEnabled || frame.receiver !== this.receiver || frame.channel !== this.channel) return;\n this.observe('rxLevel');\n if (frame.averageLevelDbm !== undefined) this.observe('rxAverageLevel');\n if (frame.peakBinDbm !== undefined) this.observe('rxPeakBin');\n\n const key = `${frame.receiver}:${frame.channel}`;\n const pending = this.pendingRx.get(key);\n if (pending) {\n const sameReading = Math.abs(pending.frame.levelDbm - frame.levelDbm) < 0.05;\n if (sameReading && rxPriority(frame) >= rxPriority(pending.frame)) {\n clearTimeout(pending.timer);\n this.pendingRx.delete(key);\n } else if (sameReading) {\n return;\n } else {\n this.flushRx(key, pending);\n }\n }\n const timer = setTimeout(() => {\n const current = this.pendingRx.get(key);\n if (current?.frame === frame) this.flushRx(key, current);\n }, RX_COALESCE_MS);\n this.pendingRx.set(key, { frame, timer });\n }\n\n private acceptTxFrame(frame: TciTxMeterFrame): void {\n if (!this.txEnabled || frame.trx !== this.trx) return;\n if (frame.micLevelDbm !== undefined) this.observe('txMicLevel');\n if (frame.rmsPowerWatts !== undefined) this.observe('txRmsPower');\n if (frame.peakPowerWatts !== undefined) this.observe('txPeakPower');\n if (frame.swr !== undefined) this.observe('txSwr');\n if (frame.alc?.unit === 'dbfs') this.observe('txAlcDbfs');\n this.emit('txFrame', frame);\n }\n\n private acceptEnableAcknowledgement(command: TciCommand): void {\n const enabled = command.args[0]?.toLowerCase();\n if (enabled !== 'true' && enabled !== '1' && enabled !== 'on') return;\n if (command.name === 'rx_sensors_enable' && this.rxEnabled) {\n this.acknowledge(['rxLevel']);\n }\n if (command.name === 'tx_sensors_enable' && this.txEnabled) {\n this.acknowledge(['txMicLevel', 'txRmsPower', 'txPeakPower', 'txSwr']);\n }\n }\n\n private acknowledge(keys: Array<keyof TciMeterCapabilities>): void {\n let changed = false;\n for (const key of keys) {\n if (supportRank(this.capabilities[key]) < supportRank('acknowledged')) {\n this.capabilities[key] = 'acknowledged';\n changed = true;\n }\n }\n if (changed) this.emit('capabilitiesChanged', this.getCapabilities());\n }\n\n private observe(key: keyof TciMeterCapabilities): void {\n if (this.capabilities[key] === 'observed') return;\n this.capabilities[key] = 'observed';\n this.emit('capabilitiesChanged', this.getCapabilities());\n }\n\n private flushRx(key: string, pending: PendingRxFrame): void {\n clearTimeout(pending.timer);\n if (this.pendingRx.get(key) === pending) this.pendingRx.delete(key);\n this.emit('rxFrame', pending.frame);\n }\n\n private clearPendingRx(): void {\n for (const pending of this.pendingRx.values()) clearTimeout(pending.timer);\n this.pendingRx.clear();\n }\n}\n\nfunction rxPriority(frame: TciRxMeterFrame): number {\n if (frame.source === 'rx_channel_sensors_ex') return 3;\n if (frame.source === 'rx_channel_sensors') return 2;\n return 1;\n}\n\nfunction supportRank(value: TciMeterSupport): number {\n switch (value) {\n case 'unsupported': return -1;\n case 'unknown': return 0;\n case 'declared': return 1;\n case 'acknowledged': return 2;\n case 'observed': return 3;\n }\n}\n","export type TciErrorCode =\n | 'connect-timeout'\n | 'handshake-timeout'\n | 'invalid-handshake'\n | 'unknown-dialect'\n | 'command-timeout'\n | 'not-connected'\n | 'disconnected'\n | 'protocol-error'\n | 'invalid-frame'\n | 'cancelled';\n\nexport class TciError extends Error {\n readonly code: TciErrorCode;\n readonly details?: unknown;\n\n constructor(code: TciErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = 'TciError';\n this.code = code;\n this.details = details;\n }\n}\n\nexport function toTciError(error: unknown, fallbackCode: TciErrorCode = 'protocol-error'): TciError {\n if (error instanceof TciError) {\n return error;\n }\n if (error instanceof Error) {\n return new TciError(fallbackCode, error.message, error);\n }\n return new TciError(fallbackCode, String(error), error);\n}\n","import type {\n TciDialect,\n TciDialectDetectionContext,\n TciDialectScore,\n TciDriveState,\n TciStreamLengthSemantics,\n} from './types.js';\nimport { StandardTciMeterAdapter, createUnknownTciMeterAdapter } from '../meter/index.js';\n\ntype VersionTuple = readonly number[];\n\ninterface StandardDialectOptions {\n id: TciDialect['id'];\n label: string;\n streamLengthSemantics: TciStreamLengthSemantics;\n supportsStreamChannels: boolean;\n supportsTxAudioSource: boolean;\n supportsIqStream: boolean;\n iqSampleRates: readonly number[];\n driveHasTrx: boolean;\n meterAdapter?: TciDialect['meterAdapter'];\n detect: (context: TciDialectDetectionContext) => TciDialectScore;\n resolve?: (context: TciDialectDetectionContext) => TciDialect;\n}\n\nclass StandardTciDialect implements TciDialect {\n readonly id: TciDialect['id'];\n readonly label: string;\n readonly streamLengthSemantics: TciStreamLengthSemantics;\n readonly supportsStreamChannels: boolean;\n readonly supportsTxAudioSource: boolean;\n readonly supportsIqStream: boolean;\n readonly iqSampleRates: readonly number[];\n readonly meterAdapter?: TciDialect['meterAdapter'];\n private readonly driveHasTrx: boolean;\n private readonly detector: StandardDialectOptions['detect'];\n private readonly resolver?: StandardDialectOptions['resolve'];\n\n constructor(options: StandardDialectOptions) {\n this.id = options.id;\n this.label = options.label;\n this.streamLengthSemantics = options.streamLengthSemantics;\n this.supportsStreamChannels = options.supportsStreamChannels;\n this.supportsTxAudioSource = options.supportsTxAudioSource;\n this.supportsIqStream = options.supportsIqStream;\n this.iqSampleRates = [...options.iqSampleRates];\n this.meterAdapter = options.meterAdapter;\n this.driveHasTrx = options.driveHasTrx;\n this.detector = options.detect;\n this.resolver = options.resolve;\n }\n\n detect(context: TciDialectDetectionContext): TciDialectScore {\n return this.detector(context);\n }\n\n resolve(context: TciDialectDetectionContext): TciDialect {\n return this.resolver?.(context) ?? this;\n }\n\n buildDriveSetArgs(trx: number, value: number): readonly unknown[] {\n return this.driveHasTrx ? [trx, value] : [value];\n }\n\n buildDriveReadArgs(trx: number): readonly unknown[] {\n return this.driveHasTrx ? [trx] : [];\n }\n\n parseDrive(args: readonly string[], defaultTrx: number): TciDriveState | undefined {\n return parseDriveState(args, defaultTrx, this.driveHasTrx);\n }\n\n buildTuneDriveSetArgs(trx: number, value: number): readonly unknown[] {\n return this.driveHasTrx ? [trx, value] : [value];\n }\n\n buildTuneDriveReadArgs(trx: number): readonly unknown[] {\n return this.driveHasTrx ? [trx] : [];\n }\n\n parseTuneDrive(args: readonly string[], defaultTrx: number): TciDriveState | undefined {\n return parseDriveState(args, defaultTrx, this.driveHasTrx);\n }\n}\n\nfunction parseDriveState(args: readonly string[], defaultTrx: number, hasTrx: boolean): TciDriveState | undefined {\n const trx = hasTrx ? Number(args[0]) : defaultTrx;\n const value = Number(args[hasTrx ? 1 : 0]);\n if (!Number.isInteger(trx) || !Number.isFinite(value)) return undefined;\n return { trx, value };\n}\n\nfunction version(context: TciDialectDetectionContext): VersionTuple | undefined {\n return parseTciVersion(context.identity.protocolVersion);\n}\n\nfunction programIncludes(context: TciDialectDetectionContext, value: string): boolean {\n return context.identity.programName?.toLowerCase().includes(value) ?? false;\n}\n\nexport const expertSdr14Dialect: TciDialect = new StandardTciDialect({\n id: 'expertsdr-1.4', label: 'ExpertSDR / TCI 1.4', streamLengthSemantics: 'per-channel',\n supportsStreamChannels: false, supportsTxAudioSource: false, supportsIqStream: true,\n iqSampleRates: [48_000, 96_000, 192_000, 384_000], driveHasTrx: false,\n meterAdapter: new StandardTciMeterAdapter({ interval: { reportsApplied: false } }),\n detect: (context) => {\n const parsed = version(context);\n if (!parsed || compareTciVersion(parsed, [1, 4]) > 0) return { score: 0, evidence: [] };\n return { score: 80 + (programIncludes(context, 'expert') ? 10 : 0), evidence: [`protocol ${formatVersion(parsed)} <= 1.4`] };\n },\n});\n\nexport const expertSdrLegacyDialect: TciDialect = new StandardTciDialect({\n id: 'expertsdr-1.5-1.8', label: 'ExpertSDR / TCI 1.5-1.8', streamLengthSemantics: 'per-channel',\n supportsStreamChannels: false, supportsTxAudioSource: false, supportsIqStream: true,\n iqSampleRates: [48_000, 96_000, 192_000, 384_000], driveHasTrx: true,\n meterAdapter: new StandardTciMeterAdapter({ interval: { reportsApplied: false } }),\n detect: (context) => {\n const parsed = version(context);\n if (!parsed || compareTciVersion(parsed, [1, 5]) < 0 || compareTciVersion(parsed, [1, 9]) >= 0) return { score: 0, evidence: [] };\n return { score: 80 + (programIncludes(context, 'expert') ? 10 : 0), evidence: [`protocol ${formatVersion(parsed)} is in 1.5-1.8`] };\n },\n});\n\nexport const expertSdrModernDialect: TciDialect = new StandardTciDialect({\n id: 'expertsdr-1.9-2.0', label: 'ExpertSDR / TCI 1.9-2.0', streamLengthSemantics: 'scalar',\n supportsStreamChannels: true, supportsTxAudioSource: true, supportsIqStream: true,\n iqSampleRates: [48_000, 96_000, 192_000, 384_000], driveHasTrx: true,\n meterAdapter: new StandardTciMeterAdapter({ interval: { reportsApplied: false } }),\n detect: (context) => {\n const parsed = version(context);\n if (!parsed || compareTciVersion(parsed, [1, 9]) < 0) return { score: 0, evidence: [] };\n const future = compareTciVersion(parsed, [2, 0]) > 0;\n return {\n score: 70 + (programIncludes(context, 'expert') ? 15 : 0),\n evidence: [`protocol ${formatVersion(parsed)} uses modern stream negotiation`],\n warnings: future ? [`Unknown future TCI version ${formatVersion(parsed)}; using the modern dialect`] : [],\n };\n },\n});\n\nexport const thetisDialect: TciDialect = new StandardTciDialect({\n id: 'thetis-2.0', label: 'Thetis / TCI 2.0', streamLengthSemantics: 'scalar',\n supportsStreamChannels: true, supportsTxAudioSource: true, supportsIqStream: true,\n iqSampleRates: [48_000, 96_000, 192_000, 384_000], driveHasTrx: true,\n meterAdapter: new StandardTciMeterAdapter({\n interval: { minMs: 30, maxMs: 1_000 },\n supportsRxExtended: true,\n }),\n detect: (context) => {\n const evidence: string[] = [];\n let score = 0;\n if (programIncludes(context, 'thetis')) { score += 120; evidence.push('PROTOCOL program is Thetis'); }\n const observed = ['tx_frequency_ex', 'tx_profiles_ex', 'tx_profile_ex', 'calibration_ex']\n .filter((name) => context.commandNames.has(name));\n if (observed.length > 0) { score += 100; evidence.push(`Thetis extension commands: ${observed.join(', ')}`); }\n if (/anan|hermes|orion|saturn/i.test(context.identity.device ?? '')) {\n score += 30;\n evidence.push(`Thetis-family device: ${context.identity.device}`);\n }\n return { score, evidence };\n },\n});\n\nexport const aetherSdrDialect: TciDialect = new StandardTciDialect({\n id: 'aethersdr-1.5', label: 'AetherSDR / TCI 1.5 hybrid', streamLengthSemantics: 'scalar',\n supportsStreamChannels: true, supportsTxAudioSource: true, supportsIqStream: true,\n iqSampleRates: [24_000, 48_000, 96_000, 192_000], driveHasTrx: true,\n meterAdapter: new StandardTciMeterAdapter({\n interval: { fixedMs: 200 },\n txAlcUnit: 'dbfs',\n }),\n detect: (context) => {\n if (!/^aethersdr$/i.test(context.identity.device ?? '')) return { score: 0, evidence: [] };\n const evidence = [`AetherSDR device identity: ${context.identity.device}`];\n const modernAudioCommands = ['audio_stream_sample_type', 'audio_stream_channels', 'audio_stream_samples']\n .filter((name) => context.commandNames.has(name));\n if (modernAudioCommands.length > 0) evidence.push(`Modern audio negotiation: ${modernAudioCommands.join(', ')}`);\n return {\n score: 150,\n evidence,\n warnings: context.identity.protocolVersion === '1.5'\n ? ['AetherSDR reports TCI 1.5 but uses modern scalar audio stream semantics']\n : [],\n };\n },\n});\n\nexport const genericObservedDialect: TciDialect = new StandardTciDialect({\n id: 'generic-observed', label: 'Generic observed TCI', streamLengthSemantics: 'auto',\n supportsStreamChannels: true, supportsTxAudioSource: true, supportsIqStream: false,\n iqSampleRates: [], driveHasTrx: true,\n meterAdapter: createUnknownTciMeterAdapter(),\n detect: (context) => ({\n score: context.commandNames.has('ready') ? 10 : 0,\n evidence: ['No vendor-specific match; using observed command shapes'],\n warnings: ['Dialect identity is uncertain'],\n }),\n resolve: (context) => {\n const drive = [...context.commands].reverse().find((command) => command.name === 'drive');\n const driveHasTrx = (drive?.args.length ?? 0) >= 2;\n return new StandardTciDialect({\n id: 'generic-observed',\n label: 'Generic observed TCI',\n streamLengthSemantics: 'auto',\n supportsStreamChannels: context.commandNames.has('audio_stream_channels'),\n supportsTxAudioSource: compareTciVersion(version(context) ?? [0], [2, 0]) >= 0,\n supportsIqStream: context.commandNames.has('iq_samplerate'),\n iqSampleRates: context.commandNames.has('iq_samplerate') ? [48_000] : [],\n driveHasTrx,\n meterAdapter: createUnknownTciMeterAdapter(),\n detect: genericObservedDialect.detect.bind(genericObservedDialect),\n });\n },\n});\n\nexport const builtInDialects: readonly TciDialect[] = [\n aetherSdrDialect, thetisDialect, expertSdr14Dialect, expertSdrLegacyDialect, expertSdrModernDialect, genericObservedDialect,\n];\n\nexport function parseTciVersion(value: string | undefined): VersionTuple | undefined {\n if (!value) return undefined;\n const match = value.trim().match(/^(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?/);\n if (!match) return undefined;\n return match.slice(1).filter((part): part is string => part !== undefined).map(Number);\n}\n\nexport function compareTciVersion(left: VersionTuple, right: VersionTuple): number {\n const length = Math.max(left.length, right.length);\n for (let index = 0; index < length; index += 1) {\n const difference = (left[index] ?? 0) - (right[index] ?? 0);\n if (difference !== 0) return difference;\n }\n return 0;\n}\n\nfunction formatVersion(value: VersionTuple): string { return value.join('.'); }\n","import { TciError } from '../errors.js';\nimport { builtInDialects } from './builtins.js';\nimport type {\n TciDialect,\n TciDialectDetection,\n TciDialectDetectionContext,\n TciDialectId,\n TciDialectSelection,\n} from './types.js';\n\nexport class TciDialectRegistry {\n private readonly dialects = new Map<TciDialectId, TciDialect>();\n\n constructor(dialects: readonly TciDialect[] = builtInDialects) {\n for (const dialect of dialects) this.register(dialect);\n }\n\n register(dialect: TciDialect): void { this.dialects.set(dialect.id, dialect); }\n get(id: TciDialectId): TciDialect | undefined { return this.dialects.get(id); }\n list(): TciDialect[] { return [...this.dialects.values()]; }\n\n select(context: TciDialectDetectionContext, selection: TciDialectSelection = 'auto'): TciDialectDetection {\n if (typeof selection === 'object') {\n return { dialect: selection, confidence: 'manual', evidence: ['Custom dialect supplied by caller'], warnings: [] };\n }\n if (selection !== 'auto') {\n const dialect = this.get(selection);\n if (!dialect) throw new TciError('unknown-dialect', `Unknown TCI dialect: ${selection}`);\n return { dialect: dialect.resolve?.(context) ?? dialect, confidence: 'manual', evidence: [`Dialect ${selection} selected by caller`], warnings: [] };\n }\n\n const candidates = this.list()\n .map((dialect) => ({ dialect, result: dialect.detect(context) }))\n .sort((left, right) => right.result.score - left.result.score);\n const selected = candidates[0];\n if (!selected || selected.result.score <= 0) {\n throw new TciError('unknown-dialect', 'Unable to identify the TCI server dialect');\n }\n return {\n dialect: selected.dialect.resolve?.(context) ?? selected.dialect,\n confidence: selected.result.score >= 100 ? 'high' : selected.result.score >= 70 ? 'medium' : 'low',\n evidence: selected.result.evidence,\n warnings: selected.result.warnings ?? [],\n };\n }\n}\n\nexport const defaultTciDialectRegistry = new TciDialectRegistry();\n","import { TciError } from '../errors.js';\nimport type { TciCommand } from '../protocol/text.js';\nimport type { TciProtocolIdentity } from './types.js';\n\nconst IDENTITY_COMMANDS = new Set(['protocol', 'device', 'trx_count', 'channels_count', 'channel_count']);\nconst STATE_COMMANDS = new Set(['vfo', 'modulation', 'modulations_list', 'trx', 'drive']);\n\nexport function parseProtocolIdentity(commands: readonly TciCommand[]): TciProtocolIdentity {\n const protocol = [...commands].reverse().find((command) => command.name === 'protocol');\n const device = [...commands].reverse().find((command) => command.name === 'device');\n const rawProtocolArgs = protocol?.args ?? [];\n const firstLooksLikeVersion = /^\\d+(?:\\.\\d+){0,2}/.test(rawProtocolArgs[0] ?? '');\n return {\n programName: firstLooksLikeVersion ? undefined : rawProtocolArgs[0],\n protocolVersion: firstLooksLikeVersion ? rawProtocolArgs[0] : rawProtocolArgs[1],\n rawProtocolArgs: [...rawProtocolArgs],\n device: device?.args.join(','),\n };\n}\n\nexport function assertValidTciHandshake(commands: readonly TciCommand[]): void {\n const names = new Set(commands.map((command) => command.name));\n if (!names.has('ready')) throw new TciError('handshake-timeout', 'TCI READY was not received');\n const categories = [\n [...IDENTITY_COMMANDS].some((name) => names.has(name)),\n [...STATE_COMMANDS].some((name) => names.has(name)),\n ].filter(Boolean).length;\n const identitySignals = [...IDENTITY_COMMANDS].filter((name) => names.has(name)).length;\n if (categories < 2 && identitySignals < 2) {\n throw new TciError('invalid-handshake', 'WebSocket opened but did not provide enough TCI initialization evidence');\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/dialect/index.d.cts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { d as TciDialect, a as TciDialectId, f as TciDialectDetectionContext, c as TciDialectSelection, e as TciDialectDetection, i as TciProtocolIdentity } from '../types-
|
|
2
|
-
export { B as BuiltInTciDialectId, g as TciDialectScore, h as TciDriveState, b as TciHandshakeResult, j as TciStreamLengthSemantics, T as TciWriteResult } from '../types-
|
|
1
|
+
import { d as TciDialect, a as TciDialectId, f as TciDialectDetectionContext, c as TciDialectSelection, e as TciDialectDetection, i as TciProtocolIdentity } from '../types-CYK_NOTz.cjs';
|
|
2
|
+
export { B as BuiltInTciDialectId, g as TciDialectScore, h as TciDriveState, b as TciHandshakeResult, j as TciStreamLengthSemantics, T as TciWriteResult } from '../types-CYK_NOTz.cjs';
|
|
3
3
|
import { T as TciCommand } from '../text-BwCWY1k1.cjs';
|
|
4
|
+
import '../types-C4MtLKoy.cjs';
|
|
4
5
|
|
|
5
6
|
type VersionTuple = readonly number[];
|
|
6
7
|
declare const expertSdr14Dialect: TciDialect;
|
package/dist/dialect/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { d as TciDialect, a as TciDialectId, f as TciDialectDetectionContext, c as TciDialectSelection, e as TciDialectDetection, i as TciProtocolIdentity } from '../types-
|
|
2
|
-
export { B as BuiltInTciDialectId, g as TciDialectScore, h as TciDriveState, b as TciHandshakeResult, j as TciStreamLengthSemantics, T as TciWriteResult } from '../types-
|
|
1
|
+
import { d as TciDialect, a as TciDialectId, f as TciDialectDetectionContext, c as TciDialectSelection, e as TciDialectDetection, i as TciProtocolIdentity } from '../types-DVIPU-VR.js';
|
|
2
|
+
export { B as BuiltInTciDialectId, g as TciDialectScore, h as TciDriveState, b as TciHandshakeResult, j as TciStreamLengthSemantics, T as TciWriteResult } from '../types-DVIPU-VR.js';
|
|
3
3
|
import { T as TciCommand } from '../text-BwCWY1k1.js';
|
|
4
|
+
import '../types-C0qVJVSO.js';
|
|
4
5
|
|
|
5
6
|
type VersionTuple = readonly number[];
|
|
6
7
|
declare const expertSdr14Dialect: TciDialect;
|