@mingxy/ocosay 1.1.8 → 1.1.10

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.
@@ -75,7 +75,11 @@ export function createBackend(type = BackendType.AUTO, options = {}) {
75
75
  try {
76
76
  const naudiodon = require('naudiodon');
77
77
  if (naudiodon) {
78
- return new NaudiodonBackend(options);
78
+ const devices = naudiodon.getDevices();
79
+ if (devices && devices.length > 0) {
80
+ return new NaudiodonBackend(options);
81
+ }
82
+ logger.debug('naudiodon has no audio devices, skipping');
79
83
  }
80
84
  }
81
85
  catch (err) {
@@ -93,7 +97,7 @@ export function createBackend(type = BackendType.AUTO, options = {}) {
93
97
  case 'win32':
94
98
  return new PowerShellBackend(options);
95
99
  default:
96
- throw new Error(`Unsupported platform: ${platform}`);
100
+ return new HowlerBackend(options);
97
101
  }
98
102
  }
99
103
  function createBackendByType(type, options) {
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mingxy/ocosay",
3
- "version": "1.1.8",
3
+ "version": "1.1.10",
4
4
  "description": "OpenCode TTS 播放插件 - 支持豆包模式边接收边朗读",
5
5
  "type": "module",
6
6
  "main": "dist/plugin.js",
package/dist/plugin.js CHANGED
@@ -7485,7 +7485,11 @@ function createBackend(type = "auto" /* AUTO */, options = {}) {
7485
7485
  try {
7486
7486
  const naudiodon = __require("naudiodon");
7487
7487
  if (naudiodon) {
7488
- return new NaudiodonBackend(options);
7488
+ const devices = naudiodon.getDevices();
7489
+ if (devices && devices.length > 0) {
7490
+ return new NaudiodonBackend(options);
7491
+ }
7492
+ logger.debug("naudiodon has no audio devices, skipping");
7489
7493
  }
7490
7494
  } catch (err) {
7491
7495
  logger.error({ err }, "failed to initialize naudiodon backend");
@@ -7502,7 +7506,7 @@ function createBackend(type = "auto" /* AUTO */, options = {}) {
7502
7506
  case "win32":
7503
7507
  return new PowerShellBackend(options);
7504
7508
  default:
7505
- throw new Error(`Unsupported platform: ${platform}`);
7509
+ return new HowlerBackend(options);
7506
7510
  }
7507
7511
  }
7508
7512
  function createBackendByType(type, options) {
@@ -9594,7 +9598,7 @@ async function ensureNaudiodonCompiled() {
9594
9598
  return;
9595
9599
  } catch {
9596
9600
  logger8.info("naudiodon not compiled, will attempt to compile");
9597
- notificationService.info("\u6B63\u5728\u7F16\u8BD1 naudiodon...", "Ocosay \u97F3\u9891\u540E\u7AEF");
9601
+ notificationService.info("\u6B63\u5728\u7F16\u8BD1 naudiodon...", "Ocosay \u97F3\u9891\u540E\u7AEF", 5e3);
9598
9602
  }
9599
9603
  try {
9600
9604
  const naudiodonPath = dirname2(require2.resolve("naudiodon"));
@@ -9604,54 +9608,137 @@ async function ensureNaudiodonCompiled() {
9604
9608
  stdio: "inherit"
9605
9609
  });
9606
9610
  logger8.info("naudiodon compiled successfully");
9607
- notificationService.success("naudiodon \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA");
9611
+ notificationService.success("naudiodon \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
9608
9612
  } catch (err) {
9609
9613
  logger8.warn({ err }, "naudiodon rebuild failed, checking for PortAudio");
9610
- notificationService.warning("naudiodon \u7F16\u8BD1\u5931\u8D25", "\u6B63\u5728\u5C1D\u8BD5\u5B89\u88C5 PortAudio...");
9611
- const installed = installPortAudio();
9612
- if (installed) {
9614
+ notificationService.warning("naudiodon \u7F16\u8BD1\u5931\u8D25", "\u6B63\u5728\u5C1D\u8BD5\u5B89\u88C5 PortAudio...", 5e3);
9615
+ const installed = await installPortAudio();
9616
+ if (installed.success) {
9613
9617
  try {
9614
9618
  const naudiodonPath = dirname2(require2.resolve("naudiodon"));
9615
- notificationService.info("\u6B63\u5728\u91CD\u65B0\u7F16\u8BD1 naudiodon...", "Ocosay");
9619
+ notificationService.info("\u6B63\u5728\u91CD\u65B0\u7F16\u8BD1 naudiodon...", "Ocosay", 5e3);
9616
9620
  execSync("npm rebuild naudiodon", {
9617
9621
  cwd: naudiodonPath,
9618
9622
  stdio: "inherit"
9619
9623
  });
9620
9624
  logger8.info("naudiodon compiled successfully after PortAudio install");
9621
- notificationService.success("naudiodon \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA");
9625
+ notificationService.success("naudiodon \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
9622
9626
  } catch (retryErr) {
9623
9627
  logger8.error({ err: retryErr }, "naudiodon compile failed even after PortAudio install");
9624
- notificationService.error("naudiodon \u7F16\u8BD1\u5931\u8D25", "\u81EA\u52A8\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u5C1D\u8BD5\u624B\u52A8\u5B89\u88C5");
9628
+ notificationService.error("naudiodon \u7F16\u8BD1\u5931\u8D25", "\u81EA\u52A8\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u5C1D\u8BD5\u624B\u52A8\u5B89\u88C5", 8e3);
9625
9629
  markNaudiodonSkipped();
9626
9630
  }
9627
9631
  } else {
9628
9632
  logger8.error("PortAudio install failed");
9629
- notificationService.error("PortAudio \u5B89\u88C5\u5931\u8D25", "\u81EA\u52A8\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u5C1D\u8BD5\u624B\u52A8\u5B89\u88C5");
9633
+ notificationService.error("PortAudio \u5B89\u88C5\u5931\u8D25", "\u81EA\u52A8\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u5C1D\u8BD5\u624B\u52A8\u5B89\u88C5", 8e3);
9630
9634
  markNaudiodonSkipped();
9631
9635
  }
9632
9636
  }
9633
9637
  }
9634
- function installPortAudio() {
9635
- const platform = process.platform;
9636
- logger8.info({ platform }, "installing PortAudio for platform");
9637
- notificationService.info("\u6B63\u5728\u5B89\u88C5 PortAudio...", `\u5E73\u53F0: ${platform}`);
9638
+ function execCmd(cmd) {
9638
9639
  try {
9639
- if (platform === "linux") {
9640
- execSync("sudo apt-get update && sudo apt-get install -y libportaudio-dev portaudio", { stdio: "inherit" });
9641
- } else if (platform === "darwin") {
9642
- execSync("brew install portaudio", { stdio: "inherit" });
9643
- } else if (platform === "win32") {
9644
- execSync("choco install portaudio -y", { stdio: "inherit" });
9645
- } else {
9646
- logger8.warn("unsupported platform for automatic PortAudio install");
9647
- return false;
9648
- }
9649
- return true;
9640
+ const output = execSync(cmd, { stdio: "pipe", encoding: "utf8" });
9641
+ return { success: true, output };
9650
9642
  } catch (err) {
9651
- logger8.error({ err }, "failed to install PortAudio automatically");
9643
+ return { success: false, output: err.message || "" };
9644
+ }
9645
+ }
9646
+ function isWsl2() {
9647
+ if (process.platform !== "linux") return false;
9648
+ try {
9649
+ return require2("fs").readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft");
9650
+ } catch {
9652
9651
  return false;
9653
9652
  }
9654
9653
  }
9654
+ function checkAlsa() {
9655
+ const result = execCmd("which aplay");
9656
+ if (!result.success) return false;
9657
+ const test = execCmd("aplay -l");
9658
+ return test.success && !test.output.includes("no soundcards");
9659
+ }
9660
+ async function installPortAudio() {
9661
+ const platform = process.platform;
9662
+ const wsl = isWsl2();
9663
+ logger8.info({ platform, wsl }, "installing PortAudio");
9664
+ const runInstall = async (cmd, desc) => {
9665
+ logger8.info(`Running: ${cmd}`);
9666
+ notificationService.info(desc, "\u6B63\u5728\u5B89\u88C5...", 5e3);
9667
+ try {
9668
+ execSync(cmd, { stdio: "inherit" });
9669
+ return true;
9670
+ } catch (err) {
9671
+ const msg = err.message || "";
9672
+ if (msg.includes("sudo") || msg.includes("password") || msg.includes("Password")) {
9673
+ notificationService.error(
9674
+ "\u9700\u8981 sudo \u6743\u9650",
9675
+ "\u8BF7\u624B\u52A8\u8FD0\u884C: sudo apt-get update && sudo apt-get install -y alsa-utils",
9676
+ 8e3
9677
+ );
9678
+ logger8.error({ err }, "sudo password required");
9679
+ return false;
9680
+ }
9681
+ if (msg.includes("already") || msg.includes("is already")) {
9682
+ logger8.info("already installed");
9683
+ return true;
9684
+ }
9685
+ notificationService.error(desc + " \u5931\u8D25", msg.substring(0, 100), 8e3);
9686
+ logger8.error({ err }, `install failed: ${desc}`);
9687
+ return false;
9688
+ }
9689
+ };
9690
+ if (platform === "linux" || wsl) {
9691
+ notificationService.info("\u68C0\u6D4B\u97F3\u9891\u8BBE\u5907...", "\u97F3\u9891\u540E\u7AEF", 5e3);
9692
+ if (checkAlsa()) {
9693
+ logger8.info("alsa-utils already available and working");
9694
+ notificationService.success("alsa-utils \u5C31\u7EEA", "\u97F3\u9891\u540E\u7AEF\u5DF2\u53EF\u7528", 5e3);
9695
+ return { success: true, message: "alsa" };
9696
+ }
9697
+ notificationService.info("\u5B89\u88C5 alsa-utils...", "\u97F3\u9891\u540E\u7AEF", 5e3);
9698
+ const alsaInstalled = await runInstall(
9699
+ "sudo apt-get update && sudo apt-get install -y alsa-utils",
9700
+ "\u5B89\u88C5 alsa-utils"
9701
+ );
9702
+ if (alsaInstalled) {
9703
+ if (checkAlsa()) {
9704
+ notificationService.success("alsa-utils \u5B89\u88C5\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
9705
+ return { success: true, message: "alsa" };
9706
+ } else {
9707
+ notificationService.warning("alsa-utils \u5B89\u88C5\u540E\u68C0\u6D4B\u5931\u8D25", "\u7EE7\u7EED\u5C1D\u8BD5\u5176\u4ED6\u65B9\u6848", 5e3);
9708
+ }
9709
+ }
9710
+ notificationService.info("\u5B89\u88C5 libportaudio-dev...", "\u97F3\u9891\u540E\u7AEF", 5e3);
9711
+ const portaudioInstalled = await runInstall(
9712
+ "sudo apt-get update && sudo apt-get install -y libportaudio-dev",
9713
+ "\u5B89\u88C5 libportaudio-dev"
9714
+ );
9715
+ if (portaudioInstalled) {
9716
+ notificationService.success("libportaudio-dev \u5B89\u88C5\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
9717
+ return { success: true, message: "portaudio" };
9718
+ }
9719
+ notificationService.warning("PortAudio \u5B89\u88C5\u5931\u8D25", "\u97F3\u9891\u53EF\u80FD\u65E0\u6CD5\u6B63\u5E38\u5DE5\u4F5C", 5e3);
9720
+ markNaudiodonSkipped();
9721
+ return { success: false, message: "linux install failed" };
9722
+ }
9723
+ if (platform === "darwin") {
9724
+ const installed = await runInstall("brew install portaudio", "\u5B89\u88C5 PortAudio (macOS)");
9725
+ if (installed) {
9726
+ return { success: true, message: "portaudio" };
9727
+ }
9728
+ markNaudiodonSkipped();
9729
+ return { success: false, message: "macos install failed" };
9730
+ }
9731
+ if (platform === "win32") {
9732
+ const installed = await runInstall("choco install portaudio -y", "\u5B89\u88C5 PortAudio (Windows)");
9733
+ if (installed) {
9734
+ return { success: true, message: "portaudio" };
9735
+ }
9736
+ markNaudiodonSkipped();
9737
+ return { success: false, message: "windows install failed" };
9738
+ }
9739
+ markNaudiodonSkipped();
9740
+ return { success: false, message: "unsupported platform" };
9741
+ }
9655
9742
  var __filename = fileURLToPath(import.meta.url);
9656
9743
  var __dirname2 = dirname2(__filename);
9657
9744
  var id = "ocosay";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mingxy/ocosay",
3
- "version": "1.1.8",
3
+ "version": "1.1.10",
4
4
  "description": "OpenCode TTS 播放插件 - 支持豆包模式边接收边朗读",
5
5
  "type": "module",
6
6
  "main": "dist/plugin.js",
@@ -1,117 +1,172 @@
1
1
  const { execSync, spawn } = require('child_process');
2
2
  const fs = require('fs');
3
- const os = require('os');
3
+ const readline = require('readline');
4
4
 
5
+ const platform = process.platform;
5
6
  const isWSL = fs.existsSync('/proc/version') &&
6
7
  fs.readFileSync('/proc/version', 'utf8').includes('Microsoft');
7
- const platform = process.platform;
8
8
 
9
9
  const log = {
10
- info: (msg) => console.log(`📦 ${msg}`),
11
- success: (msg) => console.log(`✅ ${msg}`),
12
- warn: (msg) => console.log(`⚠️ ${msg}`),
13
- error: (msg) => console.error(`❌ ${msg}`)
10
+ info: (msg) => console.log(`[INFO] ${msg}`),
11
+ success: (msg) => console.log(`[OK] ${msg}`),
12
+ warn: (msg) => console.log(`[WARN] ${msg}`),
13
+ error: (msg) => console.error(`[ERROR] ${msg}`)
14
14
  };
15
15
 
16
- function checkCmd(cmd) {
16
+ function exec(cmd, ignoreError = false) {
17
+ try {
18
+ const output = execSync(cmd, { stdio: 'pipe', encoding: 'utf8' });
19
+ return { success: true, output };
20
+ } catch (err) {
21
+ if (ignoreError) return { success: false, output: '' };
22
+ return { success: false, output: err.message };
23
+ }
24
+ }
25
+
26
+ function askPassword() {
27
+ return new Promise((resolve) => {
28
+ const rl = readline.createInterface({
29
+ input: process.stdin,
30
+ output: process.stdout
31
+ });
32
+ rl.question('需要 sudo 权限,请输入密码: ', (password) => {
33
+ rl.close();
34
+ resolve(password);
35
+ });
36
+ });
37
+ }
38
+
39
+ async function runWithSudo(cmd) {
40
+ // 先尝试直接运行(可能已缓存密码)
17
41
  try {
18
42
  execSync(cmd, { stdio: 'ignore' });
19
43
  return true;
20
- } catch {
44
+ } catch { /* 继续尝试密码 */ }
45
+
46
+ // 需要密码
47
+ const password = await askPassword();
48
+ if (!password) {
49
+ log.error('未输入密码,安装取消');
21
50
  return false;
22
51
  }
52
+
53
+ return new Promise((resolve) => {
54
+ const proc = spawn('bash', ['-c', `echo "${password}" | sudo -S ${cmd}`], {
55
+ stdio: 'inherit'
56
+ });
57
+ proc.on('close', (code) => resolve(code === 0));
58
+ proc.on('error', () => resolve(false));
59
+ });
23
60
  }
24
61
 
25
- async function runCmd(cmd) {
26
- return new Promise((resolve, reject) => {
62
+ async function execCmd(cmd) {
63
+ return new Promise((resolve) => {
27
64
  const shell = platform === 'win32' ? 'powershell' : 'bash';
28
65
  const args = platform === 'win32' ? ['-Command', cmd] : ['-c', cmd];
29
66
  const proc = spawn(shell, args, { stdio: 'inherit' });
30
- proc.on('close', (code) => (code === 0 ? resolve() : reject()));
31
- proc.on('error', reject);
67
+ proc.on('close', (code) => resolve(code === 0));
68
+ proc.on('error', () => resolve(false));
32
69
  });
33
70
  }
34
71
 
35
- async function installMacOS() {
36
- if (checkCmd('brew list portaudio')) {
37
- log.success('PortAudio 已安装 (macOS)');
38
- return true;
39
- }
40
- log.info('安装 PortAudio (macOS)...');
41
- try {
42
- await runCmd('brew install portaudio');
43
- return true;
44
- } catch {
45
- return false;
72
+ function checkAlsa() {
73
+ const result = exec('which aplay');
74
+ if (result.success) {
75
+ const aplayOutput = exec('aplay -l');
76
+ if (aplayOutput.success && !aplayOutput.output.includes('no soundcards')) {
77
+ return { available: true, type: 'alsa', command: result.output.trim() };
78
+ }
46
79
  }
80
+ return { available: false, type: null, command: result.success ? result.output.trim() : null };
47
81
  }
48
82
 
49
- async function installWSL() {
50
- if (checkCmd('dpkg -s libportaudio-dev')) {
51
- log.success('PortAudio 已安装 (WSL)');
52
- return true;
53
- }
54
- log.info('安装 PortAudio (WSL)...');
55
- try {
56
- await runCmd('sudo apt-get update && sudo apt-get install -y libportaudio-dev portaudio');
57
- return true;
58
- } catch {
59
- return false;
60
- }
83
+ async function installAlsaUtils() {
84
+ log.info('安装 alsa-utils...');
85
+ return runWithSudo('apt-get update && apt-get install -y alsa-utils');
61
86
  }
62
87
 
63
- async function installWindowsNative() {
64
- if (checkCmd('choco list --local-only portaudio')) {
65
- log.success('PortAudio 已安装 (Windows)');
66
- return true;
67
- }
68
- if (!checkCmd('where choco')) {
69
- log.warn('未检测到 choco,尝试直接安装...');
70
- return false;
71
- }
72
- log.info('安装 PortAudio (Windows)...');
73
- try {
74
- await runCmd('choco install portaudio -y');
75
- return true;
76
- } catch {
77
- return false;
88
+ async function installPortAudioDev() {
89
+ log.info('安装 libportaudio-dev...');
90
+ return runWithSudo('apt-get update && apt-get install -y libportaudio-dev');
91
+ }
92
+
93
+ function testAlsaPlayback() {
94
+ const result = exec('aplay -l');
95
+ if (!result.success) return { success: false, reason: 'aplay 命令不可用' };
96
+ if (result.output.includes('no soundcards') || result.output.includes('no devices')) {
97
+ return { success: false, reason: '没有音频设备' };
78
98
  }
99
+ return { success: true, devices: result.output };
79
100
  }
80
101
 
81
102
  async function main() {
82
- console.log('🔊 PortAudio 安装脚本');
83
- console.log('====================');
103
+ console.log('=== 音频环境检测与安装脚本 ===');
84
104
  console.log(`平台: ${platform}${isWSL ? ' (WSL)' : ''}`);
85
105
  console.log('');
86
- let success = false;
87
-
88
- if (isWSL) {
89
- success = await installWSL();
90
- } else if (platform === 'darwin') {
91
- success = await installMacOS();
92
- } else if (platform === 'win32') {
93
- success = await installWindowsNative();
106
+
107
+ // 步骤1: 检测 alsa
108
+ log.info('检测 alsa...');
109
+ const alsa = checkAlsa();
110
+ if (alsa.available) {
111
+ log.success(`alsa 可用: ${alsa.command}`);
112
+ const test = testAlsaPlayback();
113
+ if (test.success) {
114
+ log.success('alsa 播放设备检测通过:');
115
+ console.log(test.devices);
116
+ log.success('=== 音频环境就绪 ===');
117
+ process.exit(0);
118
+ } else {
119
+ log.warn(`alsa 测试失败: ${test.reason}`);
120
+ }
94
121
  } else {
95
- log.error('不支持的平台');
122
+ log.warn('alsa 未安装');
96
123
  }
97
124
 
98
- if (success) {
99
- log.success('PortAudio 安装完成!');
100
- process.exit(0);
101
- } else {
102
- log.error('========================================');
103
- log.error('PortAudio 安装失败,请手动安装:');
104
- log.error('========================================');
105
- if (platform === 'darwin') {
106
- log.error(' brew install portaudio');
107
- } else if (isWSL) {
108
- log.error(' sudo apt-get install -y libportaudio-dev portaudio');
109
- } else if (platform === 'win32') {
110
- log.error(' choco install portaudio');
111
- log.error(' 或下载: https://www.portaudio.com/download.html');
112
- }
125
+ // 步骤2: 安装 alsa-utils
126
+ log.info('安装 alsa-utils...');
127
+ const installed = await installAlsaUtils();
128
+ if (!installed) {
129
+ log.error('alsa-utils 安装失败');
113
130
  process.exit(1);
114
131
  }
132
+ log.success('alsa-utils 安装成功');
133
+
134
+ // 步骤3: 重新检测
135
+ const alsa2 = checkAlsa();
136
+ if (alsa2.available) {
137
+ const test = testAlsaPlayback();
138
+ if (test.success) {
139
+ log.success('alsa 播放设备检测通过:');
140
+ console.log(test.devices);
141
+ log.success('=== 音频环境就绪 ===');
142
+ process.exit(0);
143
+ } else {
144
+ log.warn(`检测到 alsa 但无播放设备: ${test.reason}`);
145
+ }
146
+ }
147
+
148
+ // 步骤4: WSL 安装 PortAudio
149
+ if (isWSL) {
150
+ log.info('安装 libportaudio-dev...');
151
+ await installPortAudioDev();
152
+ }
153
+
154
+ // 最终检测
155
+ const alsa3 = checkAlsa();
156
+ if (alsa3.available) {
157
+ const test = testAlsaPlayback();
158
+ if (test.success) {
159
+ log.success('=== 音频环境就绪 ===');
160
+ process.exit(0);
161
+ }
162
+ }
163
+
164
+ log.error('=== 无法配置音频环境 ===');
165
+ if (isWSL) {
166
+ log.error('WSL 没有音频设备');
167
+ log.error('方案: 配置 Windows 音频转发或手动安装音频驱动');
168
+ }
169
+ process.exit(1);
115
170
  }
116
171
 
117
172
  main();