@mingxy/ocosay 1.0.35 → 1.1.1
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/dist/config.js +4 -1
- package/dist/core/backends/index.js +8 -3
- package/dist/core/notification.d.ts +27 -0
- package/dist/core/notification.js +86 -0
- package/dist/core/speaker.js +6 -35
- package/dist/core/stream-reader.js +2 -1
- package/dist/core/streaming-synthesizer.js +2 -0
- package/dist/index.d.ts +1 -13
- package/dist/index.js +4 -23
- package/dist/package.json +2 -2
- package/dist/plugin.js +990 -586
- package/dist/providers/minimax.js +43 -20
- package/dist/services/notification-service.d.ts +17 -0
- package/dist/services/notification-service.js +74 -0
- package/dist/services/speaker-service.d.ts +34 -0
- package/dist/services/speaker-service.js +74 -0
- package/dist/services/streaming-service.d.ts +109 -0
- package/dist/services/streaming-service.js +281 -0
- package/dist/tools/tts.js +32 -19
- package/dist/utils/logger.d.ts +6 -0
- package/dist/utils/logger.js +42 -6
- package/package.json +2 -2
- package/.idea/UniappTool.xml +0 -10
- package/.idea/inspectionProfiles/profiles_settings.xml +0 -5
- package/.idea/modules.xml +0 -8
- package/.idea/ocosay.iml +0 -12
- package/.idea/vcs.xml +0 -6
- package/.sisyphus/boulder.json +0 -23
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/core/backends/afplay-backend.d.ts.map +0 -1
- package/dist/core/backends/afplay-backend.js.map +0 -1
- package/dist/core/backends/aplay-backend.d.ts.map +0 -1
- package/dist/core/backends/aplay-backend.js.map +0 -1
- package/dist/core/backends/base.d.ts.map +0 -1
- package/dist/core/backends/base.js.map +0 -1
- package/dist/core/backends/howler-backend.d.ts.map +0 -1
- package/dist/core/backends/howler-backend.js.map +0 -1
- package/dist/core/backends/index.d.ts.map +0 -1
- package/dist/core/backends/index.js.map +0 -1
- package/dist/core/backends/naudiodon-backend.d.ts.map +0 -1
- package/dist/core/backends/naudiodon-backend.js.map +0 -1
- package/dist/core/backends/powershell-backend.d.ts.map +0 -1
- package/dist/core/backends/powershell-backend.js.map +0 -1
- package/dist/core/logger.d.ts.map +0 -1
- package/dist/core/logger.js.map +0 -1
- package/dist/core/player.d.ts.map +0 -1
- package/dist/core/player.js.map +0 -1
- package/dist/core/speaker.d.ts.map +0 -1
- package/dist/core/speaker.js.map +0 -1
- package/dist/core/stream-player.d.ts.map +0 -1
- package/dist/core/stream-player.js.map +0 -1
- package/dist/core/stream-reader.d.ts.map +0 -1
- package/dist/core/stream-reader.js.map +0 -1
- package/dist/core/streaming-synthesizer.d.ts.map +0 -1
- package/dist/core/streaming-synthesizer.js.map +0 -1
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/plugin.d.ts.map +0 -1
- package/dist/plugin.js.map +0 -7
- package/dist/providers/base.d.ts.map +0 -1
- package/dist/providers/base.js.map +0 -1
- package/dist/providers/minimax.d.ts.map +0 -1
- package/dist/providers/minimax.js.map +0 -1
- package/dist/tools/tts.d.ts.map +0 -1
- package/dist/tools/tts.js.map +0 -1
- package/dist/types/config.d.ts.map +0 -1
- package/dist/types/config.js.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js.map +0 -1
package/dist/config.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as os from 'os';
|
|
4
|
-
import {
|
|
4
|
+
import { createModuleLogger } from './utils/logger';
|
|
5
|
+
const logger = createModuleLogger('Config');
|
|
5
6
|
// 默认配置
|
|
6
7
|
const DEFAULT_CONFIG = {
|
|
7
8
|
enabled: true,
|
|
@@ -106,6 +107,7 @@ export function loadOrCreateConfig() {
|
|
|
106
107
|
fs.mkdirSync(configDir, { recursive: true });
|
|
107
108
|
}
|
|
108
109
|
catch (err) {
|
|
110
|
+
logger.error({ err }, 'failed to create config directory');
|
|
109
111
|
throw new Error(`[ocosay] 无法创建配置目录 ${configDir}: ${err}`);
|
|
110
112
|
}
|
|
111
113
|
}
|
|
@@ -123,6 +125,7 @@ export function loadOrCreateConfig() {
|
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
127
|
catch (err) {
|
|
128
|
+
logger.error({ err }, 'failed to write config file');
|
|
126
129
|
throw new Error(`[ocosay] cannot write config file ${CONFIG_PATH}: ${err}`);
|
|
127
130
|
}
|
|
128
131
|
logger.info({ path: CONFIG_PATH }, 'config file created');
|
|
@@ -12,6 +12,7 @@ import { AfplayBackend } from './afplay-backend';
|
|
|
12
12
|
import { AplayBackend } from './aplay-backend';
|
|
13
13
|
import { PowerShellBackend } from './powershell-backend';
|
|
14
14
|
import { HowlerBackend } from './howler-backend';
|
|
15
|
+
import { logger } from '../../utils/logger';
|
|
15
16
|
/**
|
|
16
17
|
* 后端类型枚举
|
|
17
18
|
*/
|
|
@@ -33,7 +34,8 @@ async function tryLoadNaudiodon() {
|
|
|
33
34
|
naudiodonCache = await import('naudiodon');
|
|
34
35
|
return naudiodonCache;
|
|
35
36
|
}
|
|
36
|
-
catch (
|
|
37
|
+
catch (err) {
|
|
38
|
+
logger.warn({ err }, 'failed to load naudiodon module');
|
|
37
39
|
naudiodonCache = false;
|
|
38
40
|
return null;
|
|
39
41
|
}
|
|
@@ -43,7 +45,8 @@ function isNaudiodonAvailable() {
|
|
|
43
45
|
require.resolve('naudiodon');
|
|
44
46
|
return true;
|
|
45
47
|
}
|
|
46
|
-
catch (
|
|
48
|
+
catch (err) {
|
|
49
|
+
logger.debug({ err }, 'naudiodon not available');
|
|
47
50
|
return false;
|
|
48
51
|
}
|
|
49
52
|
}
|
|
@@ -75,7 +78,9 @@ export function createBackend(type = BackendType.AUTO, options = {}) {
|
|
|
75
78
|
return new NaudiodonBackend(options);
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
|
-
catch (
|
|
81
|
+
catch (err) {
|
|
82
|
+
logger.warn({ err }, 'failed to initialize naudiodon backend');
|
|
83
|
+
}
|
|
79
84
|
}
|
|
80
85
|
switch (platform) {
|
|
81
86
|
case 'darwin':
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type ToastVariant = 'success' | 'error' | 'info' | 'warning';
|
|
2
|
+
export interface ToastOptions {
|
|
3
|
+
title: string;
|
|
4
|
+
message: string;
|
|
5
|
+
variant?: ToastVariant;
|
|
6
|
+
duration?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* NotificationService - 统一 Toast 通知管理
|
|
10
|
+
* 参照 DCP 实现,不做防御性检查,直接调用并用 try-catch 处理
|
|
11
|
+
*/
|
|
12
|
+
declare class NotificationService {
|
|
13
|
+
private tui;
|
|
14
|
+
private pendingToasts;
|
|
15
|
+
private retryTimer?;
|
|
16
|
+
setTui(tui: any): void;
|
|
17
|
+
showToast(options: ToastOptions): boolean;
|
|
18
|
+
private scheduleRetry;
|
|
19
|
+
private flushPending;
|
|
20
|
+
success(title: string, message: string, duration?: number): boolean;
|
|
21
|
+
error(title: string, message: string, duration?: number): boolean;
|
|
22
|
+
info(title: string, message: string, duration?: number): boolean;
|
|
23
|
+
warning(title: string, message: string, duration?: number): boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare const notificationService: NotificationService;
|
|
26
|
+
export default notificationService;
|
|
27
|
+
//# sourceMappingURL=notification.d.ts.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// src/core/notification.ts
|
|
2
|
+
import { createModuleLogger } from '../utils/logger';
|
|
3
|
+
const logger = createModuleLogger('NotificationService');
|
|
4
|
+
/**
|
|
5
|
+
* NotificationService - 统一 Toast 通知管理
|
|
6
|
+
* 参照 DCP 实现,不做防御性检查,直接调用并用 try-catch 处理
|
|
7
|
+
*/
|
|
8
|
+
class NotificationService {
|
|
9
|
+
tui = null;
|
|
10
|
+
pendingToasts = [];
|
|
11
|
+
retryTimer;
|
|
12
|
+
setTui(tui) {
|
|
13
|
+
this.tui = tui;
|
|
14
|
+
logger.debug('tui reference set');
|
|
15
|
+
this.flushPending();
|
|
16
|
+
}
|
|
17
|
+
showToast(options) {
|
|
18
|
+
const { title, message, variant = 'info', duration = 5000 } = options;
|
|
19
|
+
if (!this.tui) {
|
|
20
|
+
logger.debug({ title }, 'tui not ready, queueing toast');
|
|
21
|
+
this.pendingToasts.push(options);
|
|
22
|
+
this.scheduleRetry();
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
// 参照 DCP 格式:{ body: { title, message, variant, duration } }
|
|
27
|
+
this.tui.showToast({
|
|
28
|
+
body: {
|
|
29
|
+
title,
|
|
30
|
+
message,
|
|
31
|
+
variant,
|
|
32
|
+
duration,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
logger.debug({ title, variant }, 'toast shown');
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
// 参照 DCP:捕获异常但不抛出
|
|
40
|
+
logger.warn({ err, title }, 'toast call failed, queueing for retry');
|
|
41
|
+
this.pendingToasts.push(options);
|
|
42
|
+
this.scheduleRetry();
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
scheduleRetry() {
|
|
47
|
+
if (this.retryTimer)
|
|
48
|
+
return;
|
|
49
|
+
this.retryTimer = setTimeout(() => {
|
|
50
|
+
this.retryTimer = undefined;
|
|
51
|
+
this.flushPending();
|
|
52
|
+
}, 2000);
|
|
53
|
+
}
|
|
54
|
+
flushPending() {
|
|
55
|
+
if (this.pendingToasts.length === 0 || !this.tui)
|
|
56
|
+
return;
|
|
57
|
+
logger.info({ count: this.pendingToasts.length }, 'flushing pending toasts');
|
|
58
|
+
const pending = [...this.pendingToasts];
|
|
59
|
+
this.pendingToasts = []; // 乐观清空
|
|
60
|
+
for (const toast of pending) {
|
|
61
|
+
try {
|
|
62
|
+
this.showToast(toast);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
// 保护性重新加入队列
|
|
66
|
+
this.pendingToasts.push(toast);
|
|
67
|
+
logger.warn({ err }, 'showToast threw unexpected error, re-queued');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
success(title, message, duration) {
|
|
72
|
+
return this.showToast({ title, message, variant: 'success', duration });
|
|
73
|
+
}
|
|
74
|
+
error(title, message, duration) {
|
|
75
|
+
return this.showToast({ title, message, variant: 'error', duration });
|
|
76
|
+
}
|
|
77
|
+
info(title, message, duration) {
|
|
78
|
+
return this.showToast({ title, message, variant: 'info', duration });
|
|
79
|
+
}
|
|
80
|
+
warning(title, message, duration) {
|
|
81
|
+
return this.showToast({ title, message, variant: 'warning', duration });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export const notificationService = new NotificationService();
|
|
85
|
+
export default notificationService;
|
|
86
|
+
//# sourceMappingURL=notification.js.map
|
package/dist/core/speaker.js
CHANGED
|
@@ -6,30 +6,9 @@ import { EventEmitter } from 'events';
|
|
|
6
6
|
import { TTSError, TTSErrorCode } from './types';
|
|
7
7
|
import { getProvider, listProviders, hasProvider } from '../providers/base';
|
|
8
8
|
import { AudioPlayer } from './player';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* 适配用户期望的格式: toast({ title, body, type })
|
|
13
|
-
* 注意:必须保持方法调用形式 (tui.showToast()) 而不是提取方法后调用 (showToastFn()),
|
|
14
|
-
* 否则 this 上下文丢失导致 OpenCode 内部 this._client 为 undefined
|
|
15
|
-
*/
|
|
16
|
-
function toast(options) {
|
|
17
|
-
const tui = global.__opencode_tui__;
|
|
18
|
-
if (tui?.showToast) {
|
|
19
|
-
try {
|
|
20
|
-
// SDK期望直接传参: { title, message, variant, duration },不是 { body: {...} }
|
|
21
|
-
tui.showToast({
|
|
22
|
-
title: options.title,
|
|
23
|
-
message: options.body,
|
|
24
|
-
variant: options.type,
|
|
25
|
-
duration: 3000
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
catch (err) {
|
|
29
|
-
logger.warn({ err }, 'toast call failed');
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
9
|
+
import { createModuleLogger } from '../utils/logger';
|
|
10
|
+
import { notificationService } from './notification';
|
|
11
|
+
const logger = createModuleLogger('Speaker');
|
|
33
12
|
/**
|
|
34
13
|
* Speaker - TTS 统一调用入口类
|
|
35
14
|
* 封装 Provider 和 Player,提供简洁的 speak/pause/resume/stop API
|
|
@@ -50,12 +29,7 @@ export class Speaker extends EventEmitter {
|
|
|
50
29
|
onEnd: () => {
|
|
51
30
|
this.isSpeaking = false;
|
|
52
31
|
this.emit('end', this.currentText);
|
|
53
|
-
|
|
54
|
-
toast({
|
|
55
|
-
title: 'TTS playback success',
|
|
56
|
-
body: 'Audio generated and playing',
|
|
57
|
-
type: 'info'
|
|
58
|
-
});
|
|
32
|
+
notificationService.info('TTS playback success', 'Audio generated and playing');
|
|
59
33
|
},
|
|
60
34
|
onError: (error) => this.emit('error', error),
|
|
61
35
|
onPause: () => {
|
|
@@ -114,13 +88,10 @@ export class Speaker extends EventEmitter {
|
|
|
114
88
|
}
|
|
115
89
|
catch (error) {
|
|
116
90
|
this.isSpeaking = false;
|
|
91
|
+
logger.error({ error }, 'speak failed');
|
|
117
92
|
// 显示播放失败 Toast
|
|
118
93
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
119
|
-
|
|
120
|
-
title: 'TTS playback error',
|
|
121
|
-
body: errorMessage,
|
|
122
|
-
type: 'error'
|
|
123
|
-
});
|
|
94
|
+
notificationService.error('TTS playback error', errorMessage);
|
|
124
95
|
if (error instanceof TTSError) {
|
|
125
96
|
this.emit('error', error);
|
|
126
97
|
throw error;
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { EventEmitter } from 'events';
|
|
10
10
|
import { StreamState } from './types';
|
|
11
|
-
import {
|
|
11
|
+
import { createModuleLogger } from '../utils/logger';
|
|
12
|
+
const logger = createModuleLogger('StreamReader');
|
|
12
13
|
export class StreamReader extends EventEmitter {
|
|
13
14
|
bufferSize;
|
|
14
15
|
bufferTimeout;
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { EventEmitter } from 'events';
|
|
13
13
|
import { TTSError } from './types';
|
|
14
|
+
import { logger } from '../utils/logger';
|
|
14
15
|
export class StreamingSynthesizer extends EventEmitter {
|
|
15
16
|
options;
|
|
16
17
|
audioChunks = [];
|
|
@@ -38,6 +39,7 @@ export class StreamingSynthesizer extends EventEmitter {
|
|
|
38
39
|
this.emit('done');
|
|
39
40
|
}
|
|
40
41
|
catch (error) {
|
|
42
|
+
logger.error({ error }, 'synthesize failed');
|
|
41
43
|
const ttsError = error instanceof TTSError
|
|
42
44
|
? error
|
|
43
45
|
: new TTSError(error instanceof Error ? error.message : 'Synthesis failed', 'UNKNOWN', this.options.provider.name, error);
|
package/dist/index.d.ts
CHANGED
|
@@ -35,19 +35,7 @@ export declare function getStreamReader(): StreamReader | undefined;
|
|
|
35
35
|
export declare function getStreamingSynthesizer(): StreamingSynthesizer | undefined;
|
|
36
36
|
export declare function getStreamPlayer(): StreamPlayer | undefined;
|
|
37
37
|
export declare function destroy(): Promise<void>;
|
|
38
|
-
/**
|
|
39
|
-
* 显示 Toast 通知
|
|
40
|
-
* 注意:必须保持方法调用形式 (tui.showToast()) 而不是提取方法后调用 (showToastFn()),
|
|
41
|
-
* 否则 this 上下文丢失导致 OpenCode 内部 this._client 为 undefined
|
|
42
|
-
*/
|
|
43
|
-
export declare function showToast(options: {
|
|
44
|
-
body: {
|
|
45
|
-
title: string;
|
|
46
|
-
message: string;
|
|
47
|
-
variant: 'success' | 'error' | 'info';
|
|
48
|
-
duration?: number;
|
|
49
|
-
};
|
|
50
|
-
}): void;
|
|
51
38
|
export { handleToolCall };
|
|
39
|
+
export { notificationService } from './core/notification.js';
|
|
52
40
|
export declare const toolNames: string[];
|
|
53
41
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -172,34 +172,15 @@ export async function destroy() {
|
|
|
172
172
|
try {
|
|
173
173
|
getProvider(providerName)?.destroy();
|
|
174
174
|
}
|
|
175
|
-
catch (e) { }
|
|
176
|
-
}
|
|
177
|
-
initialized = false;
|
|
178
|
-
autoReadEnabled = false;
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* 显示 Toast 通知
|
|
182
|
-
* 注意:必须保持方法调用形式 (tui.showToast()) 而不是提取方法后调用 (showToastFn()),
|
|
183
|
-
* 否则 this 上下文丢失导致 OpenCode 内部 this._client 为 undefined
|
|
184
|
-
*/
|
|
185
|
-
export function showToast(options) {
|
|
186
|
-
const tui = global.__opencode_tui__;
|
|
187
|
-
if (tui?.showToast) {
|
|
188
|
-
try {
|
|
189
|
-
// SDK期望直接传参: { title, message, variant, duration },不是嵌套在body里
|
|
190
|
-
tui.showToast({
|
|
191
|
-
title: options.body.title,
|
|
192
|
-
message: options.body.message,
|
|
193
|
-
variant: options.body.variant,
|
|
194
|
-
duration: options.body.duration
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
175
|
catch (err) {
|
|
198
|
-
logger.
|
|
176
|
+
logger.error({ err }, 'provider destroy failed');
|
|
199
177
|
}
|
|
200
178
|
}
|
|
179
|
+
initialized = false;
|
|
180
|
+
autoReadEnabled = false;
|
|
201
181
|
}
|
|
202
182
|
export { handleToolCall };
|
|
183
|
+
export { notificationService } from './core/notification.js';
|
|
203
184
|
export const toolNames = [
|
|
204
185
|
'tts_speak',
|
|
205
186
|
'tts_stop',
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mingxy/ocosay",
|
|
3
|
-
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "OpenCode TTS 播放插件 - 支持豆包模式边接收边朗读",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/plugin.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"opencode",
|
|
38
38
|
"plugin"
|
|
39
39
|
],
|
|
40
|
-
"author": "",
|
|
40
|
+
"author": "mingxy",
|
|
41
41
|
"license": "MIT",
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@opencode-ai/plugin": "^1.3.15",
|