termux-tts 1.5.5 → 1.7.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/bin/cli.js CHANGED
@@ -1,4 +1,8 @@
1
1
  #!/usr/bin/env node
2
+ /**
3
+ * Copyright (c) 2026 Uno Kim / AMEVA Foundation. All rights reserved.
4
+ * SPDX-License-Identifier: Apache-2.0 OR Commercial-License
5
+ */
2
6
  /**
3
7
  * AMEVA Standard Node.js CLI Runner for termux_tts.
4
8
  * Automatically resolves Python 3 environment and dispatches to python -m termux_tts.
@@ -1,125 +1,129 @@
1
- /**
2
- * termux-tts Node.js SDK
3
- * Secure On-Device Speech Synthesis Binding (CWE-94 Remediation).
4
- */
5
-
6
- const { spawn } = require('child_process');
7
- const path = require('path');
8
-
9
- class TTSEngine {
10
- constructor(options = {}) {
11
- this.language = options.language || 'ko';
12
- this.preset = options.preset || 'balanced';
13
- this.sampleRate = options.sampleRate || 22050;
14
- this.engine = options.engine || 'auto';
15
- this.device = options.device || (options.gpu ? 'gpu' : 'auto');
16
- this.tier = options.tier || null;
17
- this.model = options.model || null;
18
- }
19
-
20
- async synthesize(text, options = {}) {
21
- if (!text || typeof text !== 'string' || !text.trim()) {
22
- throw new Error('TTSInferenceError: Input text cannot be empty.');
23
- }
24
-
25
- const output = options.output || path.join(process.cwd(), 'output.wav');
26
- const speed = typeof options.speed === 'number' ? options.speed : 1.0;
27
- const preset = options.preset || this.preset;
28
- const engineType = options.engine || this.engine;
29
- const deviceType = options.device || (options.gpu ? 'gpu' : this.device);
30
- const tierType = options.tier || this.tier;
31
- const modelPath = options.model || this.model;
32
-
33
- const payload = JSON.stringify({
34
- text: text,
35
- output: output,
36
- language: this.language,
37
- sample_rate: this.sampleRate,
38
- speed: speed,
39
- preset: preset,
40
- engine: engineType,
41
- device: deviceType,
42
- tier: tierType,
43
- model: modelPath
44
- });
45
-
46
- return new Promise((resolve, reject) => {
47
- const pythonDriver = `
48
- import sys, json
49
- import termux_tts as tts
50
-
51
- try:
52
- data = json.loads(sys.stdin.read())
53
- engine = tts.load(
54
- model=data.get('model'),
55
- language=data.get('language', 'ko'),
56
- preset=data.get('preset', 'balanced'),
57
- sample_rate=data.get('sample_rate', 22050),
58
- engine=data.get('engine', 'auto'),
59
- device=data.get('device', 'auto'),
60
- tier=data.get('tier', None)
61
- )
62
- res = engine.synthesize(data['text'], output=data['output'], speed=float(data.get('speed', 1.0)))
63
- result = {
64
- 'status': 'SUCCESS',
65
- 'text': res.text,
66
- 'outputPath': data['output'],
67
- 'durationSec': res.duration_sec,
68
- 'elapsedMs': res.elapsed_ms,
69
- 'rtf': res.rtf,
70
- 'sampleRate': res.sample_rate,
71
- 'backend': res.backend,
72
- 'modelName': getattr(res, 'model_name', 'default')
73
- }
74
- sys.stdout.write(json.dumps(result))
75
- except Exception as e:
76
- sys.stderr.write(f"TTSInferenceError: {str(e)}")
77
- sys.exit(1)
78
- `;
79
- const pyBin = process.env.PYTHON_BIN || (process.platform === 'win32' ? 'py' : 'python3');
80
- const proc = spawn(pyBin, ['-c', pythonDriver], {
81
- env: { ...process.env, PYTHONPATH: path.join(__dirname, '..') }
82
- });
83
-
84
- let stdout = '';
85
- let stderr = '';
86
-
87
- proc.stdout.on('data', (d) => { stdout += d.toString(); });
88
- proc.stderr.on('data', (d) => { stderr += d.toString(); });
89
-
90
- proc.on('close', (code) => {
91
- if (code !== 0) {
92
- return reject(new Error(`TTSInferenceError (${code}): ${stderr.trim() || stdout.trim()}`));
93
- }
94
- try {
95
- const parsed = JSON.parse(stdout);
96
- if (parsed.status === 'SUCCESS') {
97
- resolve(parsed);
98
- } else {
99
- reject(new Error(`TTSInferenceError: Unexpected execution status in ${stdout}`));
100
- }
101
- } catch (err) {
102
- reject(new Error(`TTSParseError: Failed to parse Python worker JSON response: ${stdout}`));
103
- }
104
- });
105
-
106
- proc.on('error', (err) => {
107
- reject(new Error(`TTSProcessError: Failed to spawn python3 worker: ${err.message}`));
108
- });
109
-
110
- // Write JSON payload securely to stdin and close stream
111
- proc.stdin.write(payload);
112
- proc.stdin.end();
113
- });
114
- }
115
-
116
- close() {
117
- // Resource clean up
118
- }
119
- }
120
-
121
- module.exports = {
122
- TTSEngine,
123
- load: (opts) => new TTSEngine(opts),
124
- version: "0.1.0"
125
- };
1
+ /**
2
+ * Copyright (c) 2026 Uno Kim / AMEVA Foundation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0 OR Commercial-License
4
+ */
5
+ /**
6
+ * termux-tts Node.js SDK
7
+ * Secure On-Device Speech Synthesis Binding (CWE-94 Remediation).
8
+ */
9
+
10
+ const { spawn } = require('child_process');
11
+ const path = require('path');
12
+
13
+ class TTSEngine {
14
+ constructor(options = {}) {
15
+ this.language = options.language || 'ko';
16
+ this.preset = options.preset || 'balanced';
17
+ this.sampleRate = options.sampleRate || 22050;
18
+ this.engine = options.engine || 'auto';
19
+ this.device = options.device || (options.gpu ? 'gpu' : 'auto');
20
+ this.tier = options.tier || null;
21
+ this.model = options.model || null;
22
+ }
23
+
24
+ async synthesize(text, options = {}) {
25
+ if (!text || typeof text !== 'string' || !text.trim()) {
26
+ throw new Error('TTSInferenceError: Input text cannot be empty.');
27
+ }
28
+
29
+ const output = options.output || path.join(process.cwd(), 'output.wav');
30
+ const speed = typeof options.speed === 'number' ? options.speed : 1.0;
31
+ const preset = options.preset || this.preset;
32
+ const engineType = options.engine || this.engine;
33
+ const deviceType = options.device || (options.gpu ? 'gpu' : this.device);
34
+ const tierType = options.tier || this.tier;
35
+ const modelPath = options.model || this.model;
36
+
37
+ const payload = JSON.stringify({
38
+ text: text,
39
+ output: output,
40
+ language: this.language,
41
+ sample_rate: this.sampleRate,
42
+ speed: speed,
43
+ preset: preset,
44
+ engine: engineType,
45
+ device: deviceType,
46
+ tier: tierType,
47
+ model: modelPath
48
+ });
49
+
50
+ return new Promise((resolve, reject) => {
51
+ const pythonDriver = `
52
+ import sys, json
53
+ import termux_tts as tts
54
+
55
+ try:
56
+ data = json.loads(sys.stdin.read())
57
+ engine = tts.load(
58
+ model=data.get('model'),
59
+ language=data.get('language', 'ko'),
60
+ preset=data.get('preset', 'balanced'),
61
+ sample_rate=data.get('sample_rate', 22050),
62
+ engine=data.get('engine', 'auto'),
63
+ device=data.get('device', 'auto'),
64
+ tier=data.get('tier', None)
65
+ )
66
+ res = engine.synthesize(data['text'], output=data['output'], speed=float(data.get('speed', 1.0)))
67
+ result = {
68
+ 'status': 'SUCCESS',
69
+ 'text': res.text,
70
+ 'outputPath': data['output'],
71
+ 'durationSec': res.duration_sec,
72
+ 'elapsedMs': res.elapsed_ms,
73
+ 'rtf': res.rtf,
74
+ 'sampleRate': res.sample_rate,
75
+ 'backend': res.backend,
76
+ 'modelName': getattr(res, 'model_name', 'default')
77
+ }
78
+ sys.stdout.write(json.dumps(result))
79
+ except Exception as e:
80
+ sys.stderr.write(f"TTSInferenceError: {str(e)}")
81
+ sys.exit(1)
82
+ `;
83
+ const pyBin = process.env.PYTHON_BIN || (process.platform === 'win32' ? 'py' : 'python3');
84
+ const proc = spawn(pyBin, ['-c', pythonDriver], {
85
+ env: { ...process.env, PYTHONPATH: path.join(__dirname, '..') }
86
+ });
87
+
88
+ let stdout = '';
89
+ let stderr = '';
90
+
91
+ proc.stdout.on('data', (d) => { stdout += d.toString(); });
92
+ proc.stderr.on('data', (d) => { stderr += d.toString(); });
93
+
94
+ proc.on('close', (code) => {
95
+ if (code !== 0) {
96
+ return reject(new Error(`TTSInferenceError (${code}): ${stderr.trim() || stdout.trim()}`));
97
+ }
98
+ try {
99
+ const parsed = JSON.parse(stdout);
100
+ if (parsed.status === 'SUCCESS') {
101
+ resolve(parsed);
102
+ } else {
103
+ reject(new Error(`TTSInferenceError: Unexpected execution status in ${stdout}`));
104
+ }
105
+ } catch (err) {
106
+ reject(new Error(`TTSParseError: Failed to parse Python worker JSON response: ${stdout}`));
107
+ }
108
+ });
109
+
110
+ proc.on('error', (err) => {
111
+ reject(new Error(`TTSProcessError: Failed to spawn python3 worker: ${err.message}`));
112
+ });
113
+
114
+ // Write JSON payload securely to stdin and close stream
115
+ proc.stdin.write(payload);
116
+ proc.stdin.end();
117
+ });
118
+ }
119
+
120
+ close() {
121
+ // Resource clean up
122
+ }
123
+ }
124
+
125
+ module.exports = {
126
+ TTSEngine,
127
+ load: (opts) => new TTSEngine(opts),
128
+ version: "0.1.0"
129
+ };
@@ -1,40 +1,44 @@
1
- const tts = require('./index.js');
2
- const assert = require('assert');
3
- const path = require('path');
4
- const fs = require('fs');
5
-
6
- async function testNode() {
7
- console.log('[*] Testing termux-tts Node.js SDK...');
8
- const engine = tts.load({ language: 'ko' });
9
- assert.strictEqual(engine.language, 'ko');
10
-
11
- // 1. Valid synthesis test
12
- const outWav = path.join(__dirname, 'test_node_out.wav');
13
- const res = await engine.synthesize("노드 JS SDK 보안 무결성 합성 검증입니다.", {
14
- output: outWav,
15
- speed: 1.0
16
- });
17
- assert.strictEqual(res.status, 'SUCCESS');
18
- assert(res.durationSec > 0.3);
19
- assert(fs.existsSync(outWav));
20
- assert(fs.statSync(outWav).size > 1000);
21
- fs.unlinkSync(outWav);
22
- console.log(`[PASS] Node.js Synthesis: ${res.durationSec}s in ${res.elapsedMs}ms`);
23
-
24
- // 2. Security payload test (Command/Code Injection Attack payload)
25
- const attackPayload = 'Hello"""\nimport os\nos.system("echo HACKED")\n"""World';
26
- const attackOut = path.join(__dirname, 'test_attack_out.wav');
27
- const attackRes = await engine.synthesize(attackPayload, {
28
- output: attackOut
29
- });
30
- assert.strictEqual(attackRes.status, 'SUCCESS');
31
- if (fs.existsSync(attackOut)) {
32
- fs.unlinkSync(attackOut);
33
- }
34
- console.log('[PASS] Node.js RCE Injection Defense verified.');
35
- }
36
-
37
- testNode().catch((err) => {
38
- console.error('[FAIL]', err);
39
- process.exit(1);
40
- });
1
+ /**
2
+ * Copyright (c) 2026 Uno Kim / AMEVA Foundation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0 OR Commercial-License
4
+ */
5
+ const tts = require('./index.js');
6
+ const assert = require('assert');
7
+ const path = require('path');
8
+ const fs = require('fs');
9
+
10
+ async function testNode() {
11
+ console.log('[*] Testing termux-tts Node.js SDK...');
12
+ const engine = tts.load({ language: 'ko' });
13
+ assert.strictEqual(engine.language, 'ko');
14
+
15
+ // 1. Valid synthesis test
16
+ const outWav = path.join(__dirname, 'test_node_out.wav');
17
+ const res = await engine.synthesize("노드 JS SDK 보안 무결성 합성 검증입니다.", {
18
+ output: outWav,
19
+ speed: 1.0
20
+ });
21
+ assert.strictEqual(res.status, 'SUCCESS');
22
+ assert(res.durationSec > 0.3);
23
+ assert(fs.existsSync(outWav));
24
+ assert(fs.statSync(outWav).size > 1000);
25
+ fs.unlinkSync(outWav);
26
+ console.log(`[PASS] Node.js Synthesis: ${res.durationSec}s in ${res.elapsedMs}ms`);
27
+
28
+ // 2. Security payload test (Command/Code Injection Attack payload)
29
+ const attackPayload = 'Hello"""\nimport os\nos.system("echo HACKED")\n"""World';
30
+ const attackOut = path.join(__dirname, 'test_attack_out.wav');
31
+ const attackRes = await engine.synthesize(attackPayload, {
32
+ output: attackOut
33
+ });
34
+ assert.strictEqual(attackRes.status, 'SUCCESS');
35
+ if (fs.existsSync(attackOut)) {
36
+ fs.unlinkSync(attackOut);
37
+ }
38
+ console.log('[PASS] Node.js RCE Injection Defense verified.');
39
+ }
40
+
41
+ testNode().catch((err) => {
42
+ console.error('[FAIL]', err);
43
+ process.exit(1);
44
+ });
package/index.js CHANGED
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Copyright (c) 2026 Uno Kim / AMEVA Foundation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0 OR Commercial-License
4
+ */
1
5
  /**
2
6
  * termux-tts Node.js SDK Entrypoint
3
7
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "termux-tts",
3
- "version": "1.5.5",
3
+ "version": "1.7.0",
4
4
  "description": "On-device Text-to-Speech framework utilizing device resources (DSP Formant Vocoder, ONNX Neural Runtime & Android Native Voice)",
5
5
  "main": "index.js",
6
6
  "bin": {