echogarden 2.3.5 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/lexicons/words.en.json +252 -2
- package/data/schemas/options.json +5 -1
- package/dist/api/Synthesis.js +1 -2
- package/dist/api/Synthesis.js.map +1 -1
- package/dist/cli/CLI.js +9 -4
- package/dist/cli/CLI.js.map +1 -1
- package/dist/recognition/OpenAICloudSTT.d.ts +2 -2
- package/dist/recognition/OpenAICloudSTT.js +10 -3
- package/dist/recognition/OpenAICloudSTT.js.map +1 -1
- package/dist/synthesis/GoogleCloudTTS.d.ts +5 -2
- package/dist/synthesis/GoogleCloudTTS.js +6 -2
- package/dist/synthesis/GoogleCloudTTS.js.map +1 -1
- package/dist/synthesis/OpenAICloudTTS.d.ts +2 -1
- package/dist/synthesis/OpenAICloudTTS.js +58 -86
- package/dist/synthesis/OpenAICloudTTS.js.map +1 -1
- package/dist/utilities/WasmMemoryManager.d.ts +1 -1
- package/docs/Options.md +3 -2
- package/package.json +9 -9
- package/src/api/Synthesis.ts +1 -2
- package/src/cli/CLI.ts +12 -6
- package/src/recognition/OpenAICloudSTT.ts +13 -5
- package/src/synthesis/GoogleCloudTTS.ts +15 -3
- package/src/synthesis/OpenAICloudTTS.ts +62 -87
- package/src/typings/Fillers.d.ts +4 -0
package/docs/Options.md
CHANGED
|
@@ -101,7 +101,8 @@ Applies to CLI operations: `speak`, `speak-file`, `speak-url`, `speak-wikipedia`
|
|
|
101
101
|
* `openAICloud.apiKey`: API key (required)
|
|
102
102
|
* `openAICloud.organization`: organization identifier. Optional
|
|
103
103
|
* `openAICloud.baseURL`: override the default base URL for the API. Optional
|
|
104
|
-
* `openAICloud.model`: model to use. Can be either `tts-1` or `
|
|
104
|
+
* `openAICloud.model`: model to use. Can be either `tts-1`, `tts-1-hd` or `gpt-4o-mini-tts`. Defaults to `tts-1`
|
|
105
|
+
* `openAICloud.instructions`: instructions for model. Only works with `gpt-4o-mini-tts`. Optional
|
|
105
106
|
* `openAICloud.timeout`: request timeout. Optional
|
|
106
107
|
* `openAICloud.maxRetries`: maximum retries on failure. Defaults to 10
|
|
107
108
|
|
|
@@ -213,7 +214,7 @@ Applies to CLI operation: `transcribe`, API method: `recognize`
|
|
|
213
214
|
|
|
214
215
|
**OpenAI Cloud**:
|
|
215
216
|
* `openAICloud.apiKey`: API key (required)
|
|
216
|
-
* `openAICloud.model`: model to use. When using the default provider (OpenAI), can
|
|
217
|
+
* `openAICloud.model`: model to use. When using the default provider (OpenAI), can be `whisper-1`, `gpt-4o-mini-transcribe` or `gpt-4o-transcribe`. For a custom provider, like Groq, see its documentation
|
|
217
218
|
* `openAICloud.organization`: organization identifier. Optional
|
|
218
219
|
* `openAICloud.baseURL`: override the default endpoint used by the API. For example, set `https://api.groq.com/openai/v1` to use Groq's OpenAI-compatible API instead of the default one. Optional
|
|
219
220
|
* `openAICloud.temperature`: temperature. Choosing `0` uses a dynamic temperature approach. Defaults to `0`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "echogarden",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "An easy-to-use speech toolset. Includes tools for synthesis, recognition, alignment, speech translation, language detection, source separation and more.",
|
|
5
5
|
"author": "Rotem Dan",
|
|
6
6
|
"license": "GPL-3.0",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"echogarden": "./dist/cli/CLILauncher.js"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@aws-sdk/client-polly": "^3.
|
|
56
|
-
"@aws-sdk/client-transcribe-streaming": "^3.
|
|
55
|
+
"@aws-sdk/client-polly": "^3.777.0",
|
|
56
|
+
"@aws-sdk/client-transcribe-streaming": "^3.777.0",
|
|
57
57
|
"@echogarden/audio-io": "^0.3.0",
|
|
58
58
|
"@echogarden/espeak-ng-emscripten": "^0.3.3",
|
|
59
59
|
"@echogarden/fasttext-wasm": "^0.1.0",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"@echogarden/speex-resampler-wasm": "^0.3.0",
|
|
68
68
|
"@echogarden/svoxpico-wasm": "^0.2.0",
|
|
69
69
|
"@echogarden/transformers-nodejs-lite": "^2.17.1-lite.3",
|
|
70
|
-
"@mozilla/readability": "^0.
|
|
70
|
+
"@mozilla/readability": "^0.6.0",
|
|
71
71
|
"alawmulaw": "^6.0.0",
|
|
72
72
|
"chalk": "^5.4.1",
|
|
73
73
|
"cldr-segmentation": "^2.2.1",
|
|
@@ -82,10 +82,10 @@
|
|
|
82
82
|
"jsdom": "^26.0.0",
|
|
83
83
|
"json5": "^2.2.3",
|
|
84
84
|
"kuromoji": "^0.1.2",
|
|
85
|
-
"microsoft-cognitiveservices-speech-sdk": "^1.
|
|
85
|
+
"microsoft-cognitiveservices-speech-sdk": "^1.43.0",
|
|
86
86
|
"msgpack-lite": "^0.1.26",
|
|
87
|
-
"onnxruntime-node": "^1.
|
|
88
|
-
"openai": "^4.
|
|
87
|
+
"onnxruntime-node": "^1.21.0",
|
|
88
|
+
"openai": "^4.90.0",
|
|
89
89
|
"sam-js": "^0.3.1",
|
|
90
90
|
"strip-ansi": "^7.1.0",
|
|
91
91
|
"tar": "^7.4.3",
|
|
@@ -112,9 +112,9 @@
|
|
|
112
112
|
"@types/graceful-fs": "^4.1.9",
|
|
113
113
|
"@types/jsdom": "^21.1.7",
|
|
114
114
|
"@types/msgpack-lite": "^0.1.11",
|
|
115
|
-
"@types/node": "^22.13.
|
|
115
|
+
"@types/node": "^22.13.14",
|
|
116
116
|
"@types/tar": "^6.1.13",
|
|
117
|
-
"@types/ws": "^8.
|
|
117
|
+
"@types/ws": "^8.18.0",
|
|
118
118
|
"ts-json-schema-generator": "^2.3.0",
|
|
119
119
|
"typescript": "^5.8.2"
|
|
120
120
|
},
|
package/src/api/Synthesis.ts
CHANGED
|
@@ -701,8 +701,7 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
701
701
|
|
|
702
702
|
logger.end()
|
|
703
703
|
|
|
704
|
-
const {
|
|
705
|
-
const rawAudio = await FFMpegTranscoder.decodeToChannels(audioData)
|
|
704
|
+
const { rawAudio, timepoints } = await GoogleCloudTTS.synthesize(text, apiKey, language, voice, speed, pitchDeltaSemitones, 0, inputIsSSML)
|
|
706
705
|
|
|
707
706
|
synthesizedAudio = rawAudio
|
|
708
707
|
|
package/src/cli/CLI.ts
CHANGED
|
@@ -1698,21 +1698,27 @@ async function writeSourceSeparationOutputIfNeeded(outputFilename: string, isola
|
|
|
1698
1698
|
{
|
|
1699
1699
|
const isolatedOutputFilePath = prefixIsolated ? `${pathWithoutExtension}.isolated.${fileExtension}` : outputFilename
|
|
1700
1700
|
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
await fileSaver(isolatedRawAudio, [], '')
|
|
1701
|
+
await saveAudioWithBitrate(isolatedRawAudio, isolatedOutputFilePath, fileExtension, 64)
|
|
1704
1702
|
}
|
|
1705
1703
|
|
|
1706
1704
|
{
|
|
1707
1705
|
const backgroundOutputFilePath = `${pathWithoutExtension}.background.${fileExtension}`
|
|
1708
1706
|
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
await fileSaver(backgroundRawAudio, [], '')
|
|
1707
|
+
await saveAudioWithBitrate(backgroundRawAudio, backgroundOutputFilePath, fileExtension, 128)
|
|
1712
1708
|
}
|
|
1713
1709
|
}
|
|
1714
1710
|
}
|
|
1715
1711
|
|
|
1712
|
+
async function saveAudioWithBitrate(rawAudio: RawAudio, filepath: string, fileExtension: string, bitrate: number) {
|
|
1713
|
+
const ffmpegOptions = getDefaultFFMpegOptionsForSpeech(fileExtension, bitrate)
|
|
1714
|
+
ffmpegOptions.filename = filepath
|
|
1715
|
+
|
|
1716
|
+
const fileDir = parsePath(filepath).dir || './'
|
|
1717
|
+
|
|
1718
|
+
await ensureDir(fileDir)
|
|
1719
|
+
await encodeFromChannels(rawAudio, ffmpegOptions)
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1716
1722
|
async function optionsLookupToTypedObject<K extends keyof APIOptions>(cliOptionsMap: Map<string, string>, optionsRoot: K, additionalOptionsSchema?: Map<string, SchemaTypeDefinition>): Promise<APIOptions[K]> {
|
|
1717
1723
|
const optionsSchema = await getOptionsSchema()
|
|
1718
1724
|
const resultingObj: any = {}
|
|
@@ -35,6 +35,14 @@ export async function recognize(rawAudio: RawAudio, languageCode: string, option
|
|
|
35
35
|
|
|
36
36
|
logger.start(options.baseURL ? `Send request to ${options.baseURL}` : 'Send request to OpenAI Cloud API')
|
|
37
37
|
|
|
38
|
+
let responseFormat: 'verbose_json' | 'json'
|
|
39
|
+
|
|
40
|
+
if (options.model === 'gpt-4o-mini-transcribe' || options.model === 'gpt-4o-transcribe') {
|
|
41
|
+
responseFormat = 'json'
|
|
42
|
+
} else {
|
|
43
|
+
responseFormat = 'verbose_json'
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
let response: VerboseResponse
|
|
39
47
|
|
|
40
48
|
if (task == 'transcribe') {
|
|
@@ -46,7 +54,7 @@ export async function recognize(rawAudio: RawAudio, languageCode: string, option
|
|
|
46
54
|
model: options.model,
|
|
47
55
|
language: languageCode,
|
|
48
56
|
prompt: options.prompt,
|
|
49
|
-
response_format:
|
|
57
|
+
response_format: responseFormat,
|
|
50
58
|
temperature: options.temperature,
|
|
51
59
|
timestamp_granularities,
|
|
52
60
|
}) as any as VerboseResponse
|
|
@@ -55,7 +63,7 @@ export async function recognize(rawAudio: RawAudio, languageCode: string, option
|
|
|
55
63
|
file: virtualFileStream,
|
|
56
64
|
model: options.model,
|
|
57
65
|
prompt: options.prompt,
|
|
58
|
-
response_format:
|
|
66
|
+
response_format: responseFormat,
|
|
59
67
|
temperature: options.temperature,
|
|
60
68
|
}) as any as VerboseResponse
|
|
61
69
|
} else {
|
|
@@ -64,7 +72,7 @@ export async function recognize(rawAudio: RawAudio, languageCode: string, option
|
|
|
64
72
|
|
|
65
73
|
const transcript = response.text.trim()
|
|
66
74
|
|
|
67
|
-
let timeline: Timeline
|
|
75
|
+
let timeline: Timeline | undefined
|
|
68
76
|
|
|
69
77
|
if (response.words) {
|
|
70
78
|
timeline = response.words.map<TimelineEntry>(entry => ({
|
|
@@ -73,7 +81,7 @@ export async function recognize(rawAudio: RawAudio, languageCode: string, option
|
|
|
73
81
|
startTime: entry.start,
|
|
74
82
|
endTime: entry.end
|
|
75
83
|
}))
|
|
76
|
-
} else {
|
|
84
|
+
} else if (response.segments) {
|
|
77
85
|
const segmentTimeline = response.segments.map<TimelineEntry>(entry => ({
|
|
78
86
|
type: 'segment',
|
|
79
87
|
text: entry.text,
|
|
@@ -129,7 +137,7 @@ interface VerboseResponse {
|
|
|
129
137
|
type Task = 'transcribe' | 'translate'
|
|
130
138
|
|
|
131
139
|
export interface OpenAICloudSTTOptions {
|
|
132
|
-
model?: 'whisper-1' | string
|
|
140
|
+
model?: 'whisper-1' | 'gpt-4o-mini-transcribe' | 'gpt-4o-transcribe' | string
|
|
133
141
|
|
|
134
142
|
apiKey?: string
|
|
135
143
|
organization?: string
|
|
@@ -2,6 +2,7 @@ import { request } from 'gaxios'
|
|
|
2
2
|
import { Logger } from '../utilities/Logger.js'
|
|
3
3
|
import { logToStderr } from '../utilities/Utilities.js'
|
|
4
4
|
import { decodeBase64 } from '../encodings/Base64.js'
|
|
5
|
+
import * as FFMpegTranscoder from '../codecs/FFMpegTranscoder.js'
|
|
5
6
|
|
|
6
7
|
const log = logToStderr
|
|
7
8
|
|
|
@@ -14,7 +15,7 @@ export async function synthesize(
|
|
|
14
15
|
pitchDeltaSemitones = 0.0,
|
|
15
16
|
volumeGainDecibels = 0.0,
|
|
16
17
|
ssml = false,
|
|
17
|
-
audioEncoding: AudioEncoding = '
|
|
18
|
+
audioEncoding: AudioEncoding = 'MP3',
|
|
18
19
|
sampleRate = 24000) {
|
|
19
20
|
|
|
20
21
|
const logger = new Logger()
|
|
@@ -70,9 +71,14 @@ export async function synthesize(
|
|
|
70
71
|
|
|
71
72
|
const result = parseResponseBody(response.data)
|
|
72
73
|
|
|
74
|
+
logger.start('Decode to raw audio')
|
|
75
|
+
|
|
76
|
+
const rawAudio = await FFMpegTranscoder.decodeToChannels(result.audioData)
|
|
77
|
+
const timepoints = result.timepoints
|
|
78
|
+
|
|
73
79
|
logger.end()
|
|
74
80
|
|
|
75
|
-
return
|
|
81
|
+
return { rawAudio, timepoints }
|
|
76
82
|
}
|
|
77
83
|
|
|
78
84
|
function parseResponseBody(responseBody: any) {
|
|
@@ -116,7 +122,13 @@ export type GoogleCloudVoice = {
|
|
|
116
122
|
naturalSampleRateHertz: number
|
|
117
123
|
}
|
|
118
124
|
|
|
119
|
-
export type AudioEncoding =
|
|
125
|
+
export type AudioEncoding =
|
|
126
|
+
'AUDIO_ENCODING_UNSPECIFIED' |
|
|
127
|
+
'LINEAR16' |
|
|
128
|
+
'MP3' |
|
|
129
|
+
'OGG_OPUS' |
|
|
130
|
+
'MULAW' |
|
|
131
|
+
'ALAW'
|
|
120
132
|
|
|
121
133
|
export type timePoint = {
|
|
122
134
|
markName: string,
|
|
@@ -22,6 +22,7 @@ export async function synthesize(text: string, voice: string, speed: number, opt
|
|
|
22
22
|
input: text,
|
|
23
23
|
model: options.model!,
|
|
24
24
|
voice: voice as any,
|
|
25
|
+
instructions: options.instructions,
|
|
25
26
|
response_format: 'opus',
|
|
26
27
|
speed,
|
|
27
28
|
}, {
|
|
@@ -43,7 +44,9 @@ export interface OpenAICloudTTSOptions {
|
|
|
43
44
|
organization?: string
|
|
44
45
|
baseURL?: string
|
|
45
46
|
|
|
46
|
-
model?: 'tts-1' | 'tts-1-hd'
|
|
47
|
+
model?: 'tts-1' | 'tts-1-hd' | 'gpt-4o-mini-tts'
|
|
48
|
+
|
|
49
|
+
instructions?: string
|
|
47
50
|
|
|
48
51
|
timeout?: number
|
|
49
52
|
maxRetries?: number
|
|
@@ -56,110 +59,72 @@ export const defaultOpenAICloudTTSOptions: OpenAICloudTTSOptions = {
|
|
|
56
59
|
|
|
57
60
|
model: 'tts-1',
|
|
58
61
|
|
|
62
|
+
instructions: undefined,
|
|
63
|
+
|
|
59
64
|
timeout: undefined,
|
|
60
65
|
maxRetries: 10,
|
|
61
66
|
}
|
|
62
67
|
|
|
68
|
+
// Reference: https://platform.openai.com/docs/guides/text-to-speech#supported-languages
|
|
63
69
|
export const supportedLanguages = [
|
|
64
70
|
'en',
|
|
65
|
-
'
|
|
66
|
-
'
|
|
67
|
-
'
|
|
68
|
-
'
|
|
69
|
-
'
|
|
70
|
-
'
|
|
71
|
-
'
|
|
72
|
-
'pt',
|
|
73
|
-
'tr',
|
|
74
|
-
'pl',
|
|
71
|
+
'af',
|
|
72
|
+
'ar',
|
|
73
|
+
'hy',
|
|
74
|
+
'az',
|
|
75
|
+
'be',
|
|
76
|
+
'bs',
|
|
77
|
+
'bg',
|
|
75
78
|
'ca',
|
|
79
|
+
'zh',
|
|
80
|
+
'hr',
|
|
81
|
+
'cs',
|
|
82
|
+
'da',
|
|
76
83
|
'nl',
|
|
77
|
-
'
|
|
78
|
-
'
|
|
79
|
-
'it',
|
|
80
|
-
'id',
|
|
81
|
-
'hi',
|
|
84
|
+
'en',
|
|
85
|
+
'et',
|
|
82
86
|
'fi',
|
|
83
|
-
'
|
|
84
|
-
'
|
|
85
|
-
'
|
|
87
|
+
'fr',
|
|
88
|
+
'gl',
|
|
89
|
+
'de',
|
|
86
90
|
'el',
|
|
87
|
-
'
|
|
88
|
-
'
|
|
89
|
-
'ro',
|
|
90
|
-
'da',
|
|
91
|
+
'he',
|
|
92
|
+
'hi',
|
|
91
93
|
'hu',
|
|
92
|
-
'
|
|
93
|
-
'
|
|
94
|
-
'
|
|
95
|
-
'
|
|
96
|
-
'
|
|
97
|
-
'
|
|
94
|
+
'is',
|
|
95
|
+
'id',
|
|
96
|
+
'it',
|
|
97
|
+
'ja',
|
|
98
|
+
'kn',
|
|
99
|
+
'kk',
|
|
100
|
+
'ko',
|
|
101
|
+
'lv',
|
|
98
102
|
'lt',
|
|
99
|
-
'
|
|
103
|
+
'mk',
|
|
104
|
+
'ms',
|
|
105
|
+
'mr',
|
|
100
106
|
'mi',
|
|
101
|
-
'
|
|
102
|
-
'
|
|
103
|
-
'sk',
|
|
104
|
-
'te',
|
|
107
|
+
'ne',
|
|
108
|
+
'no',
|
|
105
109
|
'fa',
|
|
106
|
-
'
|
|
107
|
-
'
|
|
110
|
+
'pl',
|
|
111
|
+
'pt',
|
|
112
|
+
'ro',
|
|
113
|
+
'ru',
|
|
108
114
|
'sr',
|
|
109
|
-
'
|
|
115
|
+
'sk',
|
|
110
116
|
'sl',
|
|
111
|
-
'
|
|
112
|
-
'et',
|
|
113
|
-
'mk',
|
|
114
|
-
'br',
|
|
115
|
-
'eu',
|
|
116
|
-
'is',
|
|
117
|
-
'hy',
|
|
118
|
-
'ne',
|
|
119
|
-
'mn',
|
|
120
|
-
'bs',
|
|
121
|
-
'kk',
|
|
122
|
-
'sq',
|
|
117
|
+
'es',
|
|
123
118
|
'sw',
|
|
124
|
-
'
|
|
125
|
-
'mr',
|
|
126
|
-
'pa',
|
|
127
|
-
'si',
|
|
128
|
-
'km',
|
|
129
|
-
'sn',
|
|
130
|
-
'yo',
|
|
131
|
-
'so',
|
|
132
|
-
'af',
|
|
133
|
-
'oc',
|
|
134
|
-
'ka',
|
|
135
|
-
'be',
|
|
136
|
-
'tg',
|
|
137
|
-
'sd',
|
|
138
|
-
'gu',
|
|
139
|
-
'am',
|
|
140
|
-
'yi',
|
|
141
|
-
'lo',
|
|
142
|
-
'uz',
|
|
143
|
-
'fo',
|
|
144
|
-
'ht',
|
|
145
|
-
'ps',
|
|
146
|
-
'tk',
|
|
147
|
-
'nn',
|
|
148
|
-
'mt',
|
|
149
|
-
'sa',
|
|
150
|
-
'lb',
|
|
151
|
-
'my',
|
|
152
|
-
'bo',
|
|
119
|
+
'sv',
|
|
153
120
|
'tl',
|
|
154
|
-
'
|
|
155
|
-
'
|
|
156
|
-
'
|
|
157
|
-
'
|
|
158
|
-
'
|
|
159
|
-
'
|
|
160
|
-
'
|
|
161
|
-
'jw',
|
|
162
|
-
'su',
|
|
121
|
+
'ta',
|
|
122
|
+
'th',
|
|
123
|
+
'tr',
|
|
124
|
+
'uk',
|
|
125
|
+
'ur',
|
|
126
|
+
'vi',
|
|
127
|
+
'cy',
|
|
163
128
|
]
|
|
164
129
|
|
|
165
130
|
export const voiceList: SynthesisVoice[] = [
|
|
@@ -173,6 +138,11 @@ export const voiceList: SynthesisVoice[] = [
|
|
|
173
138
|
languages: ['en-US', ...supportedLanguages],
|
|
174
139
|
gender: 'male',
|
|
175
140
|
},
|
|
141
|
+
{
|
|
142
|
+
name: 'ballad',
|
|
143
|
+
languages: ['en-GB', ...supportedLanguages],
|
|
144
|
+
gender: 'male',
|
|
145
|
+
},
|
|
176
146
|
{
|
|
177
147
|
name: 'coral',
|
|
178
148
|
languages: ['en-US', ...supportedLanguages],
|
|
@@ -208,4 +178,9 @@ export const voiceList: SynthesisVoice[] = [
|
|
|
208
178
|
languages: ['en-US', ...supportedLanguages],
|
|
209
179
|
gender: 'female',
|
|
210
180
|
},
|
|
181
|
+
{
|
|
182
|
+
name: 'verse',
|
|
183
|
+
languages: ['en-US', ...supportedLanguages],
|
|
184
|
+
gender: 'male',
|
|
185
|
+
},
|
|
211
186
|
]
|
package/src/typings/Fillers.d.ts
CHANGED
|
@@ -55,6 +55,9 @@ declare module '@echogarden/speex-resampler-wasm'
|
|
|
55
55
|
declare module '@echogarden/speex-resampler-wasm/simd'
|
|
56
56
|
declare module '@echogarden/vosk'
|
|
57
57
|
|
|
58
|
+
/*
|
|
59
|
+
// Not needed anymore in `onnxruntime-node` 1.21.0:
|
|
60
|
+
|
|
58
61
|
declare module 'onnxruntime-node' {
|
|
59
62
|
// From index.d.ts
|
|
60
63
|
export * from 'onnxruntime-common'
|
|
@@ -109,3 +112,4 @@ declare module 'onnxruntime-node' {
|
|
|
109
112
|
// From version.d.ts
|
|
110
113
|
export const version = '1.20.0'
|
|
111
114
|
}
|
|
115
|
+
*/
|