@mingxy/ocosay 1.1.11 → 1.1.13
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/core/backends/index.d.ts +4 -7
- package/dist/core/backends/index.js +46 -15
- package/dist/core/backends/playsound-backend.d.ts +39 -0
- package/dist/core/backends/playsound-backend.js +189 -0
- package/dist/core/backends/powershell-backend.js +10 -1
- package/dist/core/backends/speaker-backend.d.ts +34 -0
- package/dist/core/backends/speaker-backend.js +163 -0
- package/dist/package.json +5 -3
- package/dist/plugin.js +431 -62
- package/package.json +5 -3
package/dist/plugin.js
CHANGED
|
@@ -2013,7 +2013,7 @@ var require_thread_stream = __commonJS({
|
|
|
2013
2013
|
var { version } = require_package();
|
|
2014
2014
|
var { EventEmitter: EventEmitter7 } = __require("events");
|
|
2015
2015
|
var { Worker } = __require("worker_threads");
|
|
2016
|
-
var { join:
|
|
2016
|
+
var { join: join9 } = __require("path");
|
|
2017
2017
|
var { pathToFileURL } = __require("url");
|
|
2018
2018
|
var { wait } = require_wait();
|
|
2019
2019
|
var {
|
|
@@ -2049,7 +2049,7 @@ var require_thread_stream = __commonJS({
|
|
|
2049
2049
|
function createWorker(stream2, opts) {
|
|
2050
2050
|
const { filename, workerData } = opts;
|
|
2051
2051
|
const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
|
|
2052
|
-
const toExecute = bundlerOverrides["thread-stream-worker"] ||
|
|
2052
|
+
const toExecute = bundlerOverrides["thread-stream-worker"] || join9(__dirname, "lib", "worker.js");
|
|
2053
2053
|
const worker = new Worker(toExecute, {
|
|
2054
2054
|
...opts.workerOpts,
|
|
2055
2055
|
trackUnmanagedFds: false,
|
|
@@ -2438,9 +2438,9 @@ var require_transport = __commonJS({
|
|
|
2438
2438
|
"node_modules/pino/lib/transport.js"(exports, module) {
|
|
2439
2439
|
"use strict";
|
|
2440
2440
|
var { createRequire: createRequire2 } = __require("module");
|
|
2441
|
-
var { existsSync:
|
|
2441
|
+
var { existsSync: existsSync8 } = __require("node:fs");
|
|
2442
2442
|
var getCallers = require_caller();
|
|
2443
|
-
var { join:
|
|
2443
|
+
var { join: join9, isAbsolute, sep } = __require("node:path");
|
|
2444
2444
|
var { fileURLToPath: fileURLToPath2 } = __require("node:url");
|
|
2445
2445
|
var sleep = require_atomic_sleep();
|
|
2446
2446
|
var onExit = require_on_exit_leak_free();
|
|
@@ -2512,7 +2512,7 @@ var require_transport = __commonJS({
|
|
|
2512
2512
|
return false;
|
|
2513
2513
|
}
|
|
2514
2514
|
}
|
|
2515
|
-
return isAbsolute(path2) && !
|
|
2515
|
+
return isAbsolute(path2) && !existsSync8(path2);
|
|
2516
2516
|
}
|
|
2517
2517
|
function stripQuotes(value) {
|
|
2518
2518
|
const first = value[0];
|
|
@@ -2593,7 +2593,7 @@ var require_transport = __commonJS({
|
|
|
2593
2593
|
throw new Error("only one of target or targets can be specified");
|
|
2594
2594
|
}
|
|
2595
2595
|
if (targets) {
|
|
2596
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
2596
|
+
target = bundlerOverrides["pino-worker"] || join9(__dirname, "worker.js");
|
|
2597
2597
|
options.targets = targets.filter((dest) => dest.target).map((dest) => {
|
|
2598
2598
|
return {
|
|
2599
2599
|
...dest,
|
|
@@ -2611,7 +2611,7 @@ var require_transport = __commonJS({
|
|
|
2611
2611
|
});
|
|
2612
2612
|
});
|
|
2613
2613
|
} else if (pipeline) {
|
|
2614
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
2614
|
+
target = bundlerOverrides["pino-worker"] || join9(__dirname, "worker.js");
|
|
2615
2615
|
options.pipelines = [pipeline.map((dest) => {
|
|
2616
2616
|
return {
|
|
2617
2617
|
...dest,
|
|
@@ -2634,7 +2634,7 @@ var require_transport = __commonJS({
|
|
|
2634
2634
|
return origin;
|
|
2635
2635
|
}
|
|
2636
2636
|
if (origin === "pino/file") {
|
|
2637
|
-
return
|
|
2637
|
+
return join9(__dirname, "..", "file.js");
|
|
2638
2638
|
}
|
|
2639
2639
|
let fixTarget2;
|
|
2640
2640
|
for (const filePath of callers) {
|
|
@@ -3614,7 +3614,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
3614
3614
|
return circularValue;
|
|
3615
3615
|
}
|
|
3616
3616
|
let res = "";
|
|
3617
|
-
let
|
|
3617
|
+
let join9 = ",";
|
|
3618
3618
|
const originalIndentation = indentation;
|
|
3619
3619
|
if (Array.isArray(value)) {
|
|
3620
3620
|
if (value.length === 0) {
|
|
@@ -3628,7 +3628,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
3628
3628
|
indentation += spacer;
|
|
3629
3629
|
res += `
|
|
3630
3630
|
${indentation}`;
|
|
3631
|
-
|
|
3631
|
+
join9 = `,
|
|
3632
3632
|
${indentation}`;
|
|
3633
3633
|
}
|
|
3634
3634
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -3636,13 +3636,13 @@ ${indentation}`;
|
|
|
3636
3636
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
3637
3637
|
const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
3638
3638
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
3639
|
-
res +=
|
|
3639
|
+
res += join9;
|
|
3640
3640
|
}
|
|
3641
3641
|
const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
3642
3642
|
res += tmp !== void 0 ? tmp : "null";
|
|
3643
3643
|
if (value.length - 1 > maximumBreadth) {
|
|
3644
3644
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
3645
|
-
res += `${
|
|
3645
|
+
res += `${join9}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
3646
3646
|
}
|
|
3647
3647
|
if (spacer !== "") {
|
|
3648
3648
|
res += `
|
|
@@ -3663,7 +3663,7 @@ ${originalIndentation}`;
|
|
|
3663
3663
|
let separator = "";
|
|
3664
3664
|
if (spacer !== "") {
|
|
3665
3665
|
indentation += spacer;
|
|
3666
|
-
|
|
3666
|
+
join9 = `,
|
|
3667
3667
|
${indentation}`;
|
|
3668
3668
|
whitespace = " ";
|
|
3669
3669
|
}
|
|
@@ -3677,13 +3677,13 @@ ${indentation}`;
|
|
|
3677
3677
|
const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
|
|
3678
3678
|
if (tmp !== void 0) {
|
|
3679
3679
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
3680
|
-
separator =
|
|
3680
|
+
separator = join9;
|
|
3681
3681
|
}
|
|
3682
3682
|
}
|
|
3683
3683
|
if (keyLength > maximumBreadth) {
|
|
3684
3684
|
const removedKeys = keyLength - maximumBreadth;
|
|
3685
3685
|
res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
|
|
3686
|
-
separator =
|
|
3686
|
+
separator = join9;
|
|
3687
3687
|
}
|
|
3688
3688
|
if (spacer !== "" && separator.length > 1) {
|
|
3689
3689
|
res = `
|
|
@@ -3724,7 +3724,7 @@ ${originalIndentation}`;
|
|
|
3724
3724
|
}
|
|
3725
3725
|
const originalIndentation = indentation;
|
|
3726
3726
|
let res = "";
|
|
3727
|
-
let
|
|
3727
|
+
let join9 = ",";
|
|
3728
3728
|
if (Array.isArray(value)) {
|
|
3729
3729
|
if (value.length === 0) {
|
|
3730
3730
|
return "[]";
|
|
@@ -3737,7 +3737,7 @@ ${originalIndentation}`;
|
|
|
3737
3737
|
indentation += spacer;
|
|
3738
3738
|
res += `
|
|
3739
3739
|
${indentation}`;
|
|
3740
|
-
|
|
3740
|
+
join9 = `,
|
|
3741
3741
|
${indentation}`;
|
|
3742
3742
|
}
|
|
3743
3743
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -3745,13 +3745,13 @@ ${indentation}`;
|
|
|
3745
3745
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
3746
3746
|
const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
3747
3747
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
3748
|
-
res +=
|
|
3748
|
+
res += join9;
|
|
3749
3749
|
}
|
|
3750
3750
|
const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
3751
3751
|
res += tmp !== void 0 ? tmp : "null";
|
|
3752
3752
|
if (value.length - 1 > maximumBreadth) {
|
|
3753
3753
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
3754
|
-
res += `${
|
|
3754
|
+
res += `${join9}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
3755
3755
|
}
|
|
3756
3756
|
if (spacer !== "") {
|
|
3757
3757
|
res += `
|
|
@@ -3764,7 +3764,7 @@ ${originalIndentation}`;
|
|
|
3764
3764
|
let whitespace = "";
|
|
3765
3765
|
if (spacer !== "") {
|
|
3766
3766
|
indentation += spacer;
|
|
3767
|
-
|
|
3767
|
+
join9 = `,
|
|
3768
3768
|
${indentation}`;
|
|
3769
3769
|
whitespace = " ";
|
|
3770
3770
|
}
|
|
@@ -3773,7 +3773,7 @@ ${indentation}`;
|
|
|
3773
3773
|
const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
|
|
3774
3774
|
if (tmp !== void 0) {
|
|
3775
3775
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
3776
|
-
separator =
|
|
3776
|
+
separator = join9;
|
|
3777
3777
|
}
|
|
3778
3778
|
}
|
|
3779
3779
|
if (spacer !== "" && separator.length > 1) {
|
|
@@ -3831,20 +3831,20 @@ ${originalIndentation}`;
|
|
|
3831
3831
|
indentation += spacer;
|
|
3832
3832
|
let res2 = `
|
|
3833
3833
|
${indentation}`;
|
|
3834
|
-
const
|
|
3834
|
+
const join10 = `,
|
|
3835
3835
|
${indentation}`;
|
|
3836
3836
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
3837
3837
|
let i = 0;
|
|
3838
3838
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
3839
3839
|
const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
3840
3840
|
res2 += tmp2 !== void 0 ? tmp2 : "null";
|
|
3841
|
-
res2 +=
|
|
3841
|
+
res2 += join10;
|
|
3842
3842
|
}
|
|
3843
3843
|
const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
3844
3844
|
res2 += tmp !== void 0 ? tmp : "null";
|
|
3845
3845
|
if (value.length - 1 > maximumBreadth) {
|
|
3846
3846
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
3847
|
-
res2 += `${
|
|
3847
|
+
res2 += `${join10}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
3848
3848
|
}
|
|
3849
3849
|
res2 += `
|
|
3850
3850
|
${originalIndentation}`;
|
|
@@ -3860,16 +3860,16 @@ ${originalIndentation}`;
|
|
|
3860
3860
|
return '"[Object]"';
|
|
3861
3861
|
}
|
|
3862
3862
|
indentation += spacer;
|
|
3863
|
-
const
|
|
3863
|
+
const join9 = `,
|
|
3864
3864
|
${indentation}`;
|
|
3865
3865
|
let res = "";
|
|
3866
3866
|
let separator = "";
|
|
3867
3867
|
let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
|
|
3868
3868
|
if (isTypedArrayWithEntries(value)) {
|
|
3869
|
-
res += stringifyTypedArray(value,
|
|
3869
|
+
res += stringifyTypedArray(value, join9, maximumBreadth);
|
|
3870
3870
|
keys = keys.slice(value.length);
|
|
3871
3871
|
maximumPropertiesToStringify -= value.length;
|
|
3872
|
-
separator =
|
|
3872
|
+
separator = join9;
|
|
3873
3873
|
}
|
|
3874
3874
|
if (deterministic) {
|
|
3875
3875
|
keys = sort(keys, comparator);
|
|
@@ -3880,13 +3880,13 @@ ${indentation}`;
|
|
|
3880
3880
|
const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
|
|
3881
3881
|
if (tmp !== void 0) {
|
|
3882
3882
|
res += `${separator}${strEscape(key2)}: ${tmp}`;
|
|
3883
|
-
separator =
|
|
3883
|
+
separator = join9;
|
|
3884
3884
|
}
|
|
3885
3885
|
}
|
|
3886
3886
|
if (keyLength > maximumBreadth) {
|
|
3887
3887
|
const removedKeys = keyLength - maximumBreadth;
|
|
3888
3888
|
res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
|
|
3889
|
-
separator =
|
|
3889
|
+
separator = join9;
|
|
3890
3890
|
}
|
|
3891
3891
|
if (separator !== "") {
|
|
3892
3892
|
res = `
|
|
@@ -6737,8 +6737,8 @@ var BaseTTSProvider = class {
|
|
|
6737
6737
|
import { EventEmitter } from "events";
|
|
6738
6738
|
import fs from "fs";
|
|
6739
6739
|
import { createWriteStream } from "fs";
|
|
6740
|
-
import { tmpdir as
|
|
6741
|
-
import { join as
|
|
6740
|
+
import { tmpdir as tmpdir5 } from "os";
|
|
6741
|
+
import { join as join6 } from "path";
|
|
6742
6742
|
|
|
6743
6743
|
// src/utils/logger.ts
|
|
6744
6744
|
var import_pino = __toESM(require_pino(), 1);
|
|
@@ -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$/")) {
|
|
@@ -7458,7 +7466,312 @@ var HowlerBackend = class {
|
|
|
7458
7466
|
}
|
|
7459
7467
|
};
|
|
7460
7468
|
|
|
7469
|
+
// src/core/backends/playsound-backend.ts
|
|
7470
|
+
import { execFile as execFile3 } from "child_process";
|
|
7471
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
7472
|
+
import { join as join5 } from "path";
|
|
7473
|
+
import { writeFileSync as writeFileSync4, unlinkSync as unlinkSync4, existsSync as existsSync5 } from "fs";
|
|
7474
|
+
var SAFE_PATH_REGEX4 = /^[\w\/\.]+$/;
|
|
7475
|
+
var PlaySoundBackend = class {
|
|
7476
|
+
name = "play-sound";
|
|
7477
|
+
supportsStreaming = false;
|
|
7478
|
+
player;
|
|
7479
|
+
tempFile;
|
|
7480
|
+
events;
|
|
7481
|
+
_started = false;
|
|
7482
|
+
_paused = false;
|
|
7483
|
+
_stopped = false;
|
|
7484
|
+
// P0-4: 缓冲所有chunk,等end()时一次性写入文件
|
|
7485
|
+
chunks = [];
|
|
7486
|
+
hasEnded = false;
|
|
7487
|
+
constructor(options = {}) {
|
|
7488
|
+
this.events = options.events;
|
|
7489
|
+
}
|
|
7490
|
+
start(filePath) {
|
|
7491
|
+
if (this._started) return;
|
|
7492
|
+
if (!SAFE_PATH_REGEX4.test(filePath)) {
|
|
7493
|
+
throw new Error(`Invalid file path: ${filePath}`);
|
|
7494
|
+
}
|
|
7495
|
+
this.tempFile = filePath;
|
|
7496
|
+
this._started = true;
|
|
7497
|
+
this._stopped = false;
|
|
7498
|
+
this.events?.onStart?.();
|
|
7499
|
+
this.playWithPlaySound(filePath);
|
|
7500
|
+
}
|
|
7501
|
+
async playWithPlaySound(filePath) {
|
|
7502
|
+
try {
|
|
7503
|
+
const play = (await import("play-sound")).default;
|
|
7504
|
+
const opts = {
|
|
7505
|
+
players: ["ffplay", "aplay", "mpg123", "afplay"]
|
|
7506
|
+
// 优先级
|
|
7507
|
+
};
|
|
7508
|
+
if (process.platform === "linux") {
|
|
7509
|
+
this.player = execFile3("ffplay", [
|
|
7510
|
+
"-nodisp",
|
|
7511
|
+
// 不显示窗口
|
|
7512
|
+
"-autoexit",
|
|
7513
|
+
// 播放完自动退出
|
|
7514
|
+
"-loglevel",
|
|
7515
|
+
"error",
|
|
7516
|
+
// 减少日志输出
|
|
7517
|
+
filePath
|
|
7518
|
+
], (error) => {
|
|
7519
|
+
if (this._stopped) return;
|
|
7520
|
+
if (error) {
|
|
7521
|
+
this.handleError(error);
|
|
7522
|
+
return;
|
|
7523
|
+
}
|
|
7524
|
+
this._started = false;
|
|
7525
|
+
this.events?.onEnd?.();
|
|
7526
|
+
});
|
|
7527
|
+
} else {
|
|
7528
|
+
const audio = play;
|
|
7529
|
+
const p = audio.play(filePath, (err) => {
|
|
7530
|
+
if (this._stopped) return;
|
|
7531
|
+
if (err) {
|
|
7532
|
+
this.handleError(err);
|
|
7533
|
+
return;
|
|
7534
|
+
}
|
|
7535
|
+
this._started = false;
|
|
7536
|
+
this.events?.onEnd?.();
|
|
7537
|
+
});
|
|
7538
|
+
if (p && p.kill) {
|
|
7539
|
+
this.player = p;
|
|
7540
|
+
}
|
|
7541
|
+
}
|
|
7542
|
+
if (this.player) {
|
|
7543
|
+
this.player.on("error", (error) => {
|
|
7544
|
+
this.handleError(error);
|
|
7545
|
+
});
|
|
7546
|
+
}
|
|
7547
|
+
} catch (err) {
|
|
7548
|
+
this.handleError(err instanceof Error ? err : new Error(String(err)));
|
|
7549
|
+
}
|
|
7550
|
+
}
|
|
7551
|
+
write(chunk) {
|
|
7552
|
+
if (this._stopped) return;
|
|
7553
|
+
this.chunks.push(chunk);
|
|
7554
|
+
}
|
|
7555
|
+
end() {
|
|
7556
|
+
if (this._stopped || this.hasEnded) return;
|
|
7557
|
+
this.hasEnded = true;
|
|
7558
|
+
if (this.chunks.length === 0) return;
|
|
7559
|
+
this.tempFile = join5(tmpdir4(), `ocosay-${Date.now()}.wav`);
|
|
7560
|
+
writeFileSync4(this.tempFile, Buffer.concat(this.chunks));
|
|
7561
|
+
this.chunks = [];
|
|
7562
|
+
this.start(this.tempFile);
|
|
7563
|
+
}
|
|
7564
|
+
pause() {
|
|
7565
|
+
if (!this._started || this._paused || this._stopped) return;
|
|
7566
|
+
if (this.player) {
|
|
7567
|
+
try {
|
|
7568
|
+
this.player.kill("SIGSTOP");
|
|
7569
|
+
this._paused = true;
|
|
7570
|
+
this.events?.onPause?.();
|
|
7571
|
+
} catch (e) {
|
|
7572
|
+
}
|
|
7573
|
+
}
|
|
7574
|
+
}
|
|
7575
|
+
resume() {
|
|
7576
|
+
if (!this._paused || this._stopped) return;
|
|
7577
|
+
if (this.player) {
|
|
7578
|
+
try {
|
|
7579
|
+
this.player.kill("SIGCONT");
|
|
7580
|
+
this._paused = false;
|
|
7581
|
+
this.events?.onResume?.();
|
|
7582
|
+
} catch (e) {
|
|
7583
|
+
}
|
|
7584
|
+
}
|
|
7585
|
+
}
|
|
7586
|
+
stop() {
|
|
7587
|
+
this._stopped = true;
|
|
7588
|
+
this._started = false;
|
|
7589
|
+
this._paused = false;
|
|
7590
|
+
if (this.player) {
|
|
7591
|
+
try {
|
|
7592
|
+
this.player.kill("SIGTERM");
|
|
7593
|
+
} catch (e) {
|
|
7594
|
+
}
|
|
7595
|
+
this.player = void 0;
|
|
7596
|
+
}
|
|
7597
|
+
this.cleanup();
|
|
7598
|
+
this.chunks = [];
|
|
7599
|
+
this.hasEnded = false;
|
|
7600
|
+
this.events?.onStop?.();
|
|
7601
|
+
}
|
|
7602
|
+
setVolume(_volume) {
|
|
7603
|
+
}
|
|
7604
|
+
destroy() {
|
|
7605
|
+
this.stop();
|
|
7606
|
+
}
|
|
7607
|
+
cleanup() {
|
|
7608
|
+
if (this.tempFile && this.tempFile.startsWith(tmpdir4())) {
|
|
7609
|
+
try {
|
|
7610
|
+
if (existsSync5(this.tempFile)) {
|
|
7611
|
+
unlinkSync4(this.tempFile);
|
|
7612
|
+
}
|
|
7613
|
+
} catch (e) {
|
|
7614
|
+
}
|
|
7615
|
+
this.tempFile = void 0;
|
|
7616
|
+
}
|
|
7617
|
+
}
|
|
7618
|
+
handleError(error) {
|
|
7619
|
+
this.events?.onError?.(error);
|
|
7620
|
+
}
|
|
7621
|
+
};
|
|
7622
|
+
|
|
7623
|
+
// src/core/backends/speaker-backend.ts
|
|
7624
|
+
import Speaker from "speaker";
|
|
7625
|
+
var SpeakerBackend = class {
|
|
7626
|
+
name = "speaker";
|
|
7627
|
+
supportsStreaming = true;
|
|
7628
|
+
speaker;
|
|
7629
|
+
events;
|
|
7630
|
+
_started = false;
|
|
7631
|
+
_paused = false;
|
|
7632
|
+
_stopped = false;
|
|
7633
|
+
_format = {
|
|
7634
|
+
channels: 1,
|
|
7635
|
+
sampleRate: 16e3,
|
|
7636
|
+
bitDepth: 16,
|
|
7637
|
+
signed: true,
|
|
7638
|
+
float: false
|
|
7639
|
+
};
|
|
7640
|
+
constructor(options = {}) {
|
|
7641
|
+
this.events = options.events;
|
|
7642
|
+
if (options.sampleRate) {
|
|
7643
|
+
this._format.sampleRate = options.sampleRate;
|
|
7644
|
+
}
|
|
7645
|
+
if (options.channels) {
|
|
7646
|
+
this._format.channels = options.channels;
|
|
7647
|
+
}
|
|
7648
|
+
if (options.format === "wav") {
|
|
7649
|
+
this._format.bitDepth = 16;
|
|
7650
|
+
}
|
|
7651
|
+
}
|
|
7652
|
+
start(_filePath) {
|
|
7653
|
+
if (this._started) return;
|
|
7654
|
+
this._started = true;
|
|
7655
|
+
this._stopped = false;
|
|
7656
|
+
this._paused = false;
|
|
7657
|
+
this.events?.onStart?.();
|
|
7658
|
+
}
|
|
7659
|
+
write(chunk) {
|
|
7660
|
+
if (this._stopped || this._paused) return;
|
|
7661
|
+
if (!this._started) {
|
|
7662
|
+
this.start("");
|
|
7663
|
+
}
|
|
7664
|
+
try {
|
|
7665
|
+
if (!this.speaker) {
|
|
7666
|
+
this.createSpeaker();
|
|
7667
|
+
}
|
|
7668
|
+
if (this.speaker) {
|
|
7669
|
+
if (this.isWavHeader(chunk)) {
|
|
7670
|
+
const audioData = this.stripWavHeader(chunk);
|
|
7671
|
+
if (audioData.length > 0) {
|
|
7672
|
+
this.speaker.write(audioData);
|
|
7673
|
+
}
|
|
7674
|
+
} else {
|
|
7675
|
+
this.speaker.write(chunk);
|
|
7676
|
+
}
|
|
7677
|
+
}
|
|
7678
|
+
} catch (err) {
|
|
7679
|
+
this.handleError(err instanceof Error ? err : new Error(String(err)));
|
|
7680
|
+
}
|
|
7681
|
+
}
|
|
7682
|
+
isWavHeader(chunk) {
|
|
7683
|
+
if (chunk.length >= 44) {
|
|
7684
|
+
const riff = chunk.toString("ascii", 0, 4);
|
|
7685
|
+
const wave = chunk.toString("ascii", 8, 12);
|
|
7686
|
+
return riff === "RIFF" && wave === "WAVE";
|
|
7687
|
+
}
|
|
7688
|
+
return false;
|
|
7689
|
+
}
|
|
7690
|
+
stripWavHeader(chunk) {
|
|
7691
|
+
return chunk.slice(44);
|
|
7692
|
+
}
|
|
7693
|
+
createSpeaker() {
|
|
7694
|
+
try {
|
|
7695
|
+
const format = {
|
|
7696
|
+
channels: this._format.channels,
|
|
7697
|
+
sampleRate: this._format.sampleRate,
|
|
7698
|
+
bitDepth: this._format.bitDepth,
|
|
7699
|
+
signed: this._format.signed,
|
|
7700
|
+
float: this._format.float
|
|
7701
|
+
};
|
|
7702
|
+
this.speaker = new Speaker(format);
|
|
7703
|
+
this.speaker.on("close", () => {
|
|
7704
|
+
if (!this._stopped) {
|
|
7705
|
+
this._started = false;
|
|
7706
|
+
this.events?.onEnd?.();
|
|
7707
|
+
}
|
|
7708
|
+
});
|
|
7709
|
+
this.speaker.on("error", (err) => {
|
|
7710
|
+
this.handleError(err);
|
|
7711
|
+
});
|
|
7712
|
+
} catch (err) {
|
|
7713
|
+
this.handleError(err instanceof Error ? err : new Error(String(err)));
|
|
7714
|
+
}
|
|
7715
|
+
}
|
|
7716
|
+
end() {
|
|
7717
|
+
if (this._stopped) return;
|
|
7718
|
+
if (this.speaker) {
|
|
7719
|
+
try {
|
|
7720
|
+
this.speaker.close();
|
|
7721
|
+
} catch (e) {
|
|
7722
|
+
}
|
|
7723
|
+
this.speaker = void 0;
|
|
7724
|
+
}
|
|
7725
|
+
this._started = false;
|
|
7726
|
+
this._stopped = true;
|
|
7727
|
+
this.events?.onEnd?.();
|
|
7728
|
+
}
|
|
7729
|
+
pause() {
|
|
7730
|
+
if (this._started && !this._paused) {
|
|
7731
|
+
this._paused = true;
|
|
7732
|
+
this.events?.onPause?.();
|
|
7733
|
+
}
|
|
7734
|
+
}
|
|
7735
|
+
resume() {
|
|
7736
|
+
if (this._paused) {
|
|
7737
|
+
this._paused = false;
|
|
7738
|
+
this.events?.onResume?.();
|
|
7739
|
+
}
|
|
7740
|
+
}
|
|
7741
|
+
stop() {
|
|
7742
|
+
this._stopped = true;
|
|
7743
|
+
this._started = false;
|
|
7744
|
+
this._paused = false;
|
|
7745
|
+
if (this.speaker) {
|
|
7746
|
+
try {
|
|
7747
|
+
this.speaker.close();
|
|
7748
|
+
} catch (e) {
|
|
7749
|
+
}
|
|
7750
|
+
this.speaker = void 0;
|
|
7751
|
+
}
|
|
7752
|
+
this.events?.onStop?.();
|
|
7753
|
+
}
|
|
7754
|
+
destroy() {
|
|
7755
|
+
this.stop();
|
|
7756
|
+
}
|
|
7757
|
+
handleError(error) {
|
|
7758
|
+
this.events?.onError?.(error);
|
|
7759
|
+
}
|
|
7760
|
+
};
|
|
7761
|
+
|
|
7461
7762
|
// src/core/backends/index.ts
|
|
7763
|
+
import { execSync } from "child_process";
|
|
7764
|
+
function execCmd(cmd) {
|
|
7765
|
+
try {
|
|
7766
|
+
const output = execSync(cmd, { stdio: "pipe", encoding: "utf8" });
|
|
7767
|
+
return { success: true, output };
|
|
7768
|
+
} catch (err) {
|
|
7769
|
+
return { success: false, output: err.message || "" };
|
|
7770
|
+
}
|
|
7771
|
+
}
|
|
7772
|
+
function isCommandAvailable(cmd) {
|
|
7773
|
+
return execCmd(`which ${cmd}`).success;
|
|
7774
|
+
}
|
|
7462
7775
|
function isNaudiodonAvailable() {
|
|
7463
7776
|
try {
|
|
7464
7777
|
__require.resolve("naudiodon");
|
|
@@ -7468,11 +7781,12 @@ function isNaudiodonAvailable() {
|
|
|
7468
7781
|
return false;
|
|
7469
7782
|
}
|
|
7470
7783
|
}
|
|
7471
|
-
function
|
|
7472
|
-
if (process.platform !== "linux") return false;
|
|
7784
|
+
function isSpeakerAvailable() {
|
|
7473
7785
|
try {
|
|
7474
|
-
|
|
7475
|
-
|
|
7786
|
+
__require.resolve("speaker");
|
|
7787
|
+
return true;
|
|
7788
|
+
} catch (err) {
|
|
7789
|
+
logger.debug({ err }, "speaker not available");
|
|
7476
7790
|
return false;
|
|
7477
7791
|
}
|
|
7478
7792
|
}
|
|
@@ -7499,10 +7813,20 @@ function createBackend(type = "auto" /* AUTO */, options = {}) {
|
|
|
7499
7813
|
case "darwin":
|
|
7500
7814
|
return new AfplayBackend(options);
|
|
7501
7815
|
case "linux":
|
|
7502
|
-
if (
|
|
7503
|
-
|
|
7816
|
+
if (isCommandAvailable("aplay")) {
|
|
7817
|
+
const test = execCmd("aplay -l");
|
|
7818
|
+
if (test.success && !test.output.includes("no soundcards")) {
|
|
7819
|
+
return new AplayBackend(options);
|
|
7820
|
+
}
|
|
7504
7821
|
}
|
|
7505
|
-
|
|
7822
|
+
if (isCommandAvailable("ffplay")) {
|
|
7823
|
+
return new PlaySoundBackend(options);
|
|
7824
|
+
}
|
|
7825
|
+
if (isSpeakerAvailable()) {
|
|
7826
|
+
return new SpeakerBackend(options);
|
|
7827
|
+
}
|
|
7828
|
+
logger.warn("All Linux audio backends failed, using HowlerBackend as fallback");
|
|
7829
|
+
return new HowlerBackend(options);
|
|
7506
7830
|
case "win32":
|
|
7507
7831
|
return new PowerShellBackend(options);
|
|
7508
7832
|
default:
|
|
@@ -7521,6 +7845,10 @@ function createBackendByType(type, options) {
|
|
|
7521
7845
|
return new PowerShellBackend(options);
|
|
7522
7846
|
case "howler" /* HOWLER */:
|
|
7523
7847
|
return new HowlerBackend(options);
|
|
7848
|
+
case "play-sound" /* PLAY_SOUND */:
|
|
7849
|
+
return new PlaySoundBackend(options);
|
|
7850
|
+
case "speaker" /* SPEAKER */:
|
|
7851
|
+
return new SpeakerBackend(options);
|
|
7524
7852
|
default:
|
|
7525
7853
|
throw new Error(`Unknown backend type: ${type}`);
|
|
7526
7854
|
}
|
|
@@ -7585,7 +7913,7 @@ var AudioPlayer = class extends EventEmitter {
|
|
|
7585
7913
|
this._playing = true;
|
|
7586
7914
|
this._paused = false;
|
|
7587
7915
|
try {
|
|
7588
|
-
const tempFile =
|
|
7916
|
+
const tempFile = join6(tmpdir5(), `ocosay-${Date.now()}.${format}`);
|
|
7589
7917
|
this.currentFile = tempFile;
|
|
7590
7918
|
if (Buffer.isBuffer(audioData)) {
|
|
7591
7919
|
fs.writeFileSync(tempFile, audioData);
|
|
@@ -7757,7 +8085,7 @@ var notificationService = new NotificationService();
|
|
|
7757
8085
|
|
|
7758
8086
|
// src/core/speaker.ts
|
|
7759
8087
|
var logger4 = createModuleLogger("Speaker");
|
|
7760
|
-
var
|
|
8088
|
+
var Speaker2 = class extends EventEmitter2 {
|
|
7761
8089
|
constructor(options = {}) {
|
|
7762
8090
|
super();
|
|
7763
8091
|
this.options = options;
|
|
@@ -7933,7 +8261,7 @@ var Speaker = class extends EventEmitter2 {
|
|
|
7933
8261
|
var defaultSpeaker;
|
|
7934
8262
|
function getDefaultSpeaker() {
|
|
7935
8263
|
if (!defaultSpeaker) {
|
|
7936
|
-
defaultSpeaker = new
|
|
8264
|
+
defaultSpeaker = new Speaker2();
|
|
7937
8265
|
}
|
|
7938
8266
|
return defaultSpeaker;
|
|
7939
8267
|
}
|
|
@@ -9304,7 +9632,7 @@ async function initialize(config) {
|
|
|
9304
9632
|
defaultModel: config?.defaultModel || "stream",
|
|
9305
9633
|
defaultVoice: config?.defaultVoice
|
|
9306
9634
|
};
|
|
9307
|
-
speaker = new
|
|
9635
|
+
speaker = new Speaker2(speakerOptions);
|
|
9308
9636
|
if (config?.autoRead) {
|
|
9309
9637
|
autoReadEnabled = true;
|
|
9310
9638
|
initializeStreamComponents(config);
|
|
@@ -9563,27 +9891,27 @@ function loadOrCreateConfig() {
|
|
|
9563
9891
|
}
|
|
9564
9892
|
|
|
9565
9893
|
// src/plugin.ts
|
|
9566
|
-
import { readFileSync as readFileSync2, existsSync as
|
|
9894
|
+
import { readFileSync as readFileSync2, existsSync as existsSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
9567
9895
|
import { fileURLToPath } from "url";
|
|
9568
|
-
import { dirname as dirname2, join as
|
|
9896
|
+
import { dirname as dirname2, join as join8 } from "path";
|
|
9569
9897
|
import { homedir as homedir3 } from "os";
|
|
9570
|
-
import { execSync } from "child_process";
|
|
9898
|
+
import { execSync as execSync2 } from "child_process";
|
|
9571
9899
|
import { createRequire } from "module";
|
|
9572
9900
|
var logger8 = createModuleLogger("Plugin");
|
|
9573
9901
|
var require2 = createRequire(import.meta.url);
|
|
9574
9902
|
function getSkipFilePath() {
|
|
9575
|
-
return
|
|
9903
|
+
return join8(homedir3(), ".config", "opencode", ".naudiodon_skip");
|
|
9576
9904
|
}
|
|
9577
9905
|
function shouldSkipNaudiodon() {
|
|
9578
|
-
return
|
|
9906
|
+
return existsSync7(getSkipFilePath());
|
|
9579
9907
|
}
|
|
9580
9908
|
function markNaudiodonSkipped() {
|
|
9581
9909
|
try {
|
|
9582
|
-
const dir =
|
|
9583
|
-
if (!
|
|
9584
|
-
|
|
9910
|
+
const dir = join8(homedir3(), ".config", "opencode");
|
|
9911
|
+
if (!existsSync7(dir)) {
|
|
9912
|
+
execSync2("mkdir -p", { cwd: dir });
|
|
9585
9913
|
}
|
|
9586
|
-
|
|
9914
|
+
writeFileSync6(getSkipFilePath(), Date.now().toString(), "utf-8");
|
|
9587
9915
|
} catch {
|
|
9588
9916
|
}
|
|
9589
9917
|
}
|
|
@@ -9601,7 +9929,7 @@ async function rebuildNaudiodonDependency(dep) {
|
|
|
9601
9929
|
const naudiodonPath = dirname2(require2.resolve("naudiodon"));
|
|
9602
9930
|
try {
|
|
9603
9931
|
notificationService.info(`\u6B63\u5728\u7F16\u8BD1 ${dep}...`, "Ocosay \u4F9D\u8D56", 4e3);
|
|
9604
|
-
|
|
9932
|
+
execSync2(`npm rebuild ${dep}`, {
|
|
9605
9933
|
cwd: naudiodonPath,
|
|
9606
9934
|
stdio: "inherit"
|
|
9607
9935
|
});
|
|
@@ -9631,7 +9959,7 @@ async function fixNaudiodonDependencies(maxRetries = 5) {
|
|
|
9631
9959
|
} catch {
|
|
9632
9960
|
try {
|
|
9633
9961
|
notificationService.info(`\u6B63\u5728\u5B89\u88C5 ${dep}...`, "Ocosay", 4e3);
|
|
9634
|
-
|
|
9962
|
+
execSync2(`npm install ${dep}`, {
|
|
9635
9963
|
cwd: naudiodonPath,
|
|
9636
9964
|
stdio: "inherit"
|
|
9637
9965
|
});
|
|
@@ -9645,7 +9973,7 @@ async function fixNaudiodonDependencies(maxRetries = 5) {
|
|
|
9645
9973
|
if (!anySuccess || !await verifyNaudiodonLoad()) {
|
|
9646
9974
|
try {
|
|
9647
9975
|
notificationService.info("\u6B63\u5728\u91CD\u65B0\u7F16\u8BD1 naudiodon...", "Ocosay", 4e3);
|
|
9648
|
-
|
|
9976
|
+
execSync2("npm rebuild naudiodon", {
|
|
9649
9977
|
cwd: naudiodonPath,
|
|
9650
9978
|
stdio: "inherit"
|
|
9651
9979
|
});
|
|
@@ -9680,7 +10008,7 @@ async function ensureNaudiodonCompiled() {
|
|
|
9680
10008
|
try {
|
|
9681
10009
|
const naudiodonPath = dirname2(require2.resolve("naudiodon"));
|
|
9682
10010
|
logger8.info({ naudiodonPath }, "found naudiodon, rebuilding");
|
|
9683
|
-
|
|
10011
|
+
execSync2("npm rebuild naudiodon", {
|
|
9684
10012
|
cwd: naudiodonPath,
|
|
9685
10013
|
stdio: "inherit"
|
|
9686
10014
|
});
|
|
@@ -9705,7 +10033,7 @@ async function ensureNaudiodonCompiled() {
|
|
|
9705
10033
|
try {
|
|
9706
10034
|
const naudiodonPath = dirname2(require2.resolve("naudiodon"));
|
|
9707
10035
|
notificationService.info("\u6B63\u5728\u91CD\u65B0\u7F16\u8BD1 naudiodon...", "Ocosay", 5e3);
|
|
9708
|
-
|
|
10036
|
+
execSync2("npm rebuild naudiodon", {
|
|
9709
10037
|
cwd: naudiodonPath,
|
|
9710
10038
|
stdio: "inherit"
|
|
9711
10039
|
});
|
|
@@ -9734,9 +10062,9 @@ async function ensureNaudiodonCompiled() {
|
|
|
9734
10062
|
}
|
|
9735
10063
|
}
|
|
9736
10064
|
}
|
|
9737
|
-
function
|
|
10065
|
+
function execCmd2(cmd) {
|
|
9738
10066
|
try {
|
|
9739
|
-
const output =
|
|
10067
|
+
const output = execSync2(cmd, { stdio: "pipe", encoding: "utf8" });
|
|
9740
10068
|
return { success: true, output };
|
|
9741
10069
|
} catch (err) {
|
|
9742
10070
|
return { success: false, output: err.message || "" };
|
|
@@ -9751,11 +10079,34 @@ function isWsl2() {
|
|
|
9751
10079
|
}
|
|
9752
10080
|
}
|
|
9753
10081
|
function checkAlsa() {
|
|
9754
|
-
const result =
|
|
10082
|
+
const result = execCmd2("which aplay");
|
|
9755
10083
|
if (!result.success) return false;
|
|
9756
|
-
const test =
|
|
10084
|
+
const test = execCmd2("aplay -l");
|
|
9757
10085
|
return test.success && !test.output.includes("no soundcards");
|
|
9758
10086
|
}
|
|
10087
|
+
function checkFFplay() {
|
|
10088
|
+
return execCmd2("which ffplay").success;
|
|
10089
|
+
}
|
|
10090
|
+
async function checkAudioEnvironmentForBackend() {
|
|
10091
|
+
const platform = process.platform;
|
|
10092
|
+
if (platform === "linux") {
|
|
10093
|
+
const hasAlsa = checkAlsa();
|
|
10094
|
+
const hasFFplay = checkFFplay();
|
|
10095
|
+
if (!hasAlsa && !hasFFplay) {
|
|
10096
|
+
notificationService.warning(
|
|
10097
|
+
"\u672A\u68C0\u6D4B\u5230\u97F3\u9891\u8BBE\u5907",
|
|
10098
|
+
"\u8BF7\u5B89\u88C5 ffmpeg \u6216\u914D\u7F6E PulseAudio",
|
|
10099
|
+
8e3
|
|
10100
|
+
);
|
|
10101
|
+
} else if (hasFFplay && !hasAlsa) {
|
|
10102
|
+
notificationService.info(
|
|
10103
|
+
"ffplay \u53EF\u7528",
|
|
10104
|
+
"\u5C06\u4F7F\u7528 ffplay \u8FDB\u884C\u65E0\u58F0\u5361\u64AD\u653E",
|
|
10105
|
+
5e3
|
|
10106
|
+
);
|
|
10107
|
+
}
|
|
10108
|
+
}
|
|
10109
|
+
}
|
|
9759
10110
|
async function installPortAudio() {
|
|
9760
10111
|
const platform = process.platform;
|
|
9761
10112
|
const wsl = isWsl2();
|
|
@@ -9764,7 +10115,7 @@ async function installPortAudio() {
|
|
|
9764
10115
|
logger8.info(`Running: ${cmd}`);
|
|
9765
10116
|
notificationService.info(desc, "\u6B63\u5728\u5B89\u88C5...", 5e3);
|
|
9766
10117
|
try {
|
|
9767
|
-
|
|
10118
|
+
execSync2(cmd, { stdio: "inherit" });
|
|
9768
10119
|
return true;
|
|
9769
10120
|
} catch (err) {
|
|
9770
10121
|
const msg = err.message || "";
|
|
@@ -9787,6 +10138,23 @@ async function installPortAudio() {
|
|
|
9787
10138
|
}
|
|
9788
10139
|
};
|
|
9789
10140
|
if (platform === "linux" || wsl) {
|
|
10141
|
+
notificationService.info("\u68C0\u6D4B ffmpeg...", "\u97F3\u9891\u540E\u7AEF", 5e3);
|
|
10142
|
+
const ffmpegCheck = execCmd2("which ffplay");
|
|
10143
|
+
if (ffmpegCheck.success) {
|
|
10144
|
+
logger8.info("ffmpeg already available");
|
|
10145
|
+
notificationService.success("ffmpeg \u5C31\u7EEA", "ffplay \u53EF\u7528\u4E8E\u65E0\u58F0\u5361\u64AD\u653E", 5e3);
|
|
10146
|
+
} else {
|
|
10147
|
+
notificationService.info("\u5B89\u88C5 ffmpeg...", "\u97F3\u9891\u540E\u7AEF", 5e3);
|
|
10148
|
+
const ffmpegInstalled = await runInstall(
|
|
10149
|
+
"sudo apt-get update && sudo apt-get install -y ffmpeg",
|
|
10150
|
+
"\u5B89\u88C5 ffmpeg"
|
|
10151
|
+
);
|
|
10152
|
+
if (ffmpegInstalled) {
|
|
10153
|
+
notificationService.success("ffmpeg \u5B89\u88C5\u6210\u529F", "ffplay \u53EF\u7528\u4E8E\u65E0\u58F0\u5361\u64AD\u653E", 5e3);
|
|
10154
|
+
} else {
|
|
10155
|
+
notificationService.warning("ffmpeg \u5B89\u88C5\u5931\u8D25", "play-sound \u540E\u7AEF\u53EF\u80FD\u65E0\u6CD5\u5DE5\u4F5C", 5e3);
|
|
10156
|
+
}
|
|
10157
|
+
}
|
|
9790
10158
|
notificationService.info("\u68C0\u6D4B\u97F3\u9891\u8BBE\u5907...", "\u97F3\u9891\u540E\u7AEF", 5e3);
|
|
9791
10159
|
if (checkAlsa()) {
|
|
9792
10160
|
logger8.info("alsa-utils already available and working");
|
|
@@ -9843,7 +10211,7 @@ var __dirname2 = dirname2(__filename);
|
|
|
9843
10211
|
var id = "ocosay";
|
|
9844
10212
|
var pluginVersion = "0.0.0";
|
|
9845
10213
|
try {
|
|
9846
|
-
const pkg = JSON.parse(readFileSync2(
|
|
10214
|
+
const pkg = JSON.parse(readFileSync2(join8(__dirname2, "package.json"), "utf-8"));
|
|
9847
10215
|
pluginVersion = pkg.version || "0.0.0";
|
|
9848
10216
|
} catch {
|
|
9849
10217
|
}
|
|
@@ -9983,6 +10351,7 @@ var server = (async (input, _options) => {
|
|
|
9983
10351
|
logger8.error({ error: initError }, "initialization failed");
|
|
9984
10352
|
}
|
|
9985
10353
|
await ensureNaudiodonCompiled();
|
|
10354
|
+
await checkAudioEnvironmentForBackend();
|
|
9986
10355
|
setTimeout(() => {
|
|
9987
10356
|
if (initError) {
|
|
9988
10357
|
notificationService.error(
|