@mingxy/ocosay 1.0.3 → 1.0.5

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.
Files changed (62) hide show
  1. package/README.md +12 -0
  2. package/dist/config.js +2 -2
  3. package/dist/config.js.map +1 -1
  4. package/dist/plugin.d.ts +2 -4
  5. package/dist/plugin.d.ts.map +1 -1
  6. package/dist/plugin.js +6 -4
  7. package/dist/plugin.js.map +2 -2
  8. package/package.json +1 -1
  9. package/TECH_PLAN.md +0 -352
  10. package/__mocks__/@opencode-ai/plugin.ts +0 -32
  11. package/jest.config.js +0 -15
  12. package/src/config.ts +0 -183
  13. package/src/core/backends/afplay-backend.ts +0 -162
  14. package/src/core/backends/aplay-backend.ts +0 -160
  15. package/src/core/backends/base.ts +0 -117
  16. package/src/core/backends/index.ts +0 -128
  17. package/src/core/backends/naudiodon-backend.ts +0 -164
  18. package/src/core/backends/powershell-backend.ts +0 -173
  19. package/src/core/player.ts +0 -322
  20. package/src/core/speaker.ts +0 -283
  21. package/src/core/stream-player.ts +0 -326
  22. package/src/core/stream-reader.ts +0 -190
  23. package/src/core/streaming-synthesizer.ts +0 -123
  24. package/src/core/types.ts +0 -185
  25. package/src/index.ts +0 -236
  26. package/src/plugin.ts +0 -178
  27. package/src/providers/base.ts +0 -150
  28. package/src/providers/minimax.ts +0 -515
  29. package/src/tools/tts.ts +0 -277
  30. package/src/types/config.ts +0 -38
  31. package/src/types/naudiodon.d.ts +0 -19
  32. package/tests/__mocks__/@opencode-ai/plugin.ts +0 -32
  33. package/tests/backends.test.ts +0 -831
  34. package/tests/config.test.ts +0 -327
  35. package/tests/index.test.ts +0 -201
  36. package/tests/integration-test.d.ts +0 -6
  37. package/tests/integration-test.d.ts.map +0 -1
  38. package/tests/integration-test.js +0 -84
  39. package/tests/integration-test.js.map +0 -1
  40. package/tests/integration-test.ts +0 -93
  41. package/tests/p1-fixes.test.ts +0 -160
  42. package/tests/plugin.test.ts +0 -312
  43. package/tests/provider.test.d.ts +0 -2
  44. package/tests/provider.test.d.ts.map +0 -1
  45. package/tests/provider.test.js +0 -69
  46. package/tests/provider.test.js.map +0 -1
  47. package/tests/provider.test.ts +0 -87
  48. package/tests/speaker.test.d.ts +0 -2
  49. package/tests/speaker.test.d.ts.map +0 -1
  50. package/tests/speaker.test.js +0 -63
  51. package/tests/speaker.test.js.map +0 -1
  52. package/tests/speaker.test.ts +0 -232
  53. package/tests/stream-player.test.ts +0 -303
  54. package/tests/stream-reader.test.ts +0 -269
  55. package/tests/streaming-synthesizer.test.ts +0 -225
  56. package/tests/tts-tools.test.ts +0 -270
  57. package/tests/types.test.d.ts +0 -2
  58. package/tests/types.test.d.ts.map +0 -1
  59. package/tests/types.test.js +0 -61
  60. package/tests/types.test.js.map +0 -1
  61. package/tests/types.test.ts +0 -63
  62. package/tsconfig.json +0 -22
@@ -1,327 +0,0 @@
1
- import * as fs from 'fs'
2
- import * as path from 'path'
3
- import * as os from 'os'
4
- import { stripComments, generateDefaultConfig, mergeWithDefaults, loadOrCreateConfig } from '../src/config'
5
- import { DEFAULT_CONFIG } from '../src/types/config'
6
- import type { OcosayConfig } from '../src/types/config'
7
-
8
- jest.mock('fs')
9
- jest.mock('path')
10
- jest.mock('os')
11
-
12
- describe('config.ts', () => {
13
- const mockHomedir = '/home/testuser'
14
- const mockConfigPath = path.join(mockHomedir, '.config', 'opencode', 'ocosay.jsonc')
15
-
16
- beforeEach(() => {
17
- jest.clearAllMocks()
18
- ;(path.join as jest.Mock).mockReturnValue(mockConfigPath)
19
- ;(path.dirname as jest.Mock).mockReturnValue(path.dirname(mockConfigPath))
20
- ;(os.homedir as jest.Mock).mockReturnValue(mockHomedir)
21
- })
22
-
23
- describe('stripComments', () => {
24
- it('should strip single-line comments //', () => {
25
- const input = '{"enabled": true // comment'
26
- const result = stripComments(input)
27
- expect(result).toBe('{"enabled": true ')
28
- })
29
-
30
- it('should strip multi-line comments /* */', () => {
31
- const input = '{"enabled": true /* comment */}'
32
- const result = stripComments(input)
33
- expect(result).toBe('{"enabled": true }')
34
- })
35
-
36
- it('should strip multiple comments', () => {
37
- const input = `
38
- {
39
- // single line comment
40
- "enabled": true,
41
- /* multi
42
- line
43
- comment */
44
- "speed": 1.0
45
- }
46
- `
47
- const result = stripComments(input)
48
- expect(result).not.toContain('//')
49
- expect(result).not.toContain('/*')
50
- expect(result).not.toContain('*/')
51
- expect(result).toContain('"enabled": true')
52
- expect(result).toContain('"speed": 1.0')
53
- })
54
-
55
- it('should preserve JSON content', () => {
56
- const input = '{"enabled": true, "speed": 1.0}'
57
- const result = stripComments(input)
58
- expect(JSON.parse(result)).toEqual({ enabled: true, speed: 1.0 })
59
- })
60
-
61
- it('should preserve quoted strings containing // and /* */', () => {
62
- const input = '{"url": "https://example.com/api?foo=1&bar=2"}'
63
- const result = stripComments(input)
64
- expect(JSON.parse(result)).toEqual({ url: 'https://example.com/api?foo=1&bar=2' })
65
- })
66
-
67
- it('should preserve single-quoted strings', () => {
68
- const input = "{'key': 'value with // comment'}"
69
- const result = stripComments(input)
70
- expect(result).toContain('value with // comment')
71
- })
72
-
73
- it('should preserve template literals', () => {
74
- const input = '`string with // comment`'
75
- const result = stripComments(input)
76
- expect(result).toBe('`string with // comment`')
77
- })
78
-
79
- it('should handle comment at end of content', () => {
80
- const input = '{"key": "value"} // trailing'
81
- const result = stripComments(input)
82
- expect(result).toBe('{"key": "value"} ')
83
- })
84
-
85
- it('should handle empty string', () => {
86
- const result = stripComments('')
87
- expect(result).toBe('')
88
- })
89
- })
90
-
91
- describe('generateDefaultConfig', () => {
92
- it('should return complete OcosayConfig object', () => {
93
- const config = generateDefaultConfig()
94
- expect(config).toHaveProperty('enabled')
95
- expect(config).toHaveProperty('autoPlay')
96
- expect(config).toHaveProperty('autoRead')
97
- expect(config).toHaveProperty('streamMode')
98
- expect(config).toHaveProperty('streamBufferSize')
99
- expect(config).toHaveProperty('streamBufferTimeout')
100
- expect(config).toHaveProperty('speed')
101
- expect(config).toHaveProperty('volume')
102
- expect(config).toHaveProperty('pitch')
103
- expect(config).toHaveProperty('providers')
104
- expect(config).toHaveProperty('providers.minimax')
105
- })
106
-
107
- it('should have apiKey and baseURL as empty strings', () => {
108
- const config = generateDefaultConfig()
109
- expect(config.providers.minimax.apiKey).toBe('')
110
- expect(config.providers.minimax.baseURL).toBe('')
111
- expect(config.providers.minimax.voiceId).toBe('')
112
- })
113
-
114
- it('should have correct default values from DEFAULT_CONFIG', () => {
115
- const config = generateDefaultConfig()
116
- expect(config.enabled).toBe(DEFAULT_CONFIG.enabled)
117
- expect(config.autoPlay).toBe(DEFAULT_CONFIG.autoPlay)
118
- expect(config.autoRead).toBe(DEFAULT_CONFIG.autoRead)
119
- expect(config.streamMode).toBe(DEFAULT_CONFIG.streamMode)
120
- expect(config.streamBufferSize).toBe(DEFAULT_CONFIG.streamBufferSize)
121
- expect(config.streamBufferTimeout).toBe(DEFAULT_CONFIG.streamBufferTimeout)
122
- expect(config.speed).toBe(DEFAULT_CONFIG.speed)
123
- expect(config.volume).toBe(DEFAULT_CONFIG.volume)
124
- expect(config.pitch).toBe(DEFAULT_CONFIG.pitch)
125
- })
126
-
127
- it('should have correct minimax provider defaults', () => {
128
- const config = generateDefaultConfig()
129
- expect(config.providers.minimax.model).toBe('stream')
130
- expect(config.providers.minimax.ttsModel).toBe('speech-2.8-hd')
131
- expect(config.providers.minimax.audioFormat).toBe('mp3')
132
- })
133
- })
134
-
135
- describe('mergeWithDefaults', () => {
136
- it('should override defaults with user config', () => {
137
- const userConfig = { enabled: false, speed: 2.0 }
138
- const result = mergeWithDefaults(userConfig, DEFAULT_CONFIG)
139
- expect(result.enabled).toBe(false)
140
- expect(result.speed).toBe(2.0)
141
- })
142
-
143
- it('should use defaults for unspecified fields', () => {
144
- const userConfig: Partial<OcosayConfig> = { enabled: false }
145
- const result = mergeWithDefaults(userConfig, DEFAULT_CONFIG)
146
- expect(result.enabled).toBe(false)
147
- expect(result.autoPlay).toBe(DEFAULT_CONFIG.autoPlay)
148
- expect(result.autoRead).toBe(DEFAULT_CONFIG.autoRead)
149
- expect(result.streamMode).toBe(DEFAULT_CONFIG.streamMode)
150
- expect(result.speed).toBe(DEFAULT_CONFIG.speed)
151
- expect(result.volume).toBe(DEFAULT_CONFIG.volume)
152
- expect(result.pitch).toBe(DEFAULT_CONFIG.pitch)
153
- })
154
-
155
- it('should return defaults when no user config provided', () => {
156
- const result = mergeWithDefaults({}, DEFAULT_CONFIG)
157
- expect(result.enabled).toBe(DEFAULT_CONFIG.enabled)
158
- expect(result.autoPlay).toBe(DEFAULT_CONFIG.autoPlay)
159
- expect(result.autoRead).toBe(DEFAULT_CONFIG.autoRead)
160
- expect(result.streamBufferSize).toBe(DEFAULT_CONFIG.streamBufferSize)
161
- expect(result.streamBufferTimeout).toBe(DEFAULT_CONFIG.streamBufferTimeout)
162
- expect(result.speed).toBe(DEFAULT_CONFIG.speed)
163
- expect(result.volume).toBe(DEFAULT_CONFIG.volume)
164
- expect(result.pitch).toBe(DEFAULT_CONFIG.pitch)
165
- })
166
-
167
- it('should preserve all default fields', () => {
168
- const result = mergeWithDefaults({}, DEFAULT_CONFIG)
169
- expect(Object.keys(result).sort()).toEqual(Object.keys(DEFAULT_CONFIG).sort())
170
- })
171
- })
172
-
173
- describe('loadOrCreateConfig', () => {
174
- beforeEach(() => {
175
- // Reset module to clear any cached state
176
- jest.resetModules()
177
- jest.clearAllMocks()
178
- ;(path.join as jest.Mock).mockReturnValue(mockConfigPath)
179
- ;(path.dirname as jest.Mock).mockReturnValue(path.dirname(mockConfigPath))
180
- ;(os.homedir as jest.Mock).mockReturnValue(mockHomedir)
181
- })
182
-
183
- it('should create default config when config file does not exist', () => {
184
- ;(fs.existsSync as jest.Mock).mockReturnValue(false)
185
- ;(fs.mkdirSync as jest.Mock).mockReturnValue(undefined)
186
- ;(fs.writeFileSync as jest.Mock).mockReturnValue(undefined)
187
- ;(fs.chmodSync as jest.Mock).mockReturnValue(undefined)
188
-
189
- const config = loadOrCreateConfig()
190
-
191
- expect(fs.mkdirSync).toHaveBeenCalledWith(path.dirname(mockConfigPath), { recursive: true })
192
- expect(fs.writeFileSync).toHaveBeenCalled()
193
- expect(fs.chmodSync).toHaveBeenCalledWith(mockConfigPath, 0o600)
194
- expect(config.providers.minimax.apiKey).toBe('')
195
- expect(config.providers.minimax.baseURL).toBe('')
196
- })
197
-
198
- it('should create directory if it does not exist', () => {
199
- ;(fs.existsSync as jest.Mock).mockImplementation((p: string) => {
200
- if (p === path.dirname(mockConfigPath)) return false
201
- if (p === mockConfigPath) return false
202
- return false
203
- })
204
- ;(fs.mkdirSync as jest.Mock).mockReturnValue(undefined)
205
- ;(fs.writeFileSync as jest.Mock).mockReturnValue(undefined)
206
- ;(fs.chmodSync as jest.Mock).mockReturnValue(undefined)
207
-
208
- loadOrCreateConfig()
209
-
210
- expect(fs.mkdirSync).toHaveBeenCalledWith(path.dirname(mockConfigPath), { recursive: true })
211
- })
212
-
213
- it('should read and merge existing config file', () => {
214
- const existingConfig = {
215
- enabled: false,
216
- speed: 1.5,
217
- providers: {
218
- minimax: {
219
- apiKey: 'test-key',
220
- baseURL: 'https://api.minimax.io'
221
- }
222
- }
223
- }
224
-
225
- ;(fs.existsSync as jest.Mock).mockImplementation((p: string) => {
226
- return p === mockConfigPath || p === path.dirname(mockConfigPath)
227
- })
228
- ;(fs.readFileSync as jest.Mock).mockReturnValue(JSON.stringify(existingConfig))
229
-
230
- const config = loadOrCreateConfig()
231
-
232
- expect(fs.readFileSync).toHaveBeenCalledWith(mockConfigPath, 'utf-8')
233
- expect(config.enabled).toBe(false)
234
- expect(config.speed).toBe(1.5)
235
- expect(config.providers.minimax.apiKey).toBe('test-key')
236
- expect(config.providers.minimax.baseURL).toBe('https://api.minimax.io')
237
- })
238
-
239
- it('should merge user config with defaults', () => {
240
- const userConfig = {
241
- enabled: false
242
- // other fields missing
243
- }
244
-
245
- ;(fs.existsSync as jest.Mock).mockImplementation((p: string) => {
246
- return p === mockConfigPath || p === path.dirname(mockConfigPath)
247
- })
248
- ;(fs.readFileSync as jest.Mock).mockReturnValue(JSON.stringify(userConfig))
249
-
250
- const config = loadOrCreateConfig()
251
-
252
- expect(config.enabled).toBe(false)
253
- expect(config.autoPlay).toBe(DEFAULT_CONFIG.autoPlay)
254
- expect(config.speed).toBe(DEFAULT_CONFIG.speed)
255
- })
256
-
257
- it('should handle config file with comments', () => {
258
- const jsoncContent = `{
259
- // This is a comment
260
- "enabled": false,
261
- /* Another comment */
262
- "speed": 1.8
263
- }`
264
-
265
- ;(fs.existsSync as jest.Mock).mockImplementation((p: string) => {
266
- return p === mockConfigPath || p === path.dirname(mockConfigPath)
267
- })
268
- ;(fs.readFileSync as jest.Mock).mockReturnValue(jsoncContent)
269
-
270
- const config = loadOrCreateConfig()
271
-
272
- expect(config.enabled).toBe(false)
273
- expect(config.speed).toBe(1.8)
274
- })
275
-
276
- it('should use default config on parse error', () => {
277
- ;(fs.existsSync as jest.Mock).mockImplementation((p: string) => {
278
- return p === mockConfigPath || p === path.dirname(mockConfigPath)
279
- })
280
- ;(fs.readFileSync as jest.Mock).mockReturnValue('invalid json {')
281
-
282
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation()
283
- const config = loadOrCreateConfig()
284
-
285
- expect(consoleErrorSpy).toHaveBeenCalled()
286
- expect(config.providers.minimax.apiKey).toBe('')
287
- expect(config.providers.minimax.baseURL).toBe('')
288
-
289
- consoleErrorSpy.mockRestore()
290
- })
291
-
292
- it('should handle minimax provider partial config', () => {
293
- const userConfig = {
294
- providers: {
295
- minimax: {
296
- apiKey: 'my-key'
297
- // baseURL, voiceId, model, ttsModel, audioFormat missing
298
- }
299
- }
300
- }
301
-
302
- ;(fs.existsSync as jest.Mock).mockImplementation((p: string) => {
303
- return p === mockConfigPath || p === path.dirname(mockConfigPath)
304
- })
305
- ;(fs.readFileSync as jest.Mock).mockReturnValue(JSON.stringify(userConfig))
306
-
307
- const config = loadOrCreateConfig()
308
-
309
- expect(config.providers.minimax.apiKey).toBe('my-key')
310
- expect(config.providers.minimax.baseURL).toBe('')
311
- expect(config.providers.minimax.model).toBe('stream')
312
- expect(config.providers.minimax.ttsModel).toBe('speech-2.8-hd')
313
- expect(config.providers.minimax.audioFormat).toBe('mp3')
314
- })
315
-
316
- it('should set correct file permissions on new config', () => {
317
- ;(fs.existsSync as jest.Mock).mockReturnValue(false)
318
- ;(fs.mkdirSync as jest.Mock).mockReturnValue(undefined)
319
- ;(fs.writeFileSync as jest.Mock).mockReturnValue(undefined)
320
- ;(fs.chmodSync as jest.Mock).mockReturnValue(undefined)
321
-
322
- loadOrCreateConfig()
323
-
324
- expect(fs.chmodSync).toHaveBeenCalledWith(mockConfigPath, 0o600)
325
- })
326
- })
327
- })
@@ -1,201 +0,0 @@
1
- import { pluginInfo, toolNames, initialize, destroy, isStreamEnabled, getStreamReader } from '../src/index'
2
- import { registerProvider, unregisterProvider, BaseTTSProvider } from '../src/providers/base'
3
- import { TTSCapabilities } from '../src/core/types'
4
-
5
- class MockProvider extends BaseTTSProvider {
6
- name = 'mock'
7
- capabilities: TTSCapabilities = { speak: true, stream: true }
8
-
9
- protected async doSpeak(text: string, voice: string | undefined, model: any) {
10
- return {
11
- audioData: Buffer.from([1, 2, 3]),
12
- format: 'mp3',
13
- isStream: model === 'stream',
14
- duration: 1.0
15
- }
16
- }
17
- }
18
-
19
- describe('index module exports', () => {
20
- describe('pluginInfo', () => {
21
- it('should export correct plugin info', () => {
22
- expect(pluginInfo.name).toBe('ocosay')
23
- expect(pluginInfo.version).toBe('1.0.0')
24
- expect(pluginInfo.description).toContain('TTS')
25
- })
26
- })
27
-
28
- describe('toolNames', () => {
29
- it('should export array of tool names', () => {
30
- expect(Array.isArray(toolNames)).toBe(true)
31
- expect(toolNames.length).toBeGreaterThan(0)
32
- })
33
-
34
- it('should include tts_speak', () => {
35
- expect(toolNames).toContain('tts_speak')
36
- })
37
-
38
- it('should include tts_stop', () => {
39
- expect(toolNames).toContain('tts_stop')
40
- })
41
-
42
- it('should include tts_stream_speak', () => {
43
- expect(toolNames).toContain('tts_stream_speak')
44
- })
45
- })
46
- })
47
-
48
- describe('TuiEventBus integration', () => {
49
- const originalGlobal = global as any
50
- let mockProvider: MockProvider
51
-
52
- beforeEach(() => {
53
- mockProvider = new MockProvider()
54
- registerProvider('mock', mockProvider)
55
- })
56
-
57
- afterEach(async () => {
58
- delete originalGlobal.__opencode_tuieventbus__
59
- await destroy()
60
- unregisterProvider('mock')
61
- })
62
-
63
- describe('P1-1: eventBus available', () => {
64
- it('should subscribe to message.part.delta and message.part.end events', async () => {
65
- const mockOn = jest.fn()
66
- const mockOff = jest.fn()
67
- const MockTuiEventBus = jest.fn().mockImplementation(() => ({
68
- on: mockOn,
69
- off: mockOff
70
- }))
71
- originalGlobal.__opencode_tuieventbus__ = MockTuiEventBus
72
-
73
- await initialize({
74
- autoRead: true,
75
- defaultProvider: 'mock'
76
- })
77
-
78
- expect(mockOn).toHaveBeenCalledWith('message.part.delta', expect.any(Function))
79
- expect(mockOn).toHaveBeenCalledWith('message.part.end', expect.any(Function))
80
- })
81
-
82
- it('should handle delta events via streamReader', async () => {
83
- const mockOn = jest.fn()
84
- const mockOff = jest.fn()
85
- let deltaHandler: Function | undefined
86
-
87
- mockOn.mockImplementation((event: string, handler: Function) => {
88
- if (event === 'message.part.delta') {
89
- deltaHandler = handler
90
- }
91
- })
92
-
93
- const MockTuiEventBus = jest.fn().mockImplementation(() => ({
94
- on: mockOn,
95
- off: mockOff
96
- }))
97
- originalGlobal.__opencode_tuieventbus__ = MockTuiEventBus
98
-
99
- await initialize({
100
- autoRead: true,
101
- defaultProvider: 'mock'
102
- })
103
-
104
- const mockEvent = {
105
- sessionId: 'session-1',
106
- messageId: 'msg-1',
107
- partId: 'part-1',
108
- properties: { delta: 'Hello' }
109
- }
110
-
111
- expect(deltaHandler).toBeDefined()
112
- deltaHandler!(mockEvent)
113
- })
114
- })
115
-
116
- describe('P1-1: eventBus unavailable (graceful fallback)', () => {
117
- it('should not throw when eventBus is not available', async () => {
118
- delete originalGlobal.__opencode_tuieventbus__
119
-
120
- await expect(initialize({
121
- autoRead: true,
122
- defaultProvider: 'mock'
123
- })).resolves.not.toThrow()
124
- })
125
-
126
- it('should have stream components initialized even without eventBus', async () => {
127
- delete originalGlobal.__opencode_tuieventbus__
128
-
129
- await initialize({
130
- autoRead: true,
131
- defaultProvider: 'mock'
132
- })
133
-
134
- expect(isStreamEnabled()).toBe(true)
135
- expect(getStreamReader()).toBeDefined()
136
- })
137
- })
138
- })
139
-
140
- describe('initialize() idempotency', () => {
141
- let mockProvider: MockProvider
142
-
143
- beforeEach(() => {
144
- mockProvider = new MockProvider()
145
- registerProvider('mock', mockProvider)
146
- })
147
-
148
- afterEach(async () => {
149
- await destroy()
150
- unregisterProvider('mock')
151
- })
152
-
153
- describe('P1-2: repeated initialize() calls', () => {
154
- it('should be safe to call initialize() multiple times', async () => {
155
- await initialize({
156
- defaultProvider: 'mock'
157
- })
158
-
159
- await expect(initialize({
160
- defaultProvider: 'mock'
161
- })).resolves.not.toThrow()
162
-
163
- await expect(initialize({
164
- defaultProvider: 'mock'
165
- })).resolves.not.toThrow()
166
- })
167
-
168
- it('should not re-initialize components on repeated calls', async () => {
169
- await initialize({
170
- autoRead: true,
171
- defaultProvider: 'mock'
172
- })
173
-
174
- const firstStreamReader = getStreamReader()
175
- expect(firstStreamReader).toBeDefined()
176
-
177
- await initialize({
178
- defaultProvider: 'mock'
179
- })
180
-
181
- const secondStreamReader = getStreamReader()
182
- expect(secondStreamReader).toBe(firstStreamReader)
183
- })
184
-
185
- it('should return early if already initialized', async () => {
186
- const spyInitialize = jest.spyOn(mockProvider, 'initialize')
187
-
188
- await initialize({
189
- defaultProvider: 'mock'
190
- })
191
-
192
- spyInitialize.mockClear()
193
-
194
- await initialize({
195
- defaultProvider: 'mock'
196
- })
197
-
198
- expect(spyInitialize).not.toHaveBeenCalled()
199
- })
200
- })
201
- })
@@ -1,6 +0,0 @@
1
- /**
2
- * 真实 TTS 集成测试
3
- * 需要设置环境变量: MINIMAX_API_KEY
4
- */
5
- export {};
6
- //# sourceMappingURL=integration-test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"integration-test.d.ts","sourceRoot":"","sources":["integration-test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -1,84 +0,0 @@
1
- const { initialize } = require('../dist/index')
2
- const { getProvider } = require('../dist/providers/base')
3
-
4
- const API_KEY = process.env.MINIMAX_API_KEY
5
-
6
- if (!API_KEY) {
7
- console.error('❌ 请设置环境变量 MINIMAX_API_KEY')
8
- console.log('示例: MINIMAX_API_KEY=your-api-key node tests/integration-test.js')
9
- process.exit(1)
10
- }
11
-
12
- async function test() {
13
- console.log('🔧 初始化 ocosay...')
14
-
15
- await initialize({
16
- providers: {
17
- minimax: {
18
- apiKey: API_KEY,
19
- voiceId: 'male-qn-qingse',
20
- model: 'stream'
21
- }
22
- }
23
- })
24
-
25
- console.log('✅ 初始化成功!\n')
26
-
27
- console.log('📝 测试 1: 同步合成 (model: sync)')
28
- try {
29
- const provider = getProvider('minimax')
30
- const result = await provider.speak('你好,这是同步合成测试!', { model: 'sync' })
31
- const data = Buffer.isBuffer(result.audioData) ? result.audioData : Buffer.from([])
32
- console.log('✅ 同步合成成功! 音频大小: ' + data.length + ' bytes, 格式: ' + result.format)
33
- } catch (error) {
34
- console.error('❌ 同步合成失败:', error.message || error)
35
- }
36
-
37
- console.log('')
38
-
39
- console.log('📝 测试 2: 流式合成 (model: stream)')
40
- try {
41
- const provider = getProvider('minimax')
42
- const result = await provider.speak('你好,这是流式合成测试!', { model: 'stream' })
43
- const data = Buffer.isBuffer(result.audioData) ? result.audioData : Buffer.from([])
44
- console.log('✅ 流式合成成功! 音频大小: ' + data.length + ' bytes, 格式: ' + result.format)
45
- } catch (error) {
46
- console.error('❌ 流式合成失败:', error.message || error)
47
- }
48
-
49
- console.log('')
50
-
51
- console.log('📝 测试 3: 异步合成 (model: async)')
52
- try {
53
- const provider = getProvider('minimax')
54
- const result = await provider.speak('你好,这是异步合成测试!', { model: 'async' })
55
- const data = Buffer.isBuffer(result.audioData) ? result.audioData : Buffer.from([])
56
- console.log('✅ 异步合成成功! 音频大小: ' + data.length + ' bytes, 格式: ' + result.format)
57
- } catch (error) {
58
- console.error('❌ 异步合成失败:', error.message || error)
59
- }
60
-
61
- console.log('')
62
-
63
- console.log('📝 测试 4: 列出音色')
64
- try {
65
- const provider = getProvider('minimax')
66
- const voices = await provider.listVoices()
67
- console.log('✅ 获取到 ' + voices.length + ' 个音色:')
68
- voices.slice(0, 5).forEach(v => {
69
- console.log(' - ' + v.id + ': ' + v.name + ' (' + (v.language || 'unknown') + ')')
70
- })
71
- } catch (error) {
72
- console.error('❌ 列出音色失败:', error.message || error)
73
- }
74
-
75
- console.log('')
76
- console.log('🎉 集成测试完成!')
77
-
78
- process.exit(0)
79
- }
80
-
81
- test().catch(err => {
82
- console.error('❌ 测试失败:', err)
83
- process.exit(1)
84
- })
@@ -1 +0,0 @@
1
- {"version":3,"file":"integration-test.js","sourceRoot":"","sources":["integration-test.ts"],"names":[],"mappings":"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"}