@mingxy/ocosay 1.1.10 → 1.1.12

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.
@@ -19,13 +19,6 @@ export declare enum BackendType {
19
19
  HOWLER = "howler",
20
20
  AUTO = "auto"
21
21
  }
22
- export declare function isWsl(): boolean;
23
- /**
24
- * 创建音频后端
25
- * @param type 后端类型,默认 AUTO(自动选择)
26
- * @param options 后端配置选项
27
- * @returns 音频后端实例
28
- */
29
22
  export declare function createBackend(type?: BackendType, options?: BackendOptions): AudioBackend;
30
23
  export declare function supportsStreaming(type: BackendType): boolean;
31
24
  export declare function getDefaultBackendType(): BackendType;
@@ -7,12 +7,25 @@ export { AfplayBackend } from './afplay-backend';
7
7
  export { AplayBackend } from './aplay-backend';
8
8
  export { PowerShellBackend } from './powershell-backend';
9
9
  export { HowlerBackend } from './howler-backend';
10
+ import { execSync } from 'child_process';
10
11
  import { NaudiodonBackend } from './naudiodon-backend';
11
12
  import { AfplayBackend } from './afplay-backend';
12
13
  import { AplayBackend } from './aplay-backend';
13
14
  import { PowerShellBackend } from './powershell-backend';
14
15
  import { HowlerBackend } from './howler-backend';
15
16
  import { logger } from '../../utils/logger';
17
+ function execCmd(cmd) {
18
+ try {
19
+ const output = execSync(cmd, { stdio: 'pipe', encoding: 'utf8' });
20
+ return { success: true, output };
21
+ }
22
+ catch (err) {
23
+ return { success: false, output: err.message || '' };
24
+ }
25
+ }
26
+ function isCommandAvailable(cmd) {
27
+ return execCmd(`which ${cmd}`).success;
28
+ }
16
29
  /**
17
30
  * 后端类型枚举
18
31
  */
@@ -50,22 +63,6 @@ function isNaudiodonAvailable() {
50
63
  return false;
51
64
  }
52
65
  }
53
- export function isWsl() {
54
- if (process.platform !== 'linux')
55
- return false;
56
- try {
57
- return require('fs').readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft');
58
- }
59
- catch {
60
- return false;
61
- }
62
- }
63
- /**
64
- * 创建音频后端
65
- * @param type 后端类型,默认 AUTO(自动选择)
66
- * @param options 后端配置选项
67
- * @returns 音频后端实例
68
- */
69
66
  export function createBackend(type = BackendType.AUTO, options = {}) {
70
67
  const platform = process.platform;
71
68
  if (type !== BackendType.AUTO) {
@@ -90,10 +87,13 @@ export function createBackend(type = BackendType.AUTO, options = {}) {
90
87
  case 'darwin':
91
88
  return new AfplayBackend(options);
92
89
  case 'linux':
93
- if (isWsl()) {
94
- return new PowerShellBackend(options);
90
+ if (isCommandAvailable('aplay')) {
91
+ const test = execCmd('aplay -l');
92
+ if (test.success && !test.output.includes('no soundcards')) {
93
+ return new AplayBackend(options);
94
+ }
95
95
  }
96
- return new AplayBackend(options);
96
+ return new HowlerBackend(options);
97
97
  case 'win32':
98
98
  return new PowerShellBackend(options);
99
99
  default:
@@ -6,7 +6,16 @@ import { spawn } from 'child_process';
6
6
  import { tmpdir } from 'os';
7
7
  import { join } from 'path';
8
8
  import { writeFileSync, unlinkSync, existsSync } from 'fs';
9
- import { isWsl } from './index';
9
+ function isWsl() {
10
+ if (process.platform !== 'linux')
11
+ return false;
12
+ try {
13
+ return require('fs').readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft');
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ }
10
19
  // 白名单:Windows/WSL 路径格式(禁止 - 防止命令注入)
11
20
  // 允许: 字母数字 \w, Windows盘符 : \:, 反斜杠 \\, 下划线 _, 点 ., $ @ /, 以及 -
12
21
  const SAFE_PATH_REGEX = /^[\w\:\\_.\-$@\/]+$/i;
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mingxy/ocosay",
3
- "version": "1.1.10",
3
+ "version": "1.1.12",
4
4
  "description": "OpenCode TTS 播放插件 - 支持豆包模式边接收边朗读",
5
5
  "type": "module",
6
6
  "main": "dist/plugin.js",
package/dist/plugin.js CHANGED
@@ -7156,6 +7156,14 @@ import { spawn } from "child_process";
7156
7156
  import { tmpdir as tmpdir3 } from "os";
7157
7157
  import { join as join4 } from "path";
7158
7158
  import { writeFileSync as writeFileSync3, unlinkSync as unlinkSync3, existsSync as existsSync4 } from "fs";
7159
+ function isWsl() {
7160
+ if (process.platform !== "linux") return false;
7161
+ try {
7162
+ return __require("fs").readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft");
7163
+ } catch {
7164
+ return false;
7165
+ }
7166
+ }
7159
7167
  var SAFE_PATH_REGEX3 = /^[\w\:\\_.\-$@\/]+$/i;
7160
7168
  function wslPathToWindows(wslPath) {
7161
7169
  if (wslPath.startsWith("//wsl$/")) {
@@ -7459,6 +7467,18 @@ var HowlerBackend = class {
7459
7467
  };
7460
7468
 
7461
7469
  // src/core/backends/index.ts
7470
+ import { execSync } from "child_process";
7471
+ function execCmd(cmd) {
7472
+ try {
7473
+ const output = execSync(cmd, { stdio: "pipe", encoding: "utf8" });
7474
+ return { success: true, output };
7475
+ } catch (err) {
7476
+ return { success: false, output: err.message || "" };
7477
+ }
7478
+ }
7479
+ function isCommandAvailable(cmd) {
7480
+ return execCmd(`which ${cmd}`).success;
7481
+ }
7462
7482
  function isNaudiodonAvailable() {
7463
7483
  try {
7464
7484
  __require.resolve("naudiodon");
@@ -7468,14 +7488,6 @@ function isNaudiodonAvailable() {
7468
7488
  return false;
7469
7489
  }
7470
7490
  }
7471
- function isWsl() {
7472
- if (process.platform !== "linux") return false;
7473
- try {
7474
- return __require("fs").readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft");
7475
- } catch {
7476
- return false;
7477
- }
7478
- }
7479
7491
  function createBackend(type = "auto" /* AUTO */, options = {}) {
7480
7492
  const platform = process.platform;
7481
7493
  if (type !== "auto" /* AUTO */) {
@@ -7499,10 +7511,13 @@ function createBackend(type = "auto" /* AUTO */, options = {}) {
7499
7511
  case "darwin":
7500
7512
  return new AfplayBackend(options);
7501
7513
  case "linux":
7502
- if (isWsl()) {
7503
- return new PowerShellBackend(options);
7514
+ if (isCommandAvailable("aplay")) {
7515
+ const test = execCmd("aplay -l");
7516
+ if (test.success && !test.output.includes("no soundcards")) {
7517
+ return new AplayBackend(options);
7518
+ }
7504
7519
  }
7505
- return new AplayBackend(options);
7520
+ return new HowlerBackend(options);
7506
7521
  case "win32":
7507
7522
  return new PowerShellBackend(options);
7508
7523
  default:
@@ -9567,7 +9582,7 @@ import { readFileSync as readFileSync2, existsSync as existsSync6, writeFileSync
9567
9582
  import { fileURLToPath } from "url";
9568
9583
  import { dirname as dirname2, join as join7 } from "path";
9569
9584
  import { homedir as homedir3 } from "os";
9570
- import { execSync } from "child_process";
9585
+ import { execSync as execSync2 } from "child_process";
9571
9586
  import { createRequire } from "module";
9572
9587
  var logger8 = createModuleLogger("Plugin");
9573
9588
  var require2 = createRequire(import.meta.url);
@@ -9581,12 +9596,89 @@ function markNaudiodonSkipped() {
9581
9596
  try {
9582
9597
  const dir = join7(homedir3(), ".config", "opencode");
9583
9598
  if (!existsSync6(dir)) {
9584
- execSync("mkdir -p", { cwd: dir });
9599
+ execSync2("mkdir -p", { cwd: dir });
9585
9600
  }
9586
9601
  writeFileSync5(getSkipFilePath(), Date.now().toString(), "utf-8");
9587
9602
  } catch {
9588
9603
  }
9589
9604
  }
9605
+ async function verifyNaudiodonLoad() {
9606
+ try {
9607
+ require2("naudiodon");
9608
+ logger8.info("naudiodon loaded successfully");
9609
+ return true;
9610
+ } catch (err) {
9611
+ logger8.warn({ err }, "naudiodon load failed after rebuild");
9612
+ return false;
9613
+ }
9614
+ }
9615
+ async function rebuildNaudiodonDependency(dep) {
9616
+ const naudiodonPath = dirname2(require2.resolve("naudiodon"));
9617
+ try {
9618
+ notificationService.info(`\u6B63\u5728\u7F16\u8BD1 ${dep}...`, "Ocosay \u4F9D\u8D56", 4e3);
9619
+ execSync2(`npm rebuild ${dep}`, {
9620
+ cwd: naudiodonPath,
9621
+ stdio: "inherit"
9622
+ });
9623
+ logger8.info(`${dep} rebuilt successfully`);
9624
+ return true;
9625
+ } catch (err) {
9626
+ logger8.warn({ err }, `${dep} rebuild failed`);
9627
+ return false;
9628
+ }
9629
+ }
9630
+ async function fixNaudiodonDependencies(maxRetries = 5) {
9631
+ const naudiodonPath = dirname2(require2.resolve("naudiodon"));
9632
+ const criticalDeps = ["segfault-handler", "bindings", "node-pre-gyp"];
9633
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
9634
+ if (await verifyNaudiodonLoad()) {
9635
+ return true;
9636
+ }
9637
+ logger8.info({ attempt }, "naudiodon not loadable, trying dependency rebuild");
9638
+ notificationService.info(`\u6B63\u5728\u68C0\u67E5\u4F9D\u8D56 (${attempt + 1}/${maxRetries})...`, "Ocosay", 3e3);
9639
+ let anySuccess = false;
9640
+ for (const dep of criticalDeps) {
9641
+ try {
9642
+ require2.resolve(dep, { paths: [naudiodonPath] });
9643
+ if (await rebuildNaudiodonDependency(dep)) {
9644
+ anySuccess = true;
9645
+ }
9646
+ } catch {
9647
+ try {
9648
+ notificationService.info(`\u6B63\u5728\u5B89\u88C5 ${dep}...`, "Ocosay", 4e3);
9649
+ execSync2(`npm install ${dep}`, {
9650
+ cwd: naudiodonPath,
9651
+ stdio: "inherit"
9652
+ });
9653
+ logger8.info(`${dep} installed successfully`);
9654
+ anySuccess = true;
9655
+ } catch (installErr) {
9656
+ logger8.warn({ err: installErr }, `${dep} install failed`);
9657
+ }
9658
+ }
9659
+ }
9660
+ if (!anySuccess || !await verifyNaudiodonLoad()) {
9661
+ try {
9662
+ notificationService.info("\u6B63\u5728\u91CD\u65B0\u7F16\u8BD1 naudiodon...", "Ocosay", 4e3);
9663
+ execSync2("npm rebuild naudiodon", {
9664
+ cwd: naudiodonPath,
9665
+ stdio: "inherit"
9666
+ });
9667
+ logger8.info("naudiodon rebuilt");
9668
+ anySuccess = true;
9669
+ } catch (err) {
9670
+ logger8.warn({ err }, "naudiodon rebuild failed");
9671
+ }
9672
+ }
9673
+ if (await verifyNaudiodonLoad()) {
9674
+ return true;
9675
+ }
9676
+ if (anySuccess) {
9677
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
9678
+ }
9679
+ }
9680
+ return await verifyNaudiodonLoad();
9681
+ }
9590
9682
  async function ensureNaudiodonCompiled() {
9591
9683
  if (shouldSkipNaudiodon()) {
9592
9684
  logger8.info("naudiodon skipped previously");
@@ -9603,12 +9695,23 @@ async function ensureNaudiodonCompiled() {
9603
9695
  try {
9604
9696
  const naudiodonPath = dirname2(require2.resolve("naudiodon"));
9605
9697
  logger8.info({ naudiodonPath }, "found naudiodon, rebuilding");
9606
- execSync("npm rebuild naudiodon", {
9698
+ execSync2("npm rebuild naudiodon", {
9607
9699
  cwd: naudiodonPath,
9608
9700
  stdio: "inherit"
9609
9701
  });
9610
- logger8.info("naudiodon compiled successfully");
9611
- notificationService.success("naudiodon \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
9702
+ logger8.info("naudiodon compiled, verifying...");
9703
+ const loadSuccess = await verifyNaudiodonLoad();
9704
+ if (loadSuccess) {
9705
+ notificationService.success("naudiodon \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
9706
+ } else {
9707
+ notificationService.warning("naudiodon \u52A0\u8F7D\u5931\u8D25", "\u6B63\u5728\u68C0\u67E5\u4F9D\u8D56...", 5e3);
9708
+ const fixed = await fixNaudiodonDependencies();
9709
+ if (fixed) {
9710
+ notificationService.success("naudiodon \u4F9D\u8D56\u4FEE\u590D\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
9711
+ } else {
9712
+ throw new Error("naudiodon dependencies could not be fixed");
9713
+ }
9714
+ }
9612
9715
  } catch (err) {
9613
9716
  logger8.warn({ err }, "naudiodon rebuild failed, checking for PortAudio");
9614
9717
  notificationService.warning("naudiodon \u7F16\u8BD1\u5931\u8D25", "\u6B63\u5728\u5C1D\u8BD5\u5B89\u88C5 PortAudio...", 5e3);
@@ -9617,12 +9720,23 @@ async function ensureNaudiodonCompiled() {
9617
9720
  try {
9618
9721
  const naudiodonPath = dirname2(require2.resolve("naudiodon"));
9619
9722
  notificationService.info("\u6B63\u5728\u91CD\u65B0\u7F16\u8BD1 naudiodon...", "Ocosay", 5e3);
9620
- execSync("npm rebuild naudiodon", {
9723
+ execSync2("npm rebuild naudiodon", {
9621
9724
  cwd: naudiodonPath,
9622
9725
  stdio: "inherit"
9623
9726
  });
9624
9727
  logger8.info("naudiodon compiled successfully after PortAudio install");
9625
- notificationService.success("naudiodon \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
9728
+ const loadSuccess = await verifyNaudiodonLoad();
9729
+ if (loadSuccess) {
9730
+ notificationService.success("naudiodon \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
9731
+ } else {
9732
+ notificationService.warning("naudiodon \u52A0\u8F7D\u5931\u8D25", "\u6B63\u5728\u68C0\u67E5\u4F9D\u8D56...", 5e3);
9733
+ const fixed = await fixNaudiodonDependencies();
9734
+ if (fixed) {
9735
+ notificationService.success("naudiodon \u4F9D\u8D56\u4FEE\u590D\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
9736
+ } else {
9737
+ throw new Error("naudiodon dependencies could not be fixed");
9738
+ }
9739
+ }
9626
9740
  } catch (retryErr) {
9627
9741
  logger8.error({ err: retryErr }, "naudiodon compile failed even after PortAudio install");
9628
9742
  notificationService.error("naudiodon \u7F16\u8BD1\u5931\u8D25", "\u81EA\u52A8\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u5C1D\u8BD5\u624B\u52A8\u5B89\u88C5", 8e3);
@@ -9635,9 +9749,9 @@ async function ensureNaudiodonCompiled() {
9635
9749
  }
9636
9750
  }
9637
9751
  }
9638
- function execCmd(cmd) {
9752
+ function execCmd2(cmd) {
9639
9753
  try {
9640
- const output = execSync(cmd, { stdio: "pipe", encoding: "utf8" });
9754
+ const output = execSync2(cmd, { stdio: "pipe", encoding: "utf8" });
9641
9755
  return { success: true, output };
9642
9756
  } catch (err) {
9643
9757
  return { success: false, output: err.message || "" };
@@ -9652,9 +9766,9 @@ function isWsl2() {
9652
9766
  }
9653
9767
  }
9654
9768
  function checkAlsa() {
9655
- const result = execCmd("which aplay");
9769
+ const result = execCmd2("which aplay");
9656
9770
  if (!result.success) return false;
9657
- const test = execCmd("aplay -l");
9771
+ const test = execCmd2("aplay -l");
9658
9772
  return test.success && !test.output.includes("no soundcards");
9659
9773
  }
9660
9774
  async function installPortAudio() {
@@ -9665,7 +9779,7 @@ async function installPortAudio() {
9665
9779
  logger8.info(`Running: ${cmd}`);
9666
9780
  notificationService.info(desc, "\u6B63\u5728\u5B89\u88C5...", 5e3);
9667
9781
  try {
9668
- execSync(cmd, { stdio: "inherit" });
9782
+ execSync2(cmd, { stdio: "inherit" });
9669
9783
  return true;
9670
9784
  } catch (err) {
9671
9785
  const msg = err.message || "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mingxy/ocosay",
3
- "version": "1.1.10",
3
+ "version": "1.1.12",
4
4
  "description": "OpenCode TTS 播放插件 - 支持豆包模式边接收边朗读",
5
5
  "type": "module",
6
6
  "main": "dist/plugin.js",