@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.
Files changed (72) hide show
  1. package/dist/config.js +4 -1
  2. package/dist/core/backends/index.js +8 -3
  3. package/dist/core/notification.d.ts +27 -0
  4. package/dist/core/notification.js +86 -0
  5. package/dist/core/speaker.js +6 -35
  6. package/dist/core/stream-reader.js +2 -1
  7. package/dist/core/streaming-synthesizer.js +2 -0
  8. package/dist/index.d.ts +1 -13
  9. package/dist/index.js +4 -23
  10. package/dist/package.json +2 -2
  11. package/dist/plugin.js +990 -586
  12. package/dist/providers/minimax.js +43 -20
  13. package/dist/services/notification-service.d.ts +17 -0
  14. package/dist/services/notification-service.js +74 -0
  15. package/dist/services/speaker-service.d.ts +34 -0
  16. package/dist/services/speaker-service.js +74 -0
  17. package/dist/services/streaming-service.d.ts +109 -0
  18. package/dist/services/streaming-service.js +281 -0
  19. package/dist/tools/tts.js +32 -19
  20. package/dist/utils/logger.d.ts +6 -0
  21. package/dist/utils/logger.js +42 -6
  22. package/package.json +2 -2
  23. package/.idea/UniappTool.xml +0 -10
  24. package/.idea/inspectionProfiles/profiles_settings.xml +0 -5
  25. package/.idea/modules.xml +0 -8
  26. package/.idea/ocosay.iml +0 -12
  27. package/.idea/vcs.xml +0 -6
  28. package/.sisyphus/boulder.json +0 -23
  29. package/dist/config.d.ts.map +0 -1
  30. package/dist/config.js.map +0 -1
  31. package/dist/core/backends/afplay-backend.d.ts.map +0 -1
  32. package/dist/core/backends/afplay-backend.js.map +0 -1
  33. package/dist/core/backends/aplay-backend.d.ts.map +0 -1
  34. package/dist/core/backends/aplay-backend.js.map +0 -1
  35. package/dist/core/backends/base.d.ts.map +0 -1
  36. package/dist/core/backends/base.js.map +0 -1
  37. package/dist/core/backends/howler-backend.d.ts.map +0 -1
  38. package/dist/core/backends/howler-backend.js.map +0 -1
  39. package/dist/core/backends/index.d.ts.map +0 -1
  40. package/dist/core/backends/index.js.map +0 -1
  41. package/dist/core/backends/naudiodon-backend.d.ts.map +0 -1
  42. package/dist/core/backends/naudiodon-backend.js.map +0 -1
  43. package/dist/core/backends/powershell-backend.d.ts.map +0 -1
  44. package/dist/core/backends/powershell-backend.js.map +0 -1
  45. package/dist/core/logger.d.ts.map +0 -1
  46. package/dist/core/logger.js.map +0 -1
  47. package/dist/core/player.d.ts.map +0 -1
  48. package/dist/core/player.js.map +0 -1
  49. package/dist/core/speaker.d.ts.map +0 -1
  50. package/dist/core/speaker.js.map +0 -1
  51. package/dist/core/stream-player.d.ts.map +0 -1
  52. package/dist/core/stream-player.js.map +0 -1
  53. package/dist/core/stream-reader.d.ts.map +0 -1
  54. package/dist/core/stream-reader.js.map +0 -1
  55. package/dist/core/streaming-synthesizer.d.ts.map +0 -1
  56. package/dist/core/streaming-synthesizer.js.map +0 -1
  57. package/dist/core/types.d.ts.map +0 -1
  58. package/dist/core/types.js.map +0 -1
  59. package/dist/index.d.ts.map +0 -1
  60. package/dist/index.js.map +0 -1
  61. package/dist/plugin.d.ts.map +0 -1
  62. package/dist/plugin.js.map +0 -7
  63. package/dist/providers/base.d.ts.map +0 -1
  64. package/dist/providers/base.js.map +0 -1
  65. package/dist/providers/minimax.d.ts.map +0 -1
  66. package/dist/providers/minimax.js.map +0 -1
  67. package/dist/tools/tts.d.ts.map +0 -1
  68. package/dist/tools/tts.js.map +0 -1
  69. package/dist/types/config.d.ts.map +0 -1
  70. package/dist/types/config.js.map +0 -1
  71. package/dist/utils/logger.d.ts.map +0 -1
  72. 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 { logger } from './utils/logger';
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 (e) {
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 (e) {
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 (e) { }
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
@@ -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 { logger } from '../utils/logger';
10
- /**
11
- * Toast 函数 - 统一的Toast通知封装
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
- // 显示播放成功 Toast
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
- toast({
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 { logger } from '../utils/logger';
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.warn({ err }, 'showToast failed');
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
- "version": "1.0.34",
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",