speechflow 1.4.4 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/README.md +273 -7
- package/etc/claude.md +70 -0
- package/etc/speechflow.png +0 -0
- package/etc/speechflow.yaml +29 -11
- package/etc/stx.conf +7 -0
- package/package.json +7 -6
- package/speechflow-cli/dst/speechflow-node-a2a-compressor-wt.d.ts +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-compressor-wt.js +155 -0
- package/speechflow-cli/dst/speechflow-node-a2a-compressor-wt.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-compressor.d.ts +15 -0
- package/speechflow-cli/dst/speechflow-node-a2a-compressor.js +287 -0
- package/speechflow-cli/dst/speechflow-node-a2a-compressor.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-dynamics-wt.d.ts +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-dynamics-wt.js +208 -0
- package/speechflow-cli/dst/speechflow-node-a2a-dynamics-wt.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-dynamics.d.ts +15 -0
- package/speechflow-cli/dst/speechflow-node-a2a-dynamics.js +312 -0
- package/speechflow-cli/dst/speechflow-node-a2a-dynamics.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-expander-wt.d.ts +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-expander-wt.js +161 -0
- package/speechflow-cli/dst/speechflow-node-a2a-expander-wt.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-expander.d.ts +13 -0
- package/speechflow-cli/dst/speechflow-node-a2a-expander.js +208 -0
- package/speechflow-cli/dst/speechflow-node-a2a-expander.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-ffmpeg.js +13 -3
- package/speechflow-cli/dst/speechflow-node-a2a-ffmpeg.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-filler.d.ts +14 -0
- package/speechflow-cli/dst/speechflow-node-a2a-filler.js +233 -0
- package/speechflow-cli/dst/speechflow-node-a2a-filler.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-gain.d.ts +12 -0
- package/speechflow-cli/dst/speechflow-node-a2a-gain.js +125 -0
- package/speechflow-cli/dst/speechflow-node-a2a-gain.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-gender.d.ts +0 -1
- package/speechflow-cli/dst/speechflow-node-a2a-gender.js +28 -12
- package/speechflow-cli/dst/speechflow-node-a2a-gender.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-meter.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-meter.js +35 -53
- package/speechflow-cli/dst/speechflow-node-a2a-meter.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-mute.js +2 -1
- package/speechflow-cli/dst/speechflow-node-a2a-mute.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-rnnoise-wt.d.ts +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-rnnoise-wt.js +55 -0
- package/speechflow-cli/dst/speechflow-node-a2a-rnnoise-wt.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-rnnoise.d.ts +14 -0
- package/speechflow-cli/dst/speechflow-node-a2a-rnnoise.js +184 -0
- package/speechflow-cli/dst/speechflow-node-a2a-rnnoise.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-speex.d.ts +14 -0
- package/speechflow-cli/dst/speechflow-node-a2a-speex.js +156 -0
- package/speechflow-cli/dst/speechflow-node-a2a-speex.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-vad.js +3 -3
- package/speechflow-cli/dst/speechflow-node-a2a-vad.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-wav.js +22 -17
- package/speechflow-cli/dst/speechflow-node-a2a-wav.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2t-awstranscribe.d.ts +18 -0
- package/speechflow-cli/dst/speechflow-node-a2t-awstranscribe.js +317 -0
- package/speechflow-cli/dst/speechflow-node-a2t-awstranscribe.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2t-deepgram.js +16 -33
- package/speechflow-cli/dst/speechflow-node-a2t-deepgram.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2t-openaitranscribe.d.ts +19 -0
- package/speechflow-cli/dst/speechflow-node-a2t-openaitranscribe.js +351 -0
- package/speechflow-cli/dst/speechflow-node-a2t-openaitranscribe.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-t2a-awspolly.d.ts +16 -0
- package/speechflow-cli/dst/speechflow-node-t2a-awspolly.js +171 -0
- package/speechflow-cli/dst/speechflow-node-t2a-awspolly.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.js +19 -14
- package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-kokoro.js +11 -6
- package/speechflow-cli/dst/speechflow-node-t2a-kokoro.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-awstranslate.d.ts +13 -0
- package/speechflow-cli/dst/speechflow-node-t2t-awstranslate.js +141 -0
- package/speechflow-cli/dst/speechflow-node-t2t-awstranslate.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-t2t-deepl.js +13 -15
- package/speechflow-cli/dst/speechflow-node-t2t-deepl.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-format.js +10 -15
- package/speechflow-cli/dst/speechflow-node-t2t-format.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-ollama.js +44 -31
- package/speechflow-cli/dst/speechflow-node-t2t-ollama.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-openai.js +44 -45
- package/speechflow-cli/dst/speechflow-node-t2t-openai.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-sentence.js +8 -8
- package/speechflow-cli/dst/speechflow-node-t2t-sentence.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-subtitle.js +10 -12
- package/speechflow-cli/dst/speechflow-node-t2t-subtitle.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-transformers.js +22 -27
- package/speechflow-cli/dst/speechflow-node-t2t-transformers.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-x2x-filter.d.ts +1 -0
- package/speechflow-cli/dst/speechflow-node-x2x-filter.js +50 -15
- package/speechflow-cli/dst/speechflow-node-x2x-filter.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-x2x-trace.js +17 -18
- package/speechflow-cli/dst/speechflow-node-x2x-trace.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-device.js +13 -21
- package/speechflow-cli/dst/speechflow-node-xio-device.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-mqtt.d.ts +1 -0
- package/speechflow-cli/dst/speechflow-node-xio-mqtt.js +22 -16
- package/speechflow-cli/dst/speechflow-node-xio-mqtt.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-websocket.js +19 -19
- package/speechflow-cli/dst/speechflow-node-xio-websocket.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node.d.ts +6 -3
- package/speechflow-cli/dst/speechflow-node.js +13 -2
- package/speechflow-cli/dst/speechflow-node.js.map +1 -1
- package/speechflow-cli/dst/speechflow-utils-audio-wt.d.ts +1 -0
- package/speechflow-cli/dst/speechflow-utils-audio-wt.js +124 -0
- package/speechflow-cli/dst/speechflow-utils-audio-wt.js.map +1 -0
- package/speechflow-cli/dst/speechflow-utils-audio.d.ts +13 -0
- package/speechflow-cli/dst/speechflow-utils-audio.js +137 -0
- package/speechflow-cli/dst/speechflow-utils-audio.js.map +1 -0
- package/speechflow-cli/dst/speechflow-utils.d.ts +18 -0
- package/speechflow-cli/dst/speechflow-utils.js +123 -35
- package/speechflow-cli/dst/speechflow-utils.js.map +1 -1
- package/speechflow-cli/dst/speechflow.js +114 -27
- package/speechflow-cli/dst/speechflow.js.map +1 -1
- package/speechflow-cli/etc/oxlint.jsonc +112 -11
- package/speechflow-cli/etc/stx.conf +2 -2
- package/speechflow-cli/etc/tsconfig.json +1 -1
- package/speechflow-cli/package.d/@shiguredo+rnnoise-wasm+2025.1.5.patch +25 -0
- package/speechflow-cli/package.json +102 -94
- package/speechflow-cli/src/lib.d.ts +24 -0
- package/speechflow-cli/src/speechflow-node-a2a-compressor-wt.ts +151 -0
- package/speechflow-cli/src/speechflow-node-a2a-compressor.ts +303 -0
- package/speechflow-cli/src/speechflow-node-a2a-expander-wt.ts +158 -0
- package/speechflow-cli/src/speechflow-node-a2a-expander.ts +212 -0
- package/speechflow-cli/src/speechflow-node-a2a-ffmpeg.ts +13 -3
- package/speechflow-cli/src/speechflow-node-a2a-filler.ts +223 -0
- package/speechflow-cli/src/speechflow-node-a2a-gain.ts +98 -0
- package/speechflow-cli/src/speechflow-node-a2a-gender.ts +31 -17
- package/speechflow-cli/src/speechflow-node-a2a-meter.ts +37 -56
- package/speechflow-cli/src/speechflow-node-a2a-mute.ts +3 -2
- package/speechflow-cli/src/speechflow-node-a2a-rnnoise-wt.ts +62 -0
- package/speechflow-cli/src/speechflow-node-a2a-rnnoise.ts +164 -0
- package/speechflow-cli/src/speechflow-node-a2a-speex.ts +137 -0
- package/speechflow-cli/src/speechflow-node-a2a-vad.ts +3 -3
- package/speechflow-cli/src/speechflow-node-a2a-wav.ts +20 -13
- package/speechflow-cli/src/speechflow-node-a2t-awstranscribe.ts +308 -0
- package/speechflow-cli/src/speechflow-node-a2t-deepgram.ts +16 -33
- package/speechflow-cli/src/speechflow-node-a2t-openaitranscribe.ts +337 -0
- package/speechflow-cli/src/speechflow-node-t2a-awspolly.ts +187 -0
- package/speechflow-cli/src/speechflow-node-t2a-elevenlabs.ts +19 -14
- package/speechflow-cli/src/speechflow-node-t2a-kokoro.ts +12 -7
- package/speechflow-cli/src/speechflow-node-t2t-awstranslate.ts +152 -0
- package/speechflow-cli/src/speechflow-node-t2t-deepl.ts +13 -15
- package/speechflow-cli/src/speechflow-node-t2t-format.ts +10 -15
- package/speechflow-cli/src/speechflow-node-t2t-ollama.ts +55 -42
- package/speechflow-cli/src/speechflow-node-t2t-openai.ts +58 -58
- package/speechflow-cli/src/speechflow-node-t2t-sentence.ts +10 -10
- package/speechflow-cli/src/speechflow-node-t2t-subtitle.ts +15 -16
- package/speechflow-cli/src/speechflow-node-t2t-transformers.ts +27 -32
- package/speechflow-cli/src/speechflow-node-x2x-filter.ts +20 -16
- package/speechflow-cli/src/speechflow-node-x2x-trace.ts +20 -19
- package/speechflow-cli/src/speechflow-node-xio-device.ts +15 -23
- package/speechflow-cli/src/speechflow-node-xio-mqtt.ts +23 -16
- package/speechflow-cli/src/speechflow-node-xio-websocket.ts +19 -19
- package/speechflow-cli/src/speechflow-node.ts +21 -8
- package/speechflow-cli/src/speechflow-utils-audio-wt.ts +172 -0
- package/speechflow-cli/src/speechflow-utils-audio.ts +147 -0
- package/speechflow-cli/src/speechflow-utils.ts +125 -32
- package/speechflow-cli/src/speechflow.ts +118 -30
- package/speechflow-ui-db/dst/index.css +1 -1
- package/speechflow-ui-db/dst/index.js +31 -31
- package/speechflow-ui-db/etc/eslint.mjs +0 -1
- package/speechflow-ui-db/etc/tsc-client.json +3 -3
- package/speechflow-ui-db/package.json +11 -10
- package/speechflow-ui-db/src/app.vue +96 -78
- package/speechflow-ui-st/dst/index.js +26 -26
- package/speechflow-ui-st/etc/eslint.mjs +0 -1
- package/speechflow-ui-st/etc/tsc-client.json +3 -3
- package/speechflow-ui-st/package.json +11 -10
- package/speechflow-ui-st/src/app.vue +5 -12
|
@@ -15,6 +15,7 @@ import Inert from "@hapi/inert"
|
|
|
15
15
|
import WebSocket from "ws"
|
|
16
16
|
import HAPIWebSocket from "hapi-plugin-websocket"
|
|
17
17
|
import HAPIHeader from "hapi-plugin-header"
|
|
18
|
+
import OSC from "osc-js"
|
|
18
19
|
|
|
19
20
|
/* external dependencies */
|
|
20
21
|
import { DateTime } from "luxon"
|
|
@@ -48,6 +49,7 @@ type wsPeerInfo = {
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
/* establish asynchronous environment */
|
|
52
|
+
let debug = false
|
|
51
53
|
;(async () => {
|
|
52
54
|
/* determine system paths */
|
|
53
55
|
const { dataDir } = syspath({
|
|
@@ -69,6 +71,7 @@ type wsPeerInfo = {
|
|
|
69
71
|
"[-p|--port <tcp-port>] " +
|
|
70
72
|
"[-C|--cache <directory>] " +
|
|
71
73
|
"[-d|--dashboard <type>:<id>:<name>[,...]] " +
|
|
74
|
+
"[-o|--osc <ip-address>:<udp-port> " +
|
|
72
75
|
"[-e|--expression <expression>] " +
|
|
73
76
|
"[-f|--file <file>] " +
|
|
74
77
|
"[-c|--config <id>@<yaml-config-file>] " +
|
|
@@ -136,6 +139,15 @@ type wsPeerInfo = {
|
|
|
136
139
|
default: "",
|
|
137
140
|
describe: "list of dashboard block types and names"
|
|
138
141
|
})
|
|
142
|
+
.option("o", {
|
|
143
|
+
alias: "osc",
|
|
144
|
+
type: "string",
|
|
145
|
+
array: false,
|
|
146
|
+
coerce,
|
|
147
|
+
nargs: 1,
|
|
148
|
+
default: "",
|
|
149
|
+
describe: "OSC/UDP endpoint to send dashboard information"
|
|
150
|
+
})
|
|
139
151
|
.option("e", {
|
|
140
152
|
alias: "expression",
|
|
141
153
|
type: "string",
|
|
@@ -186,17 +198,26 @@ type wsPeerInfo = {
|
|
|
186
198
|
logTime: true,
|
|
187
199
|
logPrefix: pkg.name
|
|
188
200
|
})
|
|
201
|
+
if (args.v.match(/^(?:info|debug)$/))
|
|
202
|
+
debug = true
|
|
189
203
|
|
|
190
204
|
/* catch uncaught exceptions */
|
|
191
205
|
process.on("uncaughtException", (err) => {
|
|
192
|
-
|
|
206
|
+
if (debug)
|
|
207
|
+
cli!.log("error", `uncaught exception: ${err.message}\n${err.stack}`)
|
|
208
|
+
else
|
|
209
|
+
cli!.log("error", `uncaught exception: ${err.message}`)
|
|
193
210
|
process.exit(1)
|
|
194
211
|
})
|
|
195
212
|
|
|
196
213
|
/* catch unhandled promise rejections */
|
|
197
214
|
process.on("unhandledRejection", (reason) => {
|
|
198
|
-
if (reason instanceof Error)
|
|
199
|
-
|
|
215
|
+
if (reason instanceof Error) {
|
|
216
|
+
if (debug)
|
|
217
|
+
cli!.log("error", `unhandled rejection: ${reason.message}\n${reason.stack}`)
|
|
218
|
+
else
|
|
219
|
+
cli!.log("error", `unhandled rejection: ${reason.message}`)
|
|
220
|
+
}
|
|
200
221
|
else
|
|
201
222
|
cli!.log("error", `unhandled rejection: ${reason}`)
|
|
202
223
|
process.exit(1)
|
|
@@ -206,7 +227,11 @@ type wsPeerInfo = {
|
|
|
206
227
|
cli.log("info", `starting SpeechFlow ${pkg["x-stdver"]} (${pkg["x-release"]})`)
|
|
207
228
|
|
|
208
229
|
/* load .env files */
|
|
209
|
-
const result = dotenvx.config({
|
|
230
|
+
const result = dotenvx.config({
|
|
231
|
+
encoding: "utf8",
|
|
232
|
+
ignore: [ "MISSING_ENV_FILE" ],
|
|
233
|
+
quiet: true
|
|
234
|
+
})
|
|
210
235
|
if (result?.parsed !== undefined)
|
|
211
236
|
for (const key of Object.keys(result.parsed))
|
|
212
237
|
cli.log("info", `loaded environment variable "${key}" from ".env" files`)
|
|
@@ -216,8 +241,10 @@ type wsPeerInfo = {
|
|
|
216
241
|
if (typeof args.e === "string" && args.e !== "") n++
|
|
217
242
|
if (typeof args.f === "string" && args.f !== "") n++
|
|
218
243
|
if (typeof args.c === "string" && args.c !== "") n++
|
|
219
|
-
if (n
|
|
220
|
-
throw new Error("
|
|
244
|
+
if (n === 0)
|
|
245
|
+
throw new Error("need at least one FlowLink specification source (use one of the options -e, -f or -c)")
|
|
246
|
+
else if (n !== 1)
|
|
247
|
+
throw new Error("cannot use more than one FlowLink specification source (use only one of the options -e, -f or -c)")
|
|
221
248
|
|
|
222
249
|
/* read configuration */
|
|
223
250
|
let config = ""
|
|
@@ -251,15 +278,25 @@ type wsPeerInfo = {
|
|
|
251
278
|
|
|
252
279
|
/* load internal SpeechFlow nodes */
|
|
253
280
|
const pkgsI = [
|
|
281
|
+
"./speechflow-node-a2a-compressor.js",
|
|
282
|
+
"./speechflow-node-a2a-expander.js",
|
|
254
283
|
"./speechflow-node-a2a-ffmpeg.js",
|
|
284
|
+
"./speechflow-node-a2a-filler.js",
|
|
285
|
+
"./speechflow-node-a2a-gain.js",
|
|
255
286
|
"./speechflow-node-a2a-gender.js",
|
|
256
287
|
"./speechflow-node-a2a-meter.js",
|
|
257
288
|
"./speechflow-node-a2a-mute.js",
|
|
289
|
+
"./speechflow-node-a2a-rnnoise.js",
|
|
290
|
+
"./speechflow-node-a2a-speex.js",
|
|
258
291
|
"./speechflow-node-a2a-vad.js",
|
|
259
292
|
"./speechflow-node-a2a-wav.js",
|
|
293
|
+
"./speechflow-node-a2t-awstranscribe.js",
|
|
260
294
|
"./speechflow-node-a2t-deepgram.js",
|
|
295
|
+
"./speechflow-node-a2t-openaitranscribe.js",
|
|
296
|
+
"./speechflow-node-t2a-awspolly.js",
|
|
261
297
|
"./speechflow-node-t2a-elevenlabs.js",
|
|
262
298
|
"./speechflow-node-t2a-kokoro.js",
|
|
299
|
+
"./speechflow-node-t2t-awstranslate.js",
|
|
263
300
|
"./speechflow-node-t2t-deepl.js",
|
|
264
301
|
"./speechflow-node-t2t-format.js",
|
|
265
302
|
"./speechflow-node-t2t-ollama.js",
|
|
@@ -313,6 +350,19 @@ type wsPeerInfo = {
|
|
|
313
350
|
cacheDir: args.C
|
|
314
351
|
}
|
|
315
352
|
|
|
353
|
+
/* provide access to internal communication busses */
|
|
354
|
+
const busses = new Map<string, EventEmitter>()
|
|
355
|
+
const accessBus = (name: string): EventEmitter => {
|
|
356
|
+
let bus: EventEmitter
|
|
357
|
+
if (busses.has(name))
|
|
358
|
+
bus = busses.get(name)!
|
|
359
|
+
else {
|
|
360
|
+
bus = new EventEmitter()
|
|
361
|
+
busses.set(name, bus)
|
|
362
|
+
}
|
|
363
|
+
return bus
|
|
364
|
+
}
|
|
365
|
+
|
|
316
366
|
/* handle one-time status query of nodes */
|
|
317
367
|
if (args.S) {
|
|
318
368
|
const table = new Table({
|
|
@@ -328,6 +378,7 @@ type wsPeerInfo = {
|
|
|
328
378
|
for (const name of Object.keys(nodes)) {
|
|
329
379
|
cli!.log("info", `gathering status of node <${name}>`)
|
|
330
380
|
const node = new nodes[name](name, cfg, {}, [])
|
|
381
|
+
node._accessBus = accessBus
|
|
331
382
|
const status = await Promise.race<{ [ key: string ]: string | number }>([
|
|
332
383
|
node.status(),
|
|
333
384
|
new Promise<never>((resolve, reject) => setTimeout(() =>
|
|
@@ -387,6 +438,7 @@ type wsPeerInfo = {
|
|
|
387
438
|
nodeNums.set(NodeClass, ++num)
|
|
388
439
|
const name = num === 1 ? id : `${id}:${num}`
|
|
389
440
|
node = new NodeClass(name, cfg, opts, args)
|
|
441
|
+
node._accessBus = accessBus
|
|
390
442
|
}
|
|
391
443
|
catch (err) {
|
|
392
444
|
/* fatal error */
|
|
@@ -402,7 +454,7 @@ type wsPeerInfo = {
|
|
|
402
454
|
graphNodes.add(node)
|
|
403
455
|
return node
|
|
404
456
|
},
|
|
405
|
-
|
|
457
|
+
connectNodes (node1: SpeechFlowNode, node2: SpeechFlowNode) {
|
|
406
458
|
cli!.log("info", `connect node <${node1.id}> to node <${node2.id}>`)
|
|
407
459
|
node1.connect(node2)
|
|
408
460
|
}
|
|
@@ -465,7 +517,7 @@ type wsPeerInfo = {
|
|
|
465
517
|
new Promise<never>((resolve, reject) => setTimeout(() =>
|
|
466
518
|
reject(new Error("timeout")), 10 * 1000))
|
|
467
519
|
]).catch((err: Error) => {
|
|
468
|
-
cli!.log("error",
|
|
520
|
+
cli!.log("error", `<${node.id}>: failed to open node <${node.id}>: ${err.message}`)
|
|
469
521
|
throw new Error(`failed to open node <${node.id}>: ${err.message}`)
|
|
470
522
|
})
|
|
471
523
|
}
|
|
@@ -615,8 +667,7 @@ type wsPeerInfo = {
|
|
|
615
667
|
hapi.route({
|
|
616
668
|
method: "GET",
|
|
617
669
|
path: "/api/{req}/{node}/{params*}",
|
|
618
|
-
options: {
|
|
619
|
-
},
|
|
670
|
+
options: {},
|
|
620
671
|
handler: (request: HAPI.Request, h: HAPI.ResponseToolkit) => {
|
|
621
672
|
const peer = request.info.remoteAddress
|
|
622
673
|
const params = request.params.params as string ?? ""
|
|
@@ -628,11 +679,9 @@ type wsPeerInfo = {
|
|
|
628
679
|
args: params.split("/").filter((seg) => seg !== "")
|
|
629
680
|
}
|
|
630
681
|
cli!.log("info", `HAPI: peer ${peer}: GET: ${JSON.stringify(req)}`)
|
|
631
|
-
return consumeExternalRequest(req)
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
return h.response({ response: "ERROR", data: err.message }).code(417)
|
|
635
|
-
})
|
|
682
|
+
return consumeExternalRequest(req)
|
|
683
|
+
.then(() => h.response({ response: "OK" }).code(200))
|
|
684
|
+
.catch((err) => h.response({ response: "ERROR", data: err.message }).code(417))
|
|
636
685
|
}
|
|
637
686
|
})
|
|
638
687
|
hapi.route({
|
|
@@ -675,11 +724,9 @@ type wsPeerInfo = {
|
|
|
675
724
|
if (req instanceof arktype.type.errors)
|
|
676
725
|
return h.response({ response: "ERROR", data: `invalid request: ${req.summary}` }).code(417)
|
|
677
726
|
cli!.log("info", `HAPI: peer ${peer}: POST: ${JSON.stringify(req)}`)
|
|
678
|
-
return consumeExternalRequest(req)
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
return h.response({ response: "ERROR", data: err.message }).code(417)
|
|
682
|
-
})
|
|
727
|
+
return consumeExternalRequest(req)
|
|
728
|
+
.then(() => h.response({ response: "OK" }).code(200))
|
|
729
|
+
.catch((err: Error) => h.response({ response: "ERROR", data: err.message }).code(417))
|
|
683
730
|
}
|
|
684
731
|
})
|
|
685
732
|
await hapi.start()
|
|
@@ -697,10 +744,25 @@ type wsPeerInfo = {
|
|
|
697
744
|
})
|
|
698
745
|
}
|
|
699
746
|
|
|
700
|
-
/*
|
|
747
|
+
/* establish OSC event emission */
|
|
748
|
+
let sendOSC: (url: string, ...args: any[]) => void
|
|
749
|
+
if (args.o !== "") {
|
|
750
|
+
const osc = new OSC({ plugin: new OSC.DatagramPlugin({ type: "udp4" }) })
|
|
751
|
+
const m = args.o.match(/^(.+?):(\d+)$/)
|
|
752
|
+
if (m === null)
|
|
753
|
+
throw new Error("invalid OSC/UDP endpoint (expected <ip-adress>:<udp-port>)")
|
|
754
|
+
const host = m[1]
|
|
755
|
+
const port = m[2]
|
|
756
|
+
sendOSC = (url: string, ...args: any[]) => {
|
|
757
|
+
const msg = new OSC.Message(url, ...args)
|
|
758
|
+
osc.send(msg, { host, port })
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/* hook for send-dashboard method of nodes */
|
|
701
763
|
for (const node of graphNodes) {
|
|
702
|
-
node.on("dashboard
|
|
703
|
-
type:
|
|
764
|
+
node.on("send-dashboard", (info: {
|
|
765
|
+
type: "audio" | "text",
|
|
704
766
|
id: string,
|
|
705
767
|
kind: "final" | "intermediate",
|
|
706
768
|
value: string | number
|
|
@@ -714,6 +776,17 @@ type wsPeerInfo = {
|
|
|
714
776
|
cli!.log("debug", `HAPI: dashboard peer ${peer}: send ${data}`)
|
|
715
777
|
info.ws.send(data)
|
|
716
778
|
}
|
|
779
|
+
for (const node of graphNodes) {
|
|
780
|
+
Promise.race<void>([
|
|
781
|
+
node.receiveDashboard(info.type, info.id, info.kind, info.value),
|
|
782
|
+
new Promise<never>((resolve, reject) => setTimeout(() =>
|
|
783
|
+
reject(new Error("timeout")), 10 * 1000))
|
|
784
|
+
]).catch((err: Error) => {
|
|
785
|
+
cli!.log("warning", `sending dashboard info to node <${node.id}> failed: ${err.message}`)
|
|
786
|
+
})
|
|
787
|
+
}
|
|
788
|
+
if (args.o !== "")
|
|
789
|
+
sendOSC("/speechflow/dashboard", info.type, info.id, info.kind, info.value)
|
|
717
790
|
})
|
|
718
791
|
}
|
|
719
792
|
|
|
@@ -849,25 +922,40 @@ type wsPeerInfo = {
|
|
|
849
922
|
/* re-hook into uncaught exception handler */
|
|
850
923
|
process.removeAllListeners("uncaughtException")
|
|
851
924
|
process.on("uncaughtException", (err) => {
|
|
852
|
-
|
|
925
|
+
if (debug)
|
|
926
|
+
cli!.log("error", `uncaught exception: ${err.message}\n${err.stack}`)
|
|
927
|
+
else
|
|
928
|
+
cli!.log("error", `uncaught exception: ${err.message}`)
|
|
853
929
|
shutdown("exception")
|
|
854
930
|
})
|
|
855
931
|
|
|
856
932
|
/* re-hook into unhandled promise rejection handler */
|
|
857
933
|
process.removeAllListeners("unhandledRejection")
|
|
858
934
|
process.on("unhandledRejection", (reason) => {
|
|
859
|
-
if (reason instanceof Error)
|
|
860
|
-
|
|
935
|
+
if (reason instanceof Error) {
|
|
936
|
+
if (debug)
|
|
937
|
+
cli!.log("error", `unhandled rejection: ${reason.message}\n${reason.stack}`)
|
|
938
|
+
else
|
|
939
|
+
cli!.log("error", `unhandled rejection: ${reason.message}`)
|
|
940
|
+
}
|
|
861
941
|
else
|
|
862
942
|
cli!.log("error", `unhandled rejection: ${reason}`)
|
|
863
943
|
shutdown("exception")
|
|
864
944
|
})
|
|
865
945
|
})().catch((err: Error) => {
|
|
866
946
|
/* top-level exception handling */
|
|
867
|
-
if (cli !== null)
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
947
|
+
if (cli !== null) {
|
|
948
|
+
if (debug)
|
|
949
|
+
cli.log("error", `${err.message}\n${err.stack}`)
|
|
950
|
+
else
|
|
951
|
+
cli.log("error", `${err.message}`)
|
|
952
|
+
}
|
|
953
|
+
else {
|
|
954
|
+
if (debug)
|
|
955
|
+
process.stderr.write(`${pkg.name}: ${chalk.red("ERROR")}: ${err.message}\n${err.stack}\n`)
|
|
956
|
+
else
|
|
957
|
+
process.stderr.write(`${pkg.name}: ${chalk.red("ERROR")}: ${err.message}`)
|
|
958
|
+
}
|
|
871
959
|
process.exit(1)
|
|
872
960
|
})
|
|
873
961
|
|