signalk-piper 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -8
- package/dist/config.d.ts +127 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +208 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/service.d.ts +142 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +596 -0
- package/dist/service.js.map +1 -0
- package/dist/wyoming.d.ts +29 -0
- package/dist/wyoming.d.ts.map +1 -0
- package/dist/wyoming.js +124 -0
- package/dist/wyoming.js.map +1 -0
- package/package.json +63 -5
- package/public/172.mjs +1 -0
- package/public/540.mjs +2 -0
- package/public/540.mjs.LICENSE.txt +9 -0
- package/public/main.mjs +1 -0
- package/public/remoteEntry.js +1 -0
- package/index.js +0 -23
package/dist/service.js
ADDED
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PiperService — the plugin's whole runtime: managed container lifecycle,
|
|
3
|
+
* Wyoming describe readiness gate, periodic health loop, `wyoming-service`
|
|
4
|
+
* PropertyValues emission (with the family's emission discipline) and the
|
|
5
|
+
* plugin router endpoints.
|
|
6
|
+
*
|
|
7
|
+
* One instance lives for the whole server process (plugin routers cannot be
|
|
8
|
+
* deregistered); `start()`/`stop()` may run many times against it.
|
|
9
|
+
*/
|
|
10
|
+
import { ManagedContainer, errMsg, fetchWithTimeout, startSafely, } from "signalk-container-helper";
|
|
11
|
+
import { DOWNLOAD_HINT, IMAGE, buildContainerConfig, isLowSampleRateVoice, isSemverTag, resolveSettings, resolveTag, } from "./config.js";
|
|
12
|
+
import { describeService, isSupportedProtocolVersion } from "./wyoming.js";
|
|
13
|
+
export const PLUGIN_ID = "signalk-piper";
|
|
14
|
+
export const SERVICE_TYPE = "tts";
|
|
15
|
+
export const NOTIFICATION_PATH = "notifications.voice.piper";
|
|
16
|
+
/**
|
|
17
|
+
* Docker Hub tags listing feeding the config panel's version dropdown (same
|
|
18
|
+
* endpoint signalk-container's own dockerHubTags source uses; one page
|
|
19
|
+
* covers the dropdown's top-N plus the running-tag fallback).
|
|
20
|
+
*/
|
|
21
|
+
const TAGS_URL = `https://hub.docker.com/v2/repositories/${IMAGE}/tags/?page_size=25`;
|
|
22
|
+
export const DEFAULT_TIMING = {
|
|
23
|
+
gateIntervalMs: 2_000,
|
|
24
|
+
gateDeadlineMs: 600_000,
|
|
25
|
+
gateStatusEveryMs: 15_000,
|
|
26
|
+
describeTimeoutMs: 5_000,
|
|
27
|
+
healthIntervalMs: 30_000,
|
|
28
|
+
healthFailThreshold: 3,
|
|
29
|
+
emitDebounceMs: 500,
|
|
30
|
+
flapWindowMs: 60_000,
|
|
31
|
+
flapMaxTransitions: 10,
|
|
32
|
+
};
|
|
33
|
+
export class PiperService {
|
|
34
|
+
container;
|
|
35
|
+
settings = resolveSettings(undefined);
|
|
36
|
+
app;
|
|
37
|
+
timing;
|
|
38
|
+
running = false;
|
|
39
|
+
/** Increments per start() so stale async work from a prior run is inert. */
|
|
40
|
+
runId = 0;
|
|
41
|
+
uri = null;
|
|
42
|
+
lastInfo = null;
|
|
43
|
+
lastHealth = null;
|
|
44
|
+
consecutiveFailures = 0;
|
|
45
|
+
probeInFlight = false;
|
|
46
|
+
/** True while an update apply recreates the container (pauses probes). */
|
|
47
|
+
updating = false;
|
|
48
|
+
healthTimer = null;
|
|
49
|
+
// Emission discipline state (reset per run).
|
|
50
|
+
currentStatus = null;
|
|
51
|
+
lastEmittedStatus = null;
|
|
52
|
+
lastEmittedUri = null;
|
|
53
|
+
lastEmitAt = 0;
|
|
54
|
+
pendingEmitTimer = null;
|
|
55
|
+
transitionTimes = [];
|
|
56
|
+
flapSuppressed = false;
|
|
57
|
+
emissionsDisabled = false;
|
|
58
|
+
constructor(app, timing = {}) {
|
|
59
|
+
this.app = app;
|
|
60
|
+
this.timing = { ...DEFAULT_TIMING, ...timing };
|
|
61
|
+
this.container = new ManagedContainer({
|
|
62
|
+
app,
|
|
63
|
+
pluginId: PLUGIN_ID,
|
|
64
|
+
name: "piper",
|
|
65
|
+
image: IMAGE,
|
|
66
|
+
defaultTag: "auto",
|
|
67
|
+
resolveTag,
|
|
68
|
+
// Reads this.settings so the one process-lifetime container instance
|
|
69
|
+
// follows config changes; still pure per (settings, tag).
|
|
70
|
+
buildConfig: (tag) => buildContainerConfig(this.settings, tag),
|
|
71
|
+
updates: {
|
|
72
|
+
versionSource: { dockerHubTags: IMAGE, filter: isSemverTag },
|
|
73
|
+
},
|
|
74
|
+
// No `readiness`: it is HTTP-only and Wyoming is raw TCP. Readiness is
|
|
75
|
+
// our own describe gate below.
|
|
76
|
+
ensureOptions: {
|
|
77
|
+
healthCheck: () => this.probeOk(),
|
|
78
|
+
onUnhealthy: (name, err) => {
|
|
79
|
+
this.app.debug(`signalk-container reports ${name} unhealthy${err ? `: ${errMsg(err)}` : ""}`);
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
get isRunning() {
|
|
85
|
+
return this.running;
|
|
86
|
+
}
|
|
87
|
+
/** Called from the synchronous plugin start(). */
|
|
88
|
+
start(rawConfig) {
|
|
89
|
+
this.settings = resolveSettings(rawConfig);
|
|
90
|
+
this.running = true;
|
|
91
|
+
this.runId += 1;
|
|
92
|
+
this.uri = null;
|
|
93
|
+
this.lastInfo = null;
|
|
94
|
+
this.lastHealth = null;
|
|
95
|
+
this.consecutiveFailures = 0;
|
|
96
|
+
this.currentStatus = null;
|
|
97
|
+
this.lastEmittedStatus = null;
|
|
98
|
+
this.lastEmittedUri = null;
|
|
99
|
+
this.lastEmitAt = 0;
|
|
100
|
+
this.transitionTimes = [];
|
|
101
|
+
this.flapSuppressed = false;
|
|
102
|
+
this.emissionsDisabled = false;
|
|
103
|
+
const runId = this.runId;
|
|
104
|
+
startSafely(this.app, async () => {
|
|
105
|
+
await this.startAsync(runId);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/** Async server stop() — awaited by Signal K before a restart. */
|
|
109
|
+
async stop() {
|
|
110
|
+
this.running = false;
|
|
111
|
+
this.runId += 1;
|
|
112
|
+
this.stopHealthLoop();
|
|
113
|
+
try {
|
|
114
|
+
this.emit("stopped");
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
this.app.debug(`stopped emission failed: ${errMsg(err)}`);
|
|
118
|
+
}
|
|
119
|
+
if (this.pendingEmitTimer !== null) {
|
|
120
|
+
clearTimeout(this.pendingEmitTimer);
|
|
121
|
+
this.pendingEmitTimer = null;
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
await this.container.stop();
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
this.app.debug(`container stop failed: ${errMsg(err)}`);
|
|
128
|
+
}
|
|
129
|
+
this.app.setPluginStatus("Stopped");
|
|
130
|
+
}
|
|
131
|
+
// ---------------------------------------------------------------------
|
|
132
|
+
// Startup flow
|
|
133
|
+
// ---------------------------------------------------------------------
|
|
134
|
+
async startAsync(runId) {
|
|
135
|
+
const { tag } = await this.container.start(this.settings.imageTag);
|
|
136
|
+
if (!this.active(runId)) {
|
|
137
|
+
// stop() ran while container.start() was still pulling/creating; its
|
|
138
|
+
// container.stop() was a no-op then, so the container may now be
|
|
139
|
+
// running with nobody monitoring it. A successor run owns the
|
|
140
|
+
// container when the plugin was merely restarted — only clean up when
|
|
141
|
+
// the plugin is actually stopped.
|
|
142
|
+
if (!this.running) {
|
|
143
|
+
try {
|
|
144
|
+
await this.container.stop();
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
this.app.debug(`stopping the superseded container failed: ${errMsg(err)}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
this.app.setPluginStatus(`Starting ${IMAGE}:${tag} — first start downloads ${DOWNLOAD_HINT}`);
|
|
153
|
+
await this.becomeReady(tag, runId);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Resolve the service address, gate on Wyoming `describe`, go ready and
|
|
157
|
+
* start the health loop. Shared by first start and the post-update resume.
|
|
158
|
+
* A gate failure after the uri is known is surfaced loudly (spec §3.3:
|
|
159
|
+
* wyoming-service 'error' + notification alarm) and the health loop still
|
|
160
|
+
* starts, so eventual recovery is detected without a manual restart.
|
|
161
|
+
*/
|
|
162
|
+
async becomeReady(tag, runId) {
|
|
163
|
+
const address = await this.resolveServiceAddress();
|
|
164
|
+
if (!this.active(runId))
|
|
165
|
+
return;
|
|
166
|
+
this.uri = `tcp://${address}`;
|
|
167
|
+
this.emit("starting");
|
|
168
|
+
try {
|
|
169
|
+
const ready = await this.describeGate(address, runId);
|
|
170
|
+
if (!this.active(runId) || ready === null)
|
|
171
|
+
return;
|
|
172
|
+
this.validateInfo(ready.info, ready.version);
|
|
173
|
+
this.validateVoice();
|
|
174
|
+
this.lastInfo = ready.info;
|
|
175
|
+
this.lastHealth = {
|
|
176
|
+
ok: true,
|
|
177
|
+
at: Date.now(),
|
|
178
|
+
latencyMs: ready.latencyMs,
|
|
179
|
+
};
|
|
180
|
+
this.consecutiveFailures = 0;
|
|
181
|
+
this.app.setPluginStatus(this.runningStatusLine(tag));
|
|
182
|
+
this.emit("ready");
|
|
183
|
+
this.notify("normal", "piper is ready");
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
if (!this.active(runId))
|
|
187
|
+
return;
|
|
188
|
+
const message = errMsg(err);
|
|
189
|
+
this.app.setPluginError(message);
|
|
190
|
+
this.lastHealth = { ok: false, at: Date.now(), latencyMs: null };
|
|
191
|
+
// At the fail threshold already: the next successful probe takes the
|
|
192
|
+
// health loop's recovery branch (ready emission + normal notification).
|
|
193
|
+
this.consecutiveFailures = this.timing.healthFailThreshold;
|
|
194
|
+
this.emit("error");
|
|
195
|
+
this.notify("alarm", message);
|
|
196
|
+
}
|
|
197
|
+
this.startHealthLoop(address, tag, runId);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* host:port of the Wyoming port. Default networking declares the port via
|
|
201
|
+
* signalkAccessiblePorts and must resolve; the explicit 0.0.0.0 publish
|
|
202
|
+
* ("bind") is not manager-resolvable, but is by definition reachable on
|
|
203
|
+
* the loopback of the host.
|
|
204
|
+
*/
|
|
205
|
+
async resolveServiceAddress() {
|
|
206
|
+
if (this.settings.advanced.bind === "0.0.0.0") {
|
|
207
|
+
return `127.0.0.1:${this.settings.port}`;
|
|
208
|
+
}
|
|
209
|
+
const address = await this.container.resolveAddress(this.settings.port);
|
|
210
|
+
if (address === null) {
|
|
211
|
+
throw new Error(`could not resolve the piper container address for port ${this.settings.port}`);
|
|
212
|
+
}
|
|
213
|
+
return address;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Poll Wyoming `describe` until the service answers with `info` — which
|
|
217
|
+
* proves the model is downloaded and the service is actually serving.
|
|
218
|
+
* Returns null when the run was superseded while waiting.
|
|
219
|
+
*/
|
|
220
|
+
async describeGate(address, runId) {
|
|
221
|
+
const { host, port } = splitAddress(address);
|
|
222
|
+
const startedAt = Date.now();
|
|
223
|
+
const deadline = startedAt + this.timing.gateDeadlineMs;
|
|
224
|
+
let lastProgressAt = startedAt;
|
|
225
|
+
let lastError;
|
|
226
|
+
for (;;) {
|
|
227
|
+
if (!this.active(runId))
|
|
228
|
+
return null;
|
|
229
|
+
try {
|
|
230
|
+
return await describeService(host, port, this.timing.describeTimeoutMs);
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
lastError = errMsg(err);
|
|
234
|
+
}
|
|
235
|
+
if (Date.now() >= deadline) {
|
|
236
|
+
throw new Error(`piper did not become ready within ${Math.round(this.timing.gateDeadlineMs / 1000)}s (last error: ${lastError ?? "none"})`);
|
|
237
|
+
}
|
|
238
|
+
if (Date.now() - lastProgressAt >= this.timing.gateStatusEveryMs) {
|
|
239
|
+
lastProgressAt = Date.now();
|
|
240
|
+
const elapsedS = Math.round((Date.now() - startedAt) / 1000);
|
|
241
|
+
this.app.setPluginStatus(`Starting — waiting for piper to answer (${elapsedS}s; first start downloads ${DOWNLOAD_HINT})`);
|
|
242
|
+
}
|
|
243
|
+
await sleep(this.timing.gateIntervalMs);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
validateInfo(info, version) {
|
|
247
|
+
if (version !== undefined && !isSupportedProtocolVersion(version)) {
|
|
248
|
+
this.app.error(`piper speaks Wyoming protocol ${version}, but this plugin targets 1.x — ` +
|
|
249
|
+
"continuing, but expect breakage until the plugin is updated");
|
|
250
|
+
}
|
|
251
|
+
const tts = Array.isArray(info.tts) ? info.tts : [];
|
|
252
|
+
if (tts.length === 0) {
|
|
253
|
+
this.app.error("piper's info response advertises no TTS programs — is the configured " +
|
|
254
|
+
"image really a Wyoming TTS service?");
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
let voices = 0;
|
|
258
|
+
for (const program of tts) {
|
|
259
|
+
if (isObject(program) && Array.isArray(program.voices)) {
|
|
260
|
+
voices += program.voices.length;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
this.app.debug(`piper is ready and advertises ${voices} voice(s)`);
|
|
264
|
+
}
|
|
265
|
+
/** Warn (loudly, once per start) about 16 kHz voices; never block. */
|
|
266
|
+
validateVoice() {
|
|
267
|
+
if (isLowSampleRateVoice(this.settings.voice)) {
|
|
268
|
+
this.app.error(`voice ${this.settings.voice} is a 16 kHz (-low/-x_low) voice; the ` +
|
|
269
|
+
"signalk-wyoming v1 family expects 22.05 kHz — use a -medium or " +
|
|
270
|
+
"-high voice");
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
runningStatusLine(tag) {
|
|
274
|
+
let line = `Running ${IMAGE}:${tag} at ${this.uri}`;
|
|
275
|
+
if (isLowSampleRateVoice(this.settings.voice)) {
|
|
276
|
+
line += ` — warning: voice ${this.settings.voice} is 16 kHz`;
|
|
277
|
+
}
|
|
278
|
+
return line;
|
|
279
|
+
}
|
|
280
|
+
// ---------------------------------------------------------------------
|
|
281
|
+
// Health loop
|
|
282
|
+
// ---------------------------------------------------------------------
|
|
283
|
+
startHealthLoop(address, tag, runId) {
|
|
284
|
+
this.stopHealthLoop();
|
|
285
|
+
this.healthTimer = setInterval(() => {
|
|
286
|
+
void this.healthProbe(address, tag, runId);
|
|
287
|
+
}, this.timing.healthIntervalMs);
|
|
288
|
+
}
|
|
289
|
+
stopHealthLoop() {
|
|
290
|
+
if (this.healthTimer !== null) {
|
|
291
|
+
clearInterval(this.healthTimer);
|
|
292
|
+
this.healthTimer = null;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
async healthProbe(address, tag, runId) {
|
|
296
|
+
if (!this.active(runId) || this.probeInFlight || this.updating)
|
|
297
|
+
return;
|
|
298
|
+
this.probeInFlight = true;
|
|
299
|
+
const { host, port } = splitAddress(address);
|
|
300
|
+
try {
|
|
301
|
+
const result = await describeService(host, port, this.timing.describeTimeoutMs);
|
|
302
|
+
if (!this.active(runId))
|
|
303
|
+
return;
|
|
304
|
+
this.lastHealth = {
|
|
305
|
+
ok: true,
|
|
306
|
+
at: Date.now(),
|
|
307
|
+
latencyMs: result.latencyMs,
|
|
308
|
+
};
|
|
309
|
+
this.lastInfo = result.info;
|
|
310
|
+
const wasDown = this.consecutiveFailures >= this.timing.healthFailThreshold;
|
|
311
|
+
this.consecutiveFailures = 0;
|
|
312
|
+
if (wasDown) {
|
|
313
|
+
this.app.setPluginStatus(this.runningStatusLine(tag));
|
|
314
|
+
this.emit("ready");
|
|
315
|
+
this.notify("normal", "piper recovered");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
catch (err) {
|
|
319
|
+
if (!this.active(runId))
|
|
320
|
+
return;
|
|
321
|
+
this.lastHealth = { ok: false, at: Date.now(), latencyMs: null };
|
|
322
|
+
this.consecutiveFailures += 1;
|
|
323
|
+
if (this.consecutiveFailures === this.timing.healthFailThreshold) {
|
|
324
|
+
const message = `piper is not answering at ${this.uri}: ${errMsg(err)}`;
|
|
325
|
+
this.app.setPluginError(message);
|
|
326
|
+
this.emit("error");
|
|
327
|
+
this.notify("alarm", message);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
finally {
|
|
331
|
+
this.probeInFlight = false;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/** Boolean describe probe for signalk-container's recurring healthCheck. */
|
|
335
|
+
async probeOk() {
|
|
336
|
+
if (!this.running || this.uri === null)
|
|
337
|
+
return true;
|
|
338
|
+
const { host, port } = splitAddress(this.uri.replace("tcp://", ""));
|
|
339
|
+
try {
|
|
340
|
+
await describeService(host, port, this.timing.describeTimeoutMs);
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
// ---------------------------------------------------------------------
|
|
348
|
+
// wyoming-service emission discipline (§3.1)
|
|
349
|
+
// ---------------------------------------------------------------------
|
|
350
|
+
/**
|
|
351
|
+
* Request an emission of `status`. Rules:
|
|
352
|
+
* - never emit the same (status, uri) pair twice in a row — a uri change
|
|
353
|
+
* (post-update port move) re-emits even when the status is unchanged;
|
|
354
|
+
* - at most one emission per emitDebounceMs (later requests collapse into
|
|
355
|
+
* the trailing timer, so a flap that settles back emits nothing);
|
|
356
|
+
* - >flapMaxTransitions transitions per flapWindowMs → warn and suppress;
|
|
357
|
+
* - omitted while the uri is unknown, except "stopped";
|
|
358
|
+
* - a throwing emitPropertyValue (server-wide PropertyValues cap) disables
|
|
359
|
+
* all further emissions for this run;
|
|
360
|
+
* - "stopped" flushes immediately (stop() must not race the debounce).
|
|
361
|
+
*/
|
|
362
|
+
emit(status) {
|
|
363
|
+
if (status !== this.currentStatus) {
|
|
364
|
+
this.currentStatus = status;
|
|
365
|
+
const now = Date.now();
|
|
366
|
+
this.transitionTimes.push(now);
|
|
367
|
+
this.transitionTimes = this.transitionTimes.filter((t) => now - t <= this.timing.flapWindowMs);
|
|
368
|
+
if (!this.flapSuppressed &&
|
|
369
|
+
this.transitionTimes.length > this.timing.flapMaxTransitions) {
|
|
370
|
+
this.flapSuppressed = true;
|
|
371
|
+
this.app.error(`wyoming-service status is flapping (>${this.timing.flapMaxTransitions} ` +
|
|
372
|
+
`transitions in ${this.timing.flapWindowMs / 1000}s) — suppressing ` +
|
|
373
|
+
"further emissions; health probes continue");
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
const flush = status === "stopped";
|
|
377
|
+
if (this.pendingEmitTimer !== null) {
|
|
378
|
+
if (!flush)
|
|
379
|
+
return; // the pending timer emits the latest status
|
|
380
|
+
clearTimeout(this.pendingEmitTimer);
|
|
381
|
+
this.pendingEmitTimer = null;
|
|
382
|
+
}
|
|
383
|
+
const wait = this.lastEmitAt + this.timing.emitDebounceMs - Date.now();
|
|
384
|
+
if (flush || wait <= 0) {
|
|
385
|
+
this.emitNow();
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
this.pendingEmitTimer = setTimeout(() => {
|
|
389
|
+
this.pendingEmitTimer = null;
|
|
390
|
+
this.emitNow();
|
|
391
|
+
}, wait);
|
|
392
|
+
}
|
|
393
|
+
emitNow() {
|
|
394
|
+
const status = this.currentStatus;
|
|
395
|
+
if (status === null)
|
|
396
|
+
return;
|
|
397
|
+
if (status === this.lastEmittedStatus && this.uri === this.lastEmittedUri)
|
|
398
|
+
return;
|
|
399
|
+
if (this.emissionsDisabled)
|
|
400
|
+
return;
|
|
401
|
+
if (this.flapSuppressed && status !== "stopped")
|
|
402
|
+
return;
|
|
403
|
+
if (this.uri === null && status !== "stopped")
|
|
404
|
+
return;
|
|
405
|
+
try {
|
|
406
|
+
this.app.emitPropertyValue("wyoming-service", {
|
|
407
|
+
plugin: PLUGIN_ID,
|
|
408
|
+
type: SERVICE_TYPE,
|
|
409
|
+
uri: this.uri,
|
|
410
|
+
status,
|
|
411
|
+
});
|
|
412
|
+
this.lastEmittedStatus = status;
|
|
413
|
+
this.lastEmittedUri = this.uri;
|
|
414
|
+
this.lastEmitAt = Date.now();
|
|
415
|
+
}
|
|
416
|
+
catch (err) {
|
|
417
|
+
this.emissionsDisabled = true;
|
|
418
|
+
this.app.error("emitPropertyValue threw (server-wide PropertyValues cap reached?) — " +
|
|
419
|
+
`disabling wyoming-service emissions for this run: ${errMsg(err)}`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
notify(state, message) {
|
|
423
|
+
try {
|
|
424
|
+
this.app.handleMessage(PLUGIN_ID, {
|
|
425
|
+
updates: [
|
|
426
|
+
{
|
|
427
|
+
values: [
|
|
428
|
+
{
|
|
429
|
+
path: NOTIFICATION_PATH,
|
|
430
|
+
value: { state, method: ["visual"], message },
|
|
431
|
+
},
|
|
432
|
+
],
|
|
433
|
+
},
|
|
434
|
+
],
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
catch (err) {
|
|
438
|
+
this.app.debug(`notification delta failed: ${errMsg(err)}`);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
// ---------------------------------------------------------------------
|
|
442
|
+
// Router
|
|
443
|
+
// ---------------------------------------------------------------------
|
|
444
|
+
/**
|
|
445
|
+
* The container was just recreated (and started) by an update apply: the
|
|
446
|
+
* host port can change and the new instance must answer `describe` before
|
|
447
|
+
* it is advertised again. Supersede the current run and re-run the
|
|
448
|
+
* readiness flow (resolve → gate → ready → health loop) against it.
|
|
449
|
+
*/
|
|
450
|
+
resumeAfterUpdate(resolvedTag) {
|
|
451
|
+
if (!this.running)
|
|
452
|
+
return;
|
|
453
|
+
this.runId += 1;
|
|
454
|
+
const runId = this.runId;
|
|
455
|
+
this.stopHealthLoop();
|
|
456
|
+
this.lastInfo = null;
|
|
457
|
+
this.lastHealth = null;
|
|
458
|
+
this.consecutiveFailures = 0;
|
|
459
|
+
startSafely(this.app, async () => {
|
|
460
|
+
this.app.setPluginStatus(`Starting ${IMAGE}:${resolvedTag} — waiting for piper after the update`);
|
|
461
|
+
await this.becomeReady(resolvedTag, runId);
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Mount plugin routes. Called once per server process (even while the
|
|
466
|
+
* plugin is disabled), so every handler checks the running flag.
|
|
467
|
+
*/
|
|
468
|
+
registerRoutes(router, rawOptionsForSave) {
|
|
469
|
+
// registerWithRouter outlives stop(): the update routes must refuse to
|
|
470
|
+
// act while the plugin is not running — a stray apply would otherwise
|
|
471
|
+
// start a container nobody monitors and clobber the saved configuration.
|
|
472
|
+
const guardRunning = (res) => {
|
|
473
|
+
if (this.running)
|
|
474
|
+
return true;
|
|
475
|
+
res.status(503).json({ error: "plugin is not running" });
|
|
476
|
+
return false;
|
|
477
|
+
};
|
|
478
|
+
const guarded = {
|
|
479
|
+
get: (path, handler) => router.get(path, (req, res) => {
|
|
480
|
+
if (!guardRunning(res))
|
|
481
|
+
return;
|
|
482
|
+
return handler(req, res);
|
|
483
|
+
}),
|
|
484
|
+
post: (path, handler) => router.post(path, async (req, res) => {
|
|
485
|
+
if (!guardRunning(res))
|
|
486
|
+
return;
|
|
487
|
+
// Pause health probes while the apply recreates the container so
|
|
488
|
+
// a slow image pull cannot fire a spurious offline alarm.
|
|
489
|
+
this.updating = true;
|
|
490
|
+
try {
|
|
491
|
+
return await handler(req, res);
|
|
492
|
+
}
|
|
493
|
+
finally {
|
|
494
|
+
this.updating = false;
|
|
495
|
+
}
|
|
496
|
+
}),
|
|
497
|
+
};
|
|
498
|
+
this.container.registerUpdateRoutes(guarded, {
|
|
499
|
+
onApplied: (requestedTag, resolvedTag) => {
|
|
500
|
+
const raw = rawOptionsForSave();
|
|
501
|
+
if (isObject(raw)) {
|
|
502
|
+
this.app.savePluginOptions({ ...raw, imageTag: requestedTag }, (err) => {
|
|
503
|
+
if (err) {
|
|
504
|
+
this.app.error(`failed to persist image tag: ${errMsg(err)}`);
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
// Never replace the whole saved configuration with just the tag.
|
|
510
|
+
this.app.error("not persisting the image tag: the saved plugin options are not " +
|
|
511
|
+
"known in this process");
|
|
512
|
+
}
|
|
513
|
+
this.settings = { ...this.settings, imageTag: requestedTag };
|
|
514
|
+
this.resumeAfterUpdate(resolvedTag);
|
|
515
|
+
},
|
|
516
|
+
});
|
|
517
|
+
// readonly when the server supports per-route access levels;
|
|
518
|
+
// admin-only otherwise (feature-detect).
|
|
519
|
+
const access = router
|
|
520
|
+
.access;
|
|
521
|
+
const statusRouter = typeof access === "function" ? access.call(router, "readonly") : router;
|
|
522
|
+
statusRouter.get("/api/status", (_req, res) => {
|
|
523
|
+
if (!this.running) {
|
|
524
|
+
res.status(503).json({ error: "plugin is not running" });
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
void this.container.getState().then((containerState) => {
|
|
528
|
+
res.json({
|
|
529
|
+
status: this.currentStatus ?? "starting",
|
|
530
|
+
uri: this.uri,
|
|
531
|
+
tag: this.container.lastStartedTag ?? this.settings.imageTag,
|
|
532
|
+
containerState,
|
|
533
|
+
lastHealth: this.lastHealth,
|
|
534
|
+
info: this.lastInfo,
|
|
535
|
+
});
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
// Version-dropdown feed for the config panel. Deliberately not guarded
|
|
539
|
+
// by the running flag: the operator picks a tag while the plugin is
|
|
540
|
+
// still disabled, and the route only reaches out on demand.
|
|
541
|
+
statusRouter.get("/api/versions", (_req, res) => {
|
|
542
|
+
void (async () => {
|
|
543
|
+
try {
|
|
544
|
+
const response = await fetchWithTimeout(TAGS_URL, {
|
|
545
|
+
timeoutMs: 10_000,
|
|
546
|
+
});
|
|
547
|
+
if (!response.ok) {
|
|
548
|
+
res
|
|
549
|
+
.status(502)
|
|
550
|
+
.json({ error: `Docker Hub answered HTTP ${response.status}` });
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
const body = (await response.json());
|
|
554
|
+
const versions = (Array.isArray(body.results) ? body.results : [])
|
|
555
|
+
.map((r) => (typeof r?.name === "string" ? r.name : ""))
|
|
556
|
+
.filter(isSemverTag)
|
|
557
|
+
.sort(compareSemverDesc)
|
|
558
|
+
.map((tag) => ({ tag }));
|
|
559
|
+
res.json({ versions });
|
|
560
|
+
}
|
|
561
|
+
catch (err) {
|
|
562
|
+
res.status(502).json({ error: errMsg(err) });
|
|
563
|
+
}
|
|
564
|
+
})();
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
// ---------------------------------------------------------------------
|
|
568
|
+
active(runId) {
|
|
569
|
+
return this.running && runId === this.runId;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
/** Numeric-descending compare for the plain x.y.z tags isSemverTag admits. */
|
|
573
|
+
function compareSemverDesc(a, b) {
|
|
574
|
+
const pa = a.split(".").map(Number);
|
|
575
|
+
const pb = b.split(".").map(Number);
|
|
576
|
+
for (let i = 0; i < 3; i += 1) {
|
|
577
|
+
const d = (pb[i] ?? 0) - (pa[i] ?? 0);
|
|
578
|
+
if (d !== 0)
|
|
579
|
+
return d;
|
|
580
|
+
}
|
|
581
|
+
return 0;
|
|
582
|
+
}
|
|
583
|
+
function splitAddress(address) {
|
|
584
|
+
const idx = address.lastIndexOf(":");
|
|
585
|
+
return {
|
|
586
|
+
host: address.slice(0, idx),
|
|
587
|
+
port: Number(address.slice(idx + 1)),
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
function isObject(v) {
|
|
591
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
592
|
+
}
|
|
593
|
+
function sleep(ms) {
|
|
594
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
595
|
+
}
|
|
596
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal self-contained Wyoming `describe` client.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately embedded (no runtime dependency on the signalk-wyoming
|
|
5
|
+
* orchestrator package): connects, writes a header-only `describe` event and
|
|
6
|
+
* incrementally decodes events until an `info` event arrives, then resolves
|
|
7
|
+
* with its parsed data.
|
|
8
|
+
*
|
|
9
|
+
* Wire format (rhasspy/wyoming): one JSON header line terminated by "\n"
|
|
10
|
+
* with optional `data_length`/`payload_length` byte counts, followed by that
|
|
11
|
+
* many bytes of JSON data and binary payload. Non-`info` events are consumed
|
|
12
|
+
* and skipped.
|
|
13
|
+
*/
|
|
14
|
+
export interface DescribeResult {
|
|
15
|
+
/** Parsed `info` event data (asr/tts/wake/... program lists). */
|
|
16
|
+
info: Record<string, unknown>;
|
|
17
|
+
/** Wyoming protocol version from the `info` header, when present. */
|
|
18
|
+
version?: string;
|
|
19
|
+
/** Round-trip time from connect to a complete `info` event. */
|
|
20
|
+
latencyMs: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Send `describe` to a Wyoming service and await its `info` response.
|
|
24
|
+
* Rejects on connection failure, malformed frames or timeout.
|
|
25
|
+
*/
|
|
26
|
+
export declare function describeService(host: string, port: number, timeoutMs?: number): Promise<DescribeResult>;
|
|
27
|
+
/** True when a Wyoming protocol version string is in the supported 1.x range. */
|
|
28
|
+
export declare function isSupportedProtocolVersion(version: string): boolean;
|
|
29
|
+
//# sourceMappingURL=wyoming.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wyoming.d.ts","sourceRoot":"","sources":["../src/wyoming.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,SAAqB,GAC7B,OAAO,CAAC,cAAc,CAAC,CA+FzB;AAED,iFAAiF;AACjF,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEnE"}
|