@mingxy/ocosay 1.1.31 → 1.1.33
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.
|
@@ -18,6 +18,7 @@ import { HowlerBackend } from './howler-backend';
|
|
|
18
18
|
import { PlaySoundBackend } from './playsound-backend';
|
|
19
19
|
import { SpeakerBackend } from './speaker-backend';
|
|
20
20
|
import { logger } from '../../utils/logger';
|
|
21
|
+
import { notificationService } from '../notification';
|
|
21
22
|
function execCmd(cmd) {
|
|
22
23
|
try {
|
|
23
24
|
const output = execSync(cmd, { stdio: 'pipe', encoding: 'utf8' });
|
|
@@ -97,6 +98,7 @@ export function createBackend(type = BackendType.AUTO, options = {}) {
|
|
|
97
98
|
}
|
|
98
99
|
catch (err) {
|
|
99
100
|
logger.error({ err }, 'failed to initialize naudiodon backend');
|
|
101
|
+
notificationService.warning('naudiodon 初始化失败', '将使用其他音频后端', 5000);
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
104
|
switch (platform) {
|
|
@@ -24,7 +24,7 @@ export declare class PlaySoundBackend implements AudioBackend {
|
|
|
24
24
|
private chunks;
|
|
25
25
|
private hasEnded;
|
|
26
26
|
constructor(options?: BackendOptions);
|
|
27
|
-
start(filePath: string): void
|
|
27
|
+
start(filePath: string): Promise<void>;
|
|
28
28
|
private playWithPlaySound;
|
|
29
29
|
write(chunk: Buffer): void;
|
|
30
30
|
end(): void;
|
|
@@ -32,7 +32,7 @@ export class PlaySoundBackend {
|
|
|
32
32
|
constructor(options = {}) {
|
|
33
33
|
this.events = options.events;
|
|
34
34
|
}
|
|
35
|
-
start(filePath) {
|
|
35
|
+
async start(filePath) {
|
|
36
36
|
if (this._started)
|
|
37
37
|
return;
|
|
38
38
|
if (!SAFE_PATH_REGEX.test(filePath)) {
|
|
@@ -42,8 +42,8 @@ export class PlaySoundBackend {
|
|
|
42
42
|
this._started = true;
|
|
43
43
|
this._stopped = false;
|
|
44
44
|
this.events?.onStart?.();
|
|
45
|
-
//
|
|
46
|
-
this.playWithPlaySound(filePath);
|
|
45
|
+
// 等待 play-sound 播放完成
|
|
46
|
+
await this.playWithPlaySound(filePath);
|
|
47
47
|
}
|
|
48
48
|
async playWithPlaySound(filePath) {
|
|
49
49
|
try {
|
package/dist/core/player.js
CHANGED
|
@@ -120,11 +120,8 @@ export class AudioPlayer extends EventEmitter {
|
|
|
120
120
|
* 播放音频文件
|
|
121
121
|
* 使用 AudioBackend 统一后端播放
|
|
122
122
|
*/
|
|
123
|
-
playFile(filePath, _format) {
|
|
124
|
-
|
|
125
|
-
this.backend.start(filePath);
|
|
126
|
-
resolve(); // backend.start() 是同步的,立即 resolve
|
|
127
|
-
});
|
|
123
|
+
async playFile(filePath, _format) {
|
|
124
|
+
await this.backend.start(filePath);
|
|
128
125
|
}
|
|
129
126
|
pause() {
|
|
130
127
|
if (!this._playing || this._paused)
|
package/dist/package.json
CHANGED
package/dist/plugin.js
CHANGED
|
@@ -2437,7 +2437,7 @@ var require_thread_stream = __commonJS({
|
|
|
2437
2437
|
var require_transport = __commonJS({
|
|
2438
2438
|
"node_modules/pino/lib/transport.js"(exports, module) {
|
|
2439
2439
|
"use strict";
|
|
2440
|
-
var { createRequire:
|
|
2440
|
+
var { createRequire: createRequire2 } = __require("module");
|
|
2441
2441
|
var { existsSync: existsSync8 } = __require("node:fs");
|
|
2442
2442
|
var getCallers = require_caller();
|
|
2443
2443
|
var { join: join9, isAbsolute, sep } = __require("node:path");
|
|
@@ -2640,7 +2640,7 @@ var require_transport = __commonJS({
|
|
|
2640
2640
|
for (const filePath of callers) {
|
|
2641
2641
|
try {
|
|
2642
2642
|
const context = filePath === "node:repl" ? process.cwd() + sep : filePath;
|
|
2643
|
-
fixTarget2 =
|
|
2643
|
+
fixTarget2 = createRequire2(context).resolve(origin);
|
|
2644
2644
|
break;
|
|
2645
2645
|
} catch (err) {
|
|
2646
2646
|
continue;
|
|
@@ -7487,7 +7487,7 @@ var PlaySoundBackend = class {
|
|
|
7487
7487
|
constructor(options = {}) {
|
|
7488
7488
|
this.events = options.events;
|
|
7489
7489
|
}
|
|
7490
|
-
start(filePath) {
|
|
7490
|
+
async start(filePath) {
|
|
7491
7491
|
if (this._started) return;
|
|
7492
7492
|
if (!SAFE_PATH_REGEX4.test(filePath)) {
|
|
7493
7493
|
throw new Error(`Invalid file path: ${filePath}`);
|
|
@@ -7496,7 +7496,7 @@ var PlaySoundBackend = class {
|
|
|
7496
7496
|
this._started = true;
|
|
7497
7497
|
this._stopped = false;
|
|
7498
7498
|
this.events?.onStart?.();
|
|
7499
|
-
this.playWithPlaySound(filePath);
|
|
7499
|
+
await this.playWithPlaySound(filePath);
|
|
7500
7500
|
}
|
|
7501
7501
|
async playWithPlaySound(filePath) {
|
|
7502
7502
|
try {
|
|
@@ -7767,6 +7767,81 @@ var SpeakerBackend = class {
|
|
|
7767
7767
|
|
|
7768
7768
|
// src/core/backends/index.ts
|
|
7769
7769
|
import { execSync } from "child_process";
|
|
7770
|
+
|
|
7771
|
+
// src/core/notification.ts
|
|
7772
|
+
var logger3 = createModuleLogger("NotificationService");
|
|
7773
|
+
var NotificationService = class {
|
|
7774
|
+
tui = null;
|
|
7775
|
+
pendingToasts = [];
|
|
7776
|
+
retryTimer;
|
|
7777
|
+
setTui(tui) {
|
|
7778
|
+
this.tui = tui;
|
|
7779
|
+
logger3.debug("tui reference set");
|
|
7780
|
+
this.flushPending();
|
|
7781
|
+
}
|
|
7782
|
+
showToast(options) {
|
|
7783
|
+
const { title, message, variant = "info", duration = 5e3 } = options;
|
|
7784
|
+
if (!this.tui) {
|
|
7785
|
+
logger3.debug({ title }, "tui not ready, queueing toast");
|
|
7786
|
+
this.pendingToasts.push(options);
|
|
7787
|
+
this.scheduleRetry();
|
|
7788
|
+
return false;
|
|
7789
|
+
}
|
|
7790
|
+
try {
|
|
7791
|
+
this.tui.showToast({
|
|
7792
|
+
body: {
|
|
7793
|
+
title,
|
|
7794
|
+
message,
|
|
7795
|
+
variant,
|
|
7796
|
+
duration
|
|
7797
|
+
}
|
|
7798
|
+
});
|
|
7799
|
+
logger3.debug({ title, variant }, "toast shown");
|
|
7800
|
+
return true;
|
|
7801
|
+
} catch (err) {
|
|
7802
|
+
logger3.warn({ err, title }, "toast call failed, queueing for retry");
|
|
7803
|
+
this.pendingToasts.push(options);
|
|
7804
|
+
this.scheduleRetry();
|
|
7805
|
+
return false;
|
|
7806
|
+
}
|
|
7807
|
+
}
|
|
7808
|
+
scheduleRetry() {
|
|
7809
|
+
if (this.retryTimer) return;
|
|
7810
|
+
this.retryTimer = setTimeout(() => {
|
|
7811
|
+
this.retryTimer = void 0;
|
|
7812
|
+
this.flushPending();
|
|
7813
|
+
}, 2e3);
|
|
7814
|
+
}
|
|
7815
|
+
flushPending() {
|
|
7816
|
+
if (this.pendingToasts.length === 0 || !this.tui) return;
|
|
7817
|
+
logger3.info({ count: this.pendingToasts.length }, "flushing pending toasts");
|
|
7818
|
+
const pending = [...this.pendingToasts];
|
|
7819
|
+
this.pendingToasts = [];
|
|
7820
|
+
for (const toast of pending) {
|
|
7821
|
+
try {
|
|
7822
|
+
this.showToast(toast);
|
|
7823
|
+
} catch (err) {
|
|
7824
|
+
this.pendingToasts.push(toast);
|
|
7825
|
+
logger3.warn({ err }, "showToast threw unexpected error, re-queued");
|
|
7826
|
+
}
|
|
7827
|
+
}
|
|
7828
|
+
}
|
|
7829
|
+
success(title, message, duration) {
|
|
7830
|
+
return this.showToast({ title, message, variant: "success", duration });
|
|
7831
|
+
}
|
|
7832
|
+
error(title, message, duration) {
|
|
7833
|
+
return this.showToast({ title, message, variant: "error", duration });
|
|
7834
|
+
}
|
|
7835
|
+
info(title, message, duration) {
|
|
7836
|
+
return this.showToast({ title, message, variant: "info", duration });
|
|
7837
|
+
}
|
|
7838
|
+
warning(title, message, duration) {
|
|
7839
|
+
return this.showToast({ title, message, variant: "warning", duration });
|
|
7840
|
+
}
|
|
7841
|
+
};
|
|
7842
|
+
var notificationService = new NotificationService();
|
|
7843
|
+
|
|
7844
|
+
// src/core/backends/index.ts
|
|
7770
7845
|
function execCmd(cmd) {
|
|
7771
7846
|
try {
|
|
7772
7847
|
const output = execSync(cmd, { stdio: "pipe", encoding: "utf8" });
|
|
@@ -7813,6 +7888,11 @@ function createBackend(type = "auto" /* AUTO */, options = {}) {
|
|
|
7813
7888
|
}
|
|
7814
7889
|
} catch (err) {
|
|
7815
7890
|
logger.error({ err }, "failed to initialize naudiodon backend");
|
|
7891
|
+
notificationService.warning(
|
|
7892
|
+
"naudiodon \u521D\u59CB\u5316\u5931\u8D25",
|
|
7893
|
+
"\u5C06\u4F7F\u7528\u5176\u4ED6\u97F3\u9891\u540E\u7AEF",
|
|
7894
|
+
5e3
|
|
7895
|
+
);
|
|
7816
7896
|
}
|
|
7817
7897
|
}
|
|
7818
7898
|
switch (platform) {
|
|
@@ -7966,11 +8046,8 @@ var AudioPlayer = class extends EventEmitter {
|
|
|
7966
8046
|
* 播放音频文件
|
|
7967
8047
|
* 使用 AudioBackend 统一后端播放
|
|
7968
8048
|
*/
|
|
7969
|
-
playFile(filePath, _format) {
|
|
7970
|
-
|
|
7971
|
-
this.backend.start(filePath);
|
|
7972
|
-
resolve();
|
|
7973
|
-
});
|
|
8049
|
+
async playFile(filePath, _format) {
|
|
8050
|
+
await this.backend.start(filePath);
|
|
7974
8051
|
}
|
|
7975
8052
|
pause() {
|
|
7976
8053
|
if (!this._playing || this._paused) return;
|
|
@@ -8016,79 +8093,6 @@ var AudioPlayer = class extends EventEmitter {
|
|
|
8016
8093
|
}
|
|
8017
8094
|
};
|
|
8018
8095
|
|
|
8019
|
-
// src/core/notification.ts
|
|
8020
|
-
var logger3 = createModuleLogger("NotificationService");
|
|
8021
|
-
var NotificationService = class {
|
|
8022
|
-
tui = null;
|
|
8023
|
-
pendingToasts = [];
|
|
8024
|
-
retryTimer;
|
|
8025
|
-
setTui(tui) {
|
|
8026
|
-
this.tui = tui;
|
|
8027
|
-
logger3.debug("tui reference set");
|
|
8028
|
-
this.flushPending();
|
|
8029
|
-
}
|
|
8030
|
-
showToast(options) {
|
|
8031
|
-
const { title, message, variant = "info", duration = 5e3 } = options;
|
|
8032
|
-
if (!this.tui) {
|
|
8033
|
-
logger3.debug({ title }, "tui not ready, queueing toast");
|
|
8034
|
-
this.pendingToasts.push(options);
|
|
8035
|
-
this.scheduleRetry();
|
|
8036
|
-
return false;
|
|
8037
|
-
}
|
|
8038
|
-
try {
|
|
8039
|
-
this.tui.showToast({
|
|
8040
|
-
body: {
|
|
8041
|
-
title,
|
|
8042
|
-
message,
|
|
8043
|
-
variant,
|
|
8044
|
-
duration
|
|
8045
|
-
}
|
|
8046
|
-
});
|
|
8047
|
-
logger3.debug({ title, variant }, "toast shown");
|
|
8048
|
-
return true;
|
|
8049
|
-
} catch (err) {
|
|
8050
|
-
logger3.warn({ err, title }, "toast call failed, queueing for retry");
|
|
8051
|
-
this.pendingToasts.push(options);
|
|
8052
|
-
this.scheduleRetry();
|
|
8053
|
-
return false;
|
|
8054
|
-
}
|
|
8055
|
-
}
|
|
8056
|
-
scheduleRetry() {
|
|
8057
|
-
if (this.retryTimer) return;
|
|
8058
|
-
this.retryTimer = setTimeout(() => {
|
|
8059
|
-
this.retryTimer = void 0;
|
|
8060
|
-
this.flushPending();
|
|
8061
|
-
}, 2e3);
|
|
8062
|
-
}
|
|
8063
|
-
flushPending() {
|
|
8064
|
-
if (this.pendingToasts.length === 0 || !this.tui) return;
|
|
8065
|
-
logger3.info({ count: this.pendingToasts.length }, "flushing pending toasts");
|
|
8066
|
-
const pending = [...this.pendingToasts];
|
|
8067
|
-
this.pendingToasts = [];
|
|
8068
|
-
for (const toast of pending) {
|
|
8069
|
-
try {
|
|
8070
|
-
this.showToast(toast);
|
|
8071
|
-
} catch (err) {
|
|
8072
|
-
this.pendingToasts.push(toast);
|
|
8073
|
-
logger3.warn({ err }, "showToast threw unexpected error, re-queued");
|
|
8074
|
-
}
|
|
8075
|
-
}
|
|
8076
|
-
}
|
|
8077
|
-
success(title, message, duration) {
|
|
8078
|
-
return this.showToast({ title, message, variant: "success", duration });
|
|
8079
|
-
}
|
|
8080
|
-
error(title, message, duration) {
|
|
8081
|
-
return this.showToast({ title, message, variant: "error", duration });
|
|
8082
|
-
}
|
|
8083
|
-
info(title, message, duration) {
|
|
8084
|
-
return this.showToast({ title, message, variant: "info", duration });
|
|
8085
|
-
}
|
|
8086
|
-
warning(title, message, duration) {
|
|
8087
|
-
return this.showToast({ title, message, variant: "warning", duration });
|
|
8088
|
-
}
|
|
8089
|
-
};
|
|
8090
|
-
var notificationService = new NotificationService();
|
|
8091
|
-
|
|
8092
8096
|
// src/core/speaker.ts
|
|
8093
8097
|
var logger4 = createModuleLogger("Speaker");
|
|
8094
8098
|
var Speaker2 = class extends EventEmitter2 {
|
|
@@ -9901,360 +9905,8 @@ import { readFileSync as readFileSync2, existsSync as existsSync7, writeFileSync
|
|
|
9901
9905
|
import { fileURLToPath } from "url";
|
|
9902
9906
|
import { dirname as dirname2, join as join8 } from "path";
|
|
9903
9907
|
import { homedir as homedir3 } from "os";
|
|
9904
|
-
import { exec, execSync as
|
|
9905
|
-
import { createRequire as createRequire2 } from "module";
|
|
9906
|
-
|
|
9907
|
-
// src/core/dependency-detector.ts
|
|
9908
|
-
import { execSync as execSync2 } from "child_process";
|
|
9909
|
-
function parseMissingHeaders(errorOutput) {
|
|
9910
|
-
const pattern = /fatal error:\s+([^:]+):\s+No such file or directory/g;
|
|
9911
|
-
const headers = [];
|
|
9912
|
-
let match = pattern.exec(errorOutput);
|
|
9913
|
-
while (match !== null) {
|
|
9914
|
-
headers.push(match[1]);
|
|
9915
|
-
match = pattern.exec(errorOutput);
|
|
9916
|
-
}
|
|
9917
|
-
return headers;
|
|
9918
|
-
}
|
|
9919
|
-
function execCapture(cmd, cwd) {
|
|
9920
|
-
try {
|
|
9921
|
-
const stdout = execSync2(cmd, {
|
|
9922
|
-
cwd,
|
|
9923
|
-
encoding: "utf8",
|
|
9924
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
9925
|
-
});
|
|
9926
|
-
return { success: true, stdout, stderr: "" };
|
|
9927
|
-
} catch (err) {
|
|
9928
|
-
const error = err;
|
|
9929
|
-
return {
|
|
9930
|
-
success: false,
|
|
9931
|
-
stdout: error.stdout ? error.stdout.toString() : "",
|
|
9932
|
-
stderr: error.stderr ? error.stderr.toString() : ""
|
|
9933
|
-
};
|
|
9934
|
-
}
|
|
9935
|
-
}
|
|
9936
|
-
function detectMissingDependencies(errorOutput) {
|
|
9937
|
-
const missingHeaders = parseMissingHeaders(errorOutput);
|
|
9938
|
-
return {
|
|
9939
|
-
missingHeaders,
|
|
9940
|
-
rawOutput: errorOutput
|
|
9941
|
-
};
|
|
9942
|
-
}
|
|
9943
|
-
|
|
9944
|
-
// src/core/dependency-mapper.ts
|
|
9908
|
+
import { exec, execSync as execSync2 } from "child_process";
|
|
9945
9909
|
import { createRequire } from "module";
|
|
9946
|
-
var require2 = createRequire(import.meta.url);
|
|
9947
|
-
var HEADER_TO_PACKAGE = [
|
|
9948
|
-
// ALSA - Linux音频库
|
|
9949
|
-
{
|
|
9950
|
-
header: "alsa/asoundlib.h",
|
|
9951
|
-
package: "libasound2-dev"
|
|
9952
|
-
},
|
|
9953
|
-
// PortAudio - 跨平台音频I/O库
|
|
9954
|
-
{
|
|
9955
|
-
header: "portaudio.h",
|
|
9956
|
-
package: "libportaudio-dev",
|
|
9957
|
-
packageMac: "portaudio",
|
|
9958
|
-
packageWin: "portaudio"
|
|
9959
|
-
},
|
|
9960
|
-
// FFmpeg相关
|
|
9961
|
-
{
|
|
9962
|
-
header: "libavcodec/avcodec.h",
|
|
9963
|
-
package: "libavcodec-dev",
|
|
9964
|
-
packageMac: "ffmpeg",
|
|
9965
|
-
packageWin: "ffmpeg"
|
|
9966
|
-
},
|
|
9967
|
-
{
|
|
9968
|
-
header: "libavformat/avformat.h",
|
|
9969
|
-
package: "libavformat-dev",
|
|
9970
|
-
packageMac: "ffmpeg",
|
|
9971
|
-
packageWin: "ffmpeg"
|
|
9972
|
-
},
|
|
9973
|
-
{
|
|
9974
|
-
header: "libavutil/avutil.h",
|
|
9975
|
-
package: "libavutil-dev",
|
|
9976
|
-
packageMac: "ffmpeg",
|
|
9977
|
-
packageWin: "ffmpeg"
|
|
9978
|
-
},
|
|
9979
|
-
{
|
|
9980
|
-
header: "libswresample/swresample.h",
|
|
9981
|
-
package: "libswresample-dev",
|
|
9982
|
-
packageMac: "ffmpeg",
|
|
9983
|
-
packageWin: "ffmpeg"
|
|
9984
|
-
},
|
|
9985
|
-
// OpenAL - 3D音频API
|
|
9986
|
-
{
|
|
9987
|
-
header: "AL/al.h",
|
|
9988
|
-
package: "libopenal-dev",
|
|
9989
|
-
packageMac: "openal",
|
|
9990
|
-
packageWin: "openal"
|
|
9991
|
-
},
|
|
9992
|
-
// SDL - 多媒体库
|
|
9993
|
-
{
|
|
9994
|
-
header: "SDL2/SDL.h",
|
|
9995
|
-
package: "libsdl2-dev",
|
|
9996
|
-
packageMac: "sdl2",
|
|
9997
|
-
packageWin: "sdl2"
|
|
9998
|
-
},
|
|
9999
|
-
// PulseAudio - Linux音频服务
|
|
10000
|
-
{
|
|
10001
|
-
header: "pulse/pulseaudio.h",
|
|
10002
|
-
package: "libpulse-dev"
|
|
10003
|
-
},
|
|
10004
|
-
// CoreAudio - macOS音频框架 (无头文件,纯框架)
|
|
10005
|
-
{
|
|
10006
|
-
header: "CoreAudio/CoreAudio.h",
|
|
10007
|
-
package: "\u81FA",
|
|
10008
|
-
// macOS系统框架,无需安装包
|
|
10009
|
-
packageMac: ""
|
|
10010
|
-
},
|
|
10011
|
-
// Windows特定
|
|
10012
|
-
{
|
|
10013
|
-
header: "windows.h",
|
|
10014
|
-
package: "",
|
|
10015
|
-
// Linux上不存在
|
|
10016
|
-
packageWin: ""
|
|
10017
|
-
// Windows SDK自带
|
|
10018
|
-
}
|
|
10019
|
-
];
|
|
10020
|
-
function detectPlatform() {
|
|
10021
|
-
const platform = process.platform;
|
|
10022
|
-
const isWsl3 = detectWsl();
|
|
10023
|
-
let packageManager;
|
|
10024
|
-
let installCommand;
|
|
10025
|
-
if (platform === "linux" || isWsl3) {
|
|
10026
|
-
packageManager = "apt-get";
|
|
10027
|
-
installCommand = "sudo apt-get install -y";
|
|
10028
|
-
} else if (platform === "darwin") {
|
|
10029
|
-
packageManager = "brew";
|
|
10030
|
-
installCommand = "brew install";
|
|
10031
|
-
} else {
|
|
10032
|
-
packageManager = "choco";
|
|
10033
|
-
installCommand = "choco install -y";
|
|
10034
|
-
}
|
|
10035
|
-
return {
|
|
10036
|
-
platform,
|
|
10037
|
-
isWsl: isWsl3,
|
|
10038
|
-
packageManager,
|
|
10039
|
-
installCommand
|
|
10040
|
-
};
|
|
10041
|
-
}
|
|
10042
|
-
function detectWsl() {
|
|
10043
|
-
if (process.platform !== "linux") return false;
|
|
10044
|
-
try {
|
|
10045
|
-
return require2("fs").readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft");
|
|
10046
|
-
} catch {
|
|
10047
|
-
return false;
|
|
10048
|
-
}
|
|
10049
|
-
}
|
|
10050
|
-
function getPackageForPlatform(mapping, platform, isWsl3) {
|
|
10051
|
-
if (platform === "win32") {
|
|
10052
|
-
if (isWsl3) {
|
|
10053
|
-
return mapping.package || null;
|
|
10054
|
-
}
|
|
10055
|
-
return mapping.packageWin || mapping.package || null;
|
|
10056
|
-
}
|
|
10057
|
-
if (platform === "darwin") {
|
|
10058
|
-
if (mapping.packageMac === "") return null;
|
|
10059
|
-
return mapping.packageMac || mapping.package || null;
|
|
10060
|
-
}
|
|
10061
|
-
if (mapping.package === "") return null;
|
|
10062
|
-
return mapping.package || null;
|
|
10063
|
-
}
|
|
10064
|
-
function mapHeaderToPackages(header, platform) {
|
|
10065
|
-
const exactMatch = HEADER_TO_PACKAGE.find(
|
|
10066
|
-
(m) => m.header.toLowerCase() === header.toLowerCase()
|
|
10067
|
-
);
|
|
10068
|
-
if (exactMatch) {
|
|
10069
|
-
const pkg = getPackageForPlatform(exactMatch, platform, detectWsl());
|
|
10070
|
-
if (pkg && pkg.length > 0 && pkg !== "\u81FA") {
|
|
10071
|
-
return [pkg];
|
|
10072
|
-
}
|
|
10073
|
-
return [];
|
|
10074
|
-
}
|
|
10075
|
-
const headerBasename = header.split("/").pop()?.toLowerCase() || "";
|
|
10076
|
-
const fuzzyMatch = HEADER_TO_PACKAGE.find((m) => {
|
|
10077
|
-
const mappingBasename = m.header.split("/").pop()?.toLowerCase() || "";
|
|
10078
|
-
return mappingBasename === headerBasename;
|
|
10079
|
-
});
|
|
10080
|
-
if (fuzzyMatch) {
|
|
10081
|
-
const pkg = getPackageForPlatform(fuzzyMatch, platform, detectWsl());
|
|
10082
|
-
if (pkg && pkg.length > 0 && pkg !== "\u81FA") {
|
|
10083
|
-
return [pkg];
|
|
10084
|
-
}
|
|
10085
|
-
return [];
|
|
10086
|
-
}
|
|
10087
|
-
return [];
|
|
10088
|
-
}
|
|
10089
|
-
function mapHeadersToPackages(headers, platform) {
|
|
10090
|
-
const packageSet = /* @__PURE__ */ new Set();
|
|
10091
|
-
for (const header of headers) {
|
|
10092
|
-
const packages = mapHeaderToPackages(header, platform);
|
|
10093
|
-
for (const pkg of packages) {
|
|
10094
|
-
if (pkg.length > 0) {
|
|
10095
|
-
packageSet.add(pkg);
|
|
10096
|
-
}
|
|
10097
|
-
}
|
|
10098
|
-
}
|
|
10099
|
-
return Array.from(packageSet);
|
|
10100
|
-
}
|
|
10101
|
-
|
|
10102
|
-
// src/core/dependency-installer.ts
|
|
10103
|
-
import { execSync as execSync3 } from "child_process";
|
|
10104
|
-
var logger8 = createModuleLogger("DependencyInstaller");
|
|
10105
|
-
function getInstallCommandPrefix() {
|
|
10106
|
-
const { platform, isWsl: isWsl3 } = detectPlatform();
|
|
10107
|
-
if (platform === "linux" || isWsl3) {
|
|
10108
|
-
return "sudo apt-get install -y";
|
|
10109
|
-
} else if (platform === "darwin") {
|
|
10110
|
-
return "brew install";
|
|
10111
|
-
}
|
|
10112
|
-
return "choco install -y";
|
|
10113
|
-
}
|
|
10114
|
-
function getCheckCommand(packageName) {
|
|
10115
|
-
const { platform, isWsl: isWsl3 } = detectPlatform();
|
|
10116
|
-
if (platform === "linux" || isWsl3) {
|
|
10117
|
-
return `dpkg -s ${packageName} 2>/dev/null | grep -q "Status: install ok installed"`;
|
|
10118
|
-
} else if (platform === "darwin") {
|
|
10119
|
-
return `brew list ${packageName} &>/dev/null`;
|
|
10120
|
-
}
|
|
10121
|
-
return `choco list --local-only ${packageName} &>/dev/null`;
|
|
10122
|
-
}
|
|
10123
|
-
function silentLog(level2, message, extra) {
|
|
10124
|
-
switch (level2) {
|
|
10125
|
-
case "info":
|
|
10126
|
-
logger8.info(extra || {}, message);
|
|
10127
|
-
break;
|
|
10128
|
-
case "warn":
|
|
10129
|
-
logger8.warn(extra || {}, message);
|
|
10130
|
-
break;
|
|
10131
|
-
case "error":
|
|
10132
|
-
logger8.error(extra || {}, message);
|
|
10133
|
-
break;
|
|
10134
|
-
}
|
|
10135
|
-
}
|
|
10136
|
-
function canSudoWithoutPassword() {
|
|
10137
|
-
try {
|
|
10138
|
-
execSync3("sudo -n true 2>&1", { stdio: "pipe" });
|
|
10139
|
-
return true;
|
|
10140
|
-
} catch {
|
|
10141
|
-
return false;
|
|
10142
|
-
}
|
|
10143
|
-
}
|
|
10144
|
-
async function installSystemPackages(packages, notifSvc) {
|
|
10145
|
-
const result = {
|
|
10146
|
-
success: false,
|
|
10147
|
-
installedPackages: [],
|
|
10148
|
-
failedPackages: []
|
|
10149
|
-
};
|
|
10150
|
-
const validPackages = packages.filter((p) => p && p.length > 0 && p !== "\u81FA");
|
|
10151
|
-
if (validPackages.length === 0) {
|
|
10152
|
-
silentLog("info", "\u6CA1\u6709\u9700\u8981\u5B89\u88C5\u7684\u5305");
|
|
10153
|
-
return { ...result, success: true };
|
|
10154
|
-
}
|
|
10155
|
-
silentLog("info", `\u5F00\u59CB\u5B89\u88C5\u7CFB\u7EDF\u4F9D\u8D56: ${validPackages.join(", ")}`, { packages: validPackages });
|
|
10156
|
-
notifSvc?.info("\u6B63\u5728\u5B89\u88C5\u7CFB\u7EDF\u4F9D\u8D56...", "Ocosay", 3e3);
|
|
10157
|
-
const installCommand = getInstallCommandPrefix();
|
|
10158
|
-
const fullCommand = `${installCommand} ${validPackages.join(" ")}`;
|
|
10159
|
-
silentLog("info", `\u6267\u884C\u5B89\u88C5\u547D\u4EE4: ${fullCommand}`);
|
|
10160
|
-
if (!canSudoWithoutPassword()) {
|
|
10161
|
-
const msg = "\u9700\u8981 sudo \u6743\u9650\uFF0C\u8BF7\u786E\u4FDD\u5DF2\u914D\u7F6E NOPASSWD";
|
|
10162
|
-
result.error = msg;
|
|
10163
|
-
notifSvc?.error("\u9700\u8981 sudo \u6743\u9650", "\u8BF7\u5728\u7EC8\u7AEF\u6267\u884C: sudo visudo \u6DFB\u52A0 NOPASSWD \u914D\u7F6E\uFF0C\u914D\u7F6E\u597D\u540E\u8BF7\u91CD\u542F OpenCode", 1e4);
|
|
10164
|
-
silentLog("error", msg);
|
|
10165
|
-
return result;
|
|
10166
|
-
}
|
|
10167
|
-
try {
|
|
10168
|
-
const { platform, isWsl: isWsl3 } = detectPlatform();
|
|
10169
|
-
if (platform === "linux" || isWsl3) {
|
|
10170
|
-
notifSvc?.info("\u6B63\u5728\u66F4\u65B0\u5305\u5217\u8868...", "Ocosay", 3e3);
|
|
10171
|
-
silentLog("info", "\u66F4\u65B0 apt \u5305\u5217\u8868");
|
|
10172
|
-
try {
|
|
10173
|
-
execSync3("sudo apt-get update", {
|
|
10174
|
-
timeout: 12e4,
|
|
10175
|
-
encoding: "utf8",
|
|
10176
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
10177
|
-
});
|
|
10178
|
-
silentLog("info", "apt-get update \u5B8C\u6210");
|
|
10179
|
-
} catch (updateErr) {
|
|
10180
|
-
silentLog("warn", "apt-get update \u5931\u8D25\uFF0C\u7EE7\u7EED\u5C1D\u8BD5\u5B89\u88C5", { error: String(updateErr) });
|
|
10181
|
-
}
|
|
10182
|
-
}
|
|
10183
|
-
notifSvc?.info(`\u6B63\u5728\u5B89\u88C5 ${validPackages.length} \u4E2A\u5305...`, "Ocosay", 5e3);
|
|
10184
|
-
const output = execSync3(fullCommand, {
|
|
10185
|
-
timeout: 3e5,
|
|
10186
|
-
encoding: "utf8",
|
|
10187
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
10188
|
-
});
|
|
10189
|
-
silentLog("info", `\u5B89\u88C5\u8F93\u51FA: ${output.substring(0, 500)}`);
|
|
10190
|
-
const verifiedPackages = [];
|
|
10191
|
-
const failedPackages = [];
|
|
10192
|
-
for (const pkg of validPackages) {
|
|
10193
|
-
const isInstalled = await verifyInstallation([pkg]);
|
|
10194
|
-
if (isInstalled) {
|
|
10195
|
-
verifiedPackages.push(pkg);
|
|
10196
|
-
silentLog("info", `\u5305 ${pkg} \u5B89\u88C5\u9A8C\u8BC1\u6210\u529F`);
|
|
10197
|
-
} else {
|
|
10198
|
-
failedPackages.push(pkg);
|
|
10199
|
-
silentLog("warn", `\u5305 ${pkg} \u5B89\u88C5\u9A8C\u8BC1\u5931\u8D25`);
|
|
10200
|
-
}
|
|
10201
|
-
}
|
|
10202
|
-
result.installedPackages = verifiedPackages;
|
|
10203
|
-
result.failedPackages = failedPackages;
|
|
10204
|
-
result.success = failedPackages.length === 0;
|
|
10205
|
-
if (result.success) {
|
|
10206
|
-
notifSvc?.success("\u7CFB\u7EDF\u4F9D\u8D56\u5B89\u88C5\u6210\u529F", verifiedPackages.join(", "), 5e3);
|
|
10207
|
-
silentLog("info", `\u6240\u6709\u5305\u5B89\u88C5\u6210\u529F: ${verifiedPackages.join(", ")}`);
|
|
10208
|
-
} else {
|
|
10209
|
-
const errorMsg = failedPackages.length > 0 ? `\u4EE5\u4E0B\u5305\u5B89\u88C5\u5931\u8D25: ${failedPackages.join(", ")}` : "\u90E8\u5206\u5305\u5B89\u88C5\u5931\u8D25";
|
|
10210
|
-
result.error = errorMsg;
|
|
10211
|
-
notifSvc?.warning("\u90E8\u5206\u4F9D\u8D56\u5B89\u88C5\u5931\u8D25", errorMsg, 8e3);
|
|
10212
|
-
silentLog("warn", errorMsg);
|
|
10213
|
-
}
|
|
10214
|
-
} catch (err) {
|
|
10215
|
-
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
10216
|
-
silentLog("error", `\u5B89\u88C5\u547D\u4EE4\u6267\u884C\u5931\u8D25: ${errorMessage}`, { error: errorMessage });
|
|
10217
|
-
if (errorMessage.includes("sudo") || errorMessage.toLowerCase().includes("password")) {
|
|
10218
|
-
result.error = "\u9700\u8981 sudo \u6743\u9650\uFF0C\u8BF7\u786E\u4FDD\u5DF2\u914D\u7F6E NOPASSWD";
|
|
10219
|
-
notifSvc?.error("\u9700\u8981 sudo \u6743\u9650", "\u8BF7\u5728\u7EC8\u7AEF\u6267\u884C: sudo visudo", 1e4);
|
|
10220
|
-
} else if (errorMessage.includes("already") || errorMessage.includes("is already")) {
|
|
10221
|
-
result.success = true;
|
|
10222
|
-
result.installedPackages = validPackages;
|
|
10223
|
-
notifSvc?.success("\u4F9D\u8D56\u5DF2\u5B58\u5728", "\u65E0\u9700\u91CD\u65B0\u5B89\u88C5", 3e3);
|
|
10224
|
-
} else {
|
|
10225
|
-
result.error = errorMessage;
|
|
10226
|
-
notifSvc?.error("\u4F9D\u8D56\u5B89\u88C5\u5931\u8D25", errorMessage.substring(0, 200), 8e3);
|
|
10227
|
-
}
|
|
10228
|
-
}
|
|
10229
|
-
return result;
|
|
10230
|
-
}
|
|
10231
|
-
async function verifyInstallation(packages) {
|
|
10232
|
-
if (packages.length === 0) {
|
|
10233
|
-
return true;
|
|
10234
|
-
}
|
|
10235
|
-
const validPackages = packages.filter((p) => p && p.length > 0 && p !== "\u81FA");
|
|
10236
|
-
if (validPackages.length === 0) {
|
|
10237
|
-
return true;
|
|
10238
|
-
}
|
|
10239
|
-
silentLog("info", `\u9A8C\u8BC1\u5305\u5B89\u88C5\u72B6\u6001: ${validPackages.join(", ")}`);
|
|
10240
|
-
try {
|
|
10241
|
-
for (const pkg of validPackages) {
|
|
10242
|
-
const checkCmd = getCheckCommand(pkg);
|
|
10243
|
-
const { success } = execCapture(checkCmd);
|
|
10244
|
-
if (!success) {
|
|
10245
|
-
silentLog("warn", `\u5305 ${pkg} \u9A8C\u8BC1\u5931\u8D25`);
|
|
10246
|
-
return false;
|
|
10247
|
-
}
|
|
10248
|
-
}
|
|
10249
|
-
silentLog("info", `\u6240\u6709\u5305\u9A8C\u8BC1\u901A\u8FC7: ${validPackages.join(", ")}`);
|
|
10250
|
-
return true;
|
|
10251
|
-
} catch (err) {
|
|
10252
|
-
silentLog("error", `\u9A8C\u8BC1\u8FC7\u7A0B\u5F02\u5E38: ${String(err)}`);
|
|
10253
|
-
return false;
|
|
10254
|
-
}
|
|
10255
|
-
}
|
|
10256
|
-
|
|
10257
|
-
// src/plugin.ts
|
|
10258
9910
|
function execAsync(cmd, cwd) {
|
|
10259
9911
|
return new Promise((resolve) => {
|
|
10260
9912
|
exec(cmd, { cwd, encoding: "utf8" }, (error, stdout, stderr) => {
|
|
@@ -10262,12 +9914,12 @@ function execAsync(cmd, cwd) {
|
|
|
10262
9914
|
});
|
|
10263
9915
|
});
|
|
10264
9916
|
}
|
|
10265
|
-
var
|
|
10266
|
-
var
|
|
10267
|
-
var opencodeBinPath =
|
|
9917
|
+
var logger8 = createModuleLogger("Plugin");
|
|
9918
|
+
var require2 = createRequire(import.meta.url);
|
|
9919
|
+
var opencodeBinPath = execSync2("which opencode").toString().trim();
|
|
10268
9920
|
var opencodeRoot = dirname2(dirname2(opencodeBinPath));
|
|
10269
9921
|
var opencodeNodeModules = join8(opencodeRoot, "node_modules");
|
|
10270
|
-
var pluginRequire =
|
|
9922
|
+
var pluginRequire = createRequire(join8(opencodeNodeModules, "package.json"));
|
|
10271
9923
|
function getSkipFilePath() {
|
|
10272
9924
|
return join8(homedir3(), ".config", "opencode", ".naudiodon_skip");
|
|
10273
9925
|
}
|
|
@@ -10286,38 +9938,38 @@ async function markNaudiodonSkipped() {
|
|
|
10286
9938
|
}
|
|
10287
9939
|
async function verifyNaudiodonLoad() {
|
|
10288
9940
|
try {
|
|
10289
|
-
|
|
10290
|
-
|
|
9941
|
+
require2("naudiodon");
|
|
9942
|
+
logger8.info("naudiodon loaded successfully");
|
|
10291
9943
|
return true;
|
|
10292
9944
|
} catch (err) {
|
|
10293
|
-
|
|
9945
|
+
logger8.warn({ err }, "naudiodon load failed after rebuild");
|
|
10294
9946
|
return false;
|
|
10295
9947
|
}
|
|
10296
9948
|
}
|
|
10297
9949
|
async function rebuildNaudiodonDependency(dep) {
|
|
10298
|
-
const naudiodonPath = dirname2(
|
|
9950
|
+
const naudiodonPath = dirname2(require2.resolve("naudiodon"));
|
|
10299
9951
|
notificationService.info(`\u6B63\u5728\u7F16\u8BD1 ${dep}...`, "Ocosay \u4F9D\u8D56", 4e3);
|
|
10300
9952
|
const result = await execAsync(`npm rebuild ${dep}`, naudiodonPath);
|
|
10301
9953
|
if (result.error) {
|
|
10302
|
-
|
|
9954
|
+
logger8.warn({ err: result.error }, `${dep} rebuild failed`);
|
|
10303
9955
|
return false;
|
|
10304
9956
|
}
|
|
10305
|
-
|
|
9957
|
+
logger8.info(`${dep} rebuilt successfully`);
|
|
10306
9958
|
return true;
|
|
10307
9959
|
}
|
|
10308
9960
|
async function fixNaudiodonDependencies(maxRetries = 5) {
|
|
10309
|
-
const naudiodonPath = dirname2(
|
|
9961
|
+
const naudiodonPath = dirname2(require2.resolve("naudiodon"));
|
|
10310
9962
|
const criticalDeps = ["segfault-handler", "bindings", "node-pre-gyp"];
|
|
10311
9963
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
10312
9964
|
if (await verifyNaudiodonLoad()) {
|
|
10313
9965
|
return true;
|
|
10314
9966
|
}
|
|
10315
|
-
|
|
9967
|
+
logger8.info({ attempt }, "naudiodon not loadable, trying dependency rebuild");
|
|
10316
9968
|
notificationService.info(`\u6B63\u5728\u68C0\u67E5\u4F9D\u8D56 (${attempt + 1}/${maxRetries})...`, "Ocosay", 3e3);
|
|
10317
9969
|
let anySuccess = false;
|
|
10318
9970
|
for (const dep of criticalDeps) {
|
|
10319
9971
|
try {
|
|
10320
|
-
|
|
9972
|
+
require2.resolve(dep, { paths: [naudiodonPath] });
|
|
10321
9973
|
if (await rebuildNaudiodonDependency(dep)) {
|
|
10322
9974
|
anySuccess = true;
|
|
10323
9975
|
}
|
|
@@ -10325,9 +9977,9 @@ async function fixNaudiodonDependencies(maxRetries = 5) {
|
|
|
10325
9977
|
notificationService.info(`\u6B63\u5728\u5B89\u88C5 ${dep}...`, "Ocosay", 4e3);
|
|
10326
9978
|
const installResult = await execAsync(`npm install ${dep}`, naudiodonPath);
|
|
10327
9979
|
if (installResult.error) {
|
|
10328
|
-
|
|
9980
|
+
logger8.warn({ err: installResult.error }, `${dep} install failed`);
|
|
10329
9981
|
} else {
|
|
10330
|
-
|
|
9982
|
+
logger8.info(`${dep} installed successfully`);
|
|
10331
9983
|
anySuccess = true;
|
|
10332
9984
|
}
|
|
10333
9985
|
}
|
|
@@ -10336,9 +9988,9 @@ async function fixNaudiodonDependencies(maxRetries = 5) {
|
|
|
10336
9988
|
notificationService.info("\u6B63\u5728\u91CD\u65B0\u7F16\u8BD1 naudiodon...", "Ocosay", 4e3);
|
|
10337
9989
|
const rebuildResult = await execAsync("npm rebuild naudiodon", naudiodonPath);
|
|
10338
9990
|
if (rebuildResult.error) {
|
|
10339
|
-
|
|
9991
|
+
logger8.warn({ err: rebuildResult.error }, "naudiodon rebuild failed");
|
|
10340
9992
|
} else {
|
|
10341
|
-
|
|
9993
|
+
logger8.info("naudiodon rebuilt");
|
|
10342
9994
|
anySuccess = true;
|
|
10343
9995
|
}
|
|
10344
9996
|
}
|
|
@@ -10353,35 +10005,35 @@ async function fixNaudiodonDependencies(maxRetries = 5) {
|
|
|
10353
10005
|
}
|
|
10354
10006
|
async function ensureNaudiodonCompiled() {
|
|
10355
10007
|
if (shouldSkipNaudiodon()) {
|
|
10356
|
-
|
|
10008
|
+
logger8.info("naudiodon skipped previously");
|
|
10357
10009
|
return;
|
|
10358
10010
|
}
|
|
10359
10011
|
try {
|
|
10360
|
-
|
|
10361
|
-
|
|
10012
|
+
require2("naudiodon");
|
|
10013
|
+
logger8.info("naudiodon already compiled");
|
|
10362
10014
|
return;
|
|
10363
10015
|
} catch {
|
|
10364
|
-
|
|
10016
|
+
logger8.info("naudiodon not compiled, will attempt to compile");
|
|
10365
10017
|
notificationService.info("\u6B63\u5728\u7F16\u8BD1 naudiodon...", "Ocosay \u97F3\u9891\u540E\u7AEF", 5e3);
|
|
10366
10018
|
}
|
|
10367
10019
|
try {
|
|
10368
|
-
const naudiodonPath = dirname2(
|
|
10369
|
-
|
|
10020
|
+
const naudiodonPath = dirname2(require2.resolve("naudiodon"));
|
|
10021
|
+
logger8.info({ naudiodonPath }, "found naudiodon, rebuilding");
|
|
10370
10022
|
const rebuildResult = await execAsync("npm rebuild naudiodon", naudiodonPath);
|
|
10371
10023
|
if (rebuildResult.error) {
|
|
10372
|
-
|
|
10024
|
+
logger8.warn({ err: rebuildResult.error }, "naudiodon rebuild failed, checking for PortAudio");
|
|
10373
10025
|
notificationService.warning("naudiodon \u7F16\u8BD1\u5931\u8D25", "\u6B63\u5728\u5C1D\u8BD5\u5B89\u88C5 PortAudio...", 5e3);
|
|
10374
10026
|
const installed = await installPortAudio();
|
|
10375
10027
|
if (installed.success) {
|
|
10376
|
-
const retryPath = dirname2(
|
|
10028
|
+
const retryPath = dirname2(require2.resolve("naudiodon"));
|
|
10377
10029
|
notificationService.info("\u6B63\u5728\u91CD\u65B0\u7F16\u8BD1 naudiodon...", "Ocosay", 5e3);
|
|
10378
10030
|
const retryResult = await execAsync("npm rebuild naudiodon", retryPath);
|
|
10379
10031
|
if (retryResult.error) {
|
|
10380
|
-
|
|
10032
|
+
logger8.error({ err: retryResult.error }, "naudiodon compile failed even after PortAudio install");
|
|
10381
10033
|
notificationService.error("naudiodon \u7F16\u8BD1\u5931\u8D25", "\u81EA\u52A8\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u5C1D\u8BD5\u624B\u52A8\u5B89\u88C5", 8e3);
|
|
10382
10034
|
markNaudiodonSkipped();
|
|
10383
10035
|
} else {
|
|
10384
|
-
|
|
10036
|
+
logger8.info("naudiodon compiled successfully after PortAudio install");
|
|
10385
10037
|
const loadSuccess = await verifyNaudiodonLoad();
|
|
10386
10038
|
if (loadSuccess) {
|
|
10387
10039
|
notificationService.success("naudiodon \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
|
|
@@ -10396,12 +10048,12 @@ async function ensureNaudiodonCompiled() {
|
|
|
10396
10048
|
}
|
|
10397
10049
|
}
|
|
10398
10050
|
} else {
|
|
10399
|
-
|
|
10051
|
+
logger8.error("PortAudio install failed");
|
|
10400
10052
|
notificationService.error("PortAudio \u5B89\u88C5\u5931\u8D25", "\u81EA\u52A8\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u5C1D\u8BD5\u624B\u52A8\u5B89\u88C5", 8e3);
|
|
10401
10053
|
markNaudiodonSkipped();
|
|
10402
10054
|
}
|
|
10403
10055
|
} else {
|
|
10404
|
-
|
|
10056
|
+
logger8.info("naudiodon compiled, verifying...");
|
|
10405
10057
|
const loadSuccess = await verifyNaudiodonLoad();
|
|
10406
10058
|
if (loadSuccess) {
|
|
10407
10059
|
notificationService.success("naudiodon \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
|
|
@@ -10416,19 +10068,19 @@ async function ensureNaudiodonCompiled() {
|
|
|
10416
10068
|
}
|
|
10417
10069
|
}
|
|
10418
10070
|
} catch (err) {
|
|
10419
|
-
|
|
10071
|
+
logger8.warn({ err }, "naudiodon rebuild failed, checking for PortAudio");
|
|
10420
10072
|
notificationService.warning("naudiodon \u7F16\u8BD1\u5931\u8D25", "\u6B63\u5728\u5C1D\u8BD5\u5B89\u88C5 PortAudio...", 5e3);
|
|
10421
10073
|
const installed = await installPortAudio();
|
|
10422
10074
|
if (installed.success) {
|
|
10423
|
-
const retryPath = dirname2(
|
|
10075
|
+
const retryPath = dirname2(require2.resolve("naudiodon"));
|
|
10424
10076
|
notificationService.info("\u6B63\u5728\u91CD\u65B0\u7F16\u8BD1 naudiodon...", "Ocosay", 5e3);
|
|
10425
10077
|
const retryResult = await execAsync("npm rebuild naudiodon", retryPath);
|
|
10426
10078
|
if (retryResult.error) {
|
|
10427
|
-
|
|
10079
|
+
logger8.error({ err: retryResult.error }, "naudiodon compile failed even after PortAudio install");
|
|
10428
10080
|
notificationService.error("naudiodon \u7F16\u8BD1\u5931\u8D25", "\u81EA\u52A8\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u5C1D\u8BD5\u624B\u52A8\u5B89\u88C5", 8e3);
|
|
10429
10081
|
markNaudiodonSkipped();
|
|
10430
10082
|
} else {
|
|
10431
|
-
|
|
10083
|
+
logger8.info("naudiodon compiled successfully after PortAudio install");
|
|
10432
10084
|
const loadSuccess = await verifyNaudiodonLoad();
|
|
10433
10085
|
if (loadSuccess) {
|
|
10434
10086
|
notificationService.success("naudiodon \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
|
|
@@ -10443,7 +10095,7 @@ async function ensureNaudiodonCompiled() {
|
|
|
10443
10095
|
}
|
|
10444
10096
|
}
|
|
10445
10097
|
} else {
|
|
10446
|
-
|
|
10098
|
+
logger8.error("PortAudio install failed");
|
|
10447
10099
|
notificationService.error("PortAudio \u5B89\u88C5\u5931\u8D25", "\u81EA\u52A8\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u5C1D\u8BD5\u624B\u52A8\u5B89\u88C5", 8e3);
|
|
10448
10100
|
markNaudiodonSkipped();
|
|
10449
10101
|
}
|
|
@@ -10460,87 +10112,35 @@ function isModuleInstalled(moduleName) {
|
|
|
10460
10112
|
async function verifyModuleLoad(dep) {
|
|
10461
10113
|
try {
|
|
10462
10114
|
pluginRequire(dep);
|
|
10463
|
-
|
|
10115
|
+
logger8.info(`${dep} loaded successfully`);
|
|
10464
10116
|
return true;
|
|
10465
10117
|
} catch (err) {
|
|
10466
|
-
|
|
10118
|
+
logger8.warn({ err }, `${dep} load failed`);
|
|
10467
10119
|
return false;
|
|
10468
10120
|
}
|
|
10469
10121
|
}
|
|
10470
|
-
async function tryCompileSpeaker() {
|
|
10471
|
-
const dep = "speaker";
|
|
10472
|
-
const result = { success: false, stderr: "" };
|
|
10473
|
-
if (isModuleInstalled(dep)) {
|
|
10474
|
-
if (await verifyModuleLoad(dep)) {
|
|
10475
|
-
result.success = true;
|
|
10476
|
-
return result;
|
|
10477
|
-
}
|
|
10478
|
-
}
|
|
10479
|
-
if (!isModuleInstalled(dep)) {
|
|
10480
|
-
const installResult = await execAsync("npm install speaker", opencodeNodeModules);
|
|
10481
|
-
if (installResult.error) {
|
|
10482
|
-
logger9.warn({ err: installResult.error }, "speaker install failed");
|
|
10483
|
-
}
|
|
10484
|
-
}
|
|
10485
|
-
const rebuildResult = await execAsync("npm rebuild speaker", opencodeNodeModules);
|
|
10486
|
-
result.stderr = (rebuildResult.stdout || "") + "\n" + (rebuildResult.stderr || "");
|
|
10487
|
-
if (rebuildResult.error) {
|
|
10488
|
-
result.stderr += "\n" + (rebuildResult.error.message || "");
|
|
10489
|
-
}
|
|
10490
|
-
if (await verifyModuleLoad(dep)) {
|
|
10491
|
-
result.success = true;
|
|
10492
|
-
}
|
|
10493
|
-
return result;
|
|
10494
|
-
}
|
|
10495
|
-
async function ensureSpeakerCompiledAsync() {
|
|
10496
|
-
const compileResult = await tryCompileSpeaker();
|
|
10497
|
-
if (compileResult.success) {
|
|
10498
|
-
logger9.info("speaker compiled successfully");
|
|
10499
|
-
return;
|
|
10500
|
-
}
|
|
10501
|
-
const detectResult = detectMissingDependencies(compileResult.stderr);
|
|
10502
|
-
if (detectResult.missingHeaders.length === 0) {
|
|
10503
|
-
logger9.info("speaker compile failed with unknown error");
|
|
10504
|
-
return;
|
|
10505
|
-
}
|
|
10506
|
-
logger9.info({ missingHeaders: detectResult.missingHeaders }, "detected missing headers");
|
|
10507
|
-
const platformInfo = detectPlatform();
|
|
10508
|
-
const packages = mapHeadersToPackages(detectResult.missingHeaders, platformInfo.platform);
|
|
10509
|
-
if (packages.length === 0) {
|
|
10510
|
-
logger9.info("no known packages for missing headers");
|
|
10511
|
-
return;
|
|
10512
|
-
}
|
|
10513
|
-
logger9.info({ packages }, "mapped headers to packages, installing");
|
|
10514
|
-
await installSystemPackages(packages, notificationService);
|
|
10515
|
-
const retryResult = await tryCompileSpeaker();
|
|
10516
|
-
if (retryResult.success) {
|
|
10517
|
-
logger9.info("speaker compiled successfully after installing dependencies");
|
|
10518
|
-
} else {
|
|
10519
|
-
logger9.warn("speaker compile still failed after dependency installation");
|
|
10520
|
-
}
|
|
10521
|
-
}
|
|
10522
10122
|
async function ensureSpeakerInstalledAsync() {
|
|
10523
10123
|
await ensurePlaySoundInstalled();
|
|
10524
10124
|
}
|
|
10525
10125
|
async function initAsync() {
|
|
10526
10126
|
setTimeout(async () => {
|
|
10527
|
-
await ensureSpeakerCompiledAsync();
|
|
10528
10127
|
await ensureSpeakerInstalledAsync();
|
|
10529
10128
|
}, 100);
|
|
10530
10129
|
}
|
|
10531
10130
|
async function ensurePlaySoundInstalled() {
|
|
10532
10131
|
const dep = "play-sound";
|
|
10533
10132
|
if (isModuleInstalled(dep)) {
|
|
10534
|
-
|
|
10133
|
+
logger8.info("play-sound already installed");
|
|
10535
10134
|
if (await verifyModuleLoad(dep)) {
|
|
10135
|
+
notificationService.success("play-sound \u5DF2\u5C31\u7EEA", "\u97F3\u9891\u540E\u7AEF\u6B63\u5E38", 5e3);
|
|
10536
10136
|
return;
|
|
10537
10137
|
}
|
|
10538
10138
|
}
|
|
10539
|
-
|
|
10139
|
+
logger8.info("play-sound not found, installing");
|
|
10540
10140
|
notificationService.info("\u6B63\u5728\u5B89\u88C5 play-sound...", "Ocosay \u97F3\u9891\u540E\u7AEF", 5e3);
|
|
10541
10141
|
const installResult = await execAsync("npm install play-sound", opencodeNodeModules);
|
|
10542
10142
|
if (installResult.error) {
|
|
10543
|
-
|
|
10143
|
+
logger8.warn({ err: installResult.error }, "play-sound install failed");
|
|
10544
10144
|
notificationService.warning(
|
|
10545
10145
|
"play-sound \u5B89\u88C5\u5931\u8D25",
|
|
10546
10146
|
"\u8BF7\u624B\u52A8\u8FD0\u884C: npm install play-sound",
|
|
@@ -10548,7 +10148,7 @@ async function ensurePlaySoundInstalled() {
|
|
|
10548
10148
|
);
|
|
10549
10149
|
return;
|
|
10550
10150
|
}
|
|
10551
|
-
|
|
10151
|
+
logger8.info("play-sound installed");
|
|
10552
10152
|
if (await verifyModuleLoad(dep)) {
|
|
10553
10153
|
notificationService.success("play-sound \u5B89\u88C5\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
|
|
10554
10154
|
} else {
|
|
@@ -10560,14 +10160,11 @@ async function ensurePlaySoundInstalled() {
|
|
|
10560
10160
|
}
|
|
10561
10161
|
}
|
|
10562
10162
|
async function ensureOptionalDepsInstalled() {
|
|
10563
|
-
ensureSpeakerCompiledAsync().catch((err) => {
|
|
10564
|
-
logger9.warn({ err }, "ensureSpeakerCompiledAsync failed");
|
|
10565
|
-
});
|
|
10566
10163
|
await ensurePlaySoundInstalled();
|
|
10567
10164
|
}
|
|
10568
10165
|
function execCmd2(cmd) {
|
|
10569
10166
|
try {
|
|
10570
|
-
const output =
|
|
10167
|
+
const output = execSync2(cmd, { stdio: "pipe", encoding: "utf8" });
|
|
10571
10168
|
return { success: true, output };
|
|
10572
10169
|
} catch (err) {
|
|
10573
10170
|
return { success: false, output: err.message || "" };
|
|
@@ -10576,7 +10173,7 @@ function execCmd2(cmd) {
|
|
|
10576
10173
|
function isWsl2() {
|
|
10577
10174
|
if (process.platform !== "linux") return false;
|
|
10578
10175
|
try {
|
|
10579
|
-
return
|
|
10176
|
+
return require2("fs").readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft");
|
|
10580
10177
|
} catch {
|
|
10581
10178
|
return false;
|
|
10582
10179
|
}
|
|
@@ -10613,9 +10210,9 @@ async function checkAudioEnvironmentForBackend() {
|
|
|
10613
10210
|
async function installPortAudio() {
|
|
10614
10211
|
const platform = process.platform;
|
|
10615
10212
|
const wsl = isWsl2();
|
|
10616
|
-
|
|
10213
|
+
logger8.info({ platform, wsl }, "installing PortAudio");
|
|
10617
10214
|
const runInstall = async (cmd, desc) => {
|
|
10618
|
-
|
|
10215
|
+
logger8.info(`Running: ${cmd}`);
|
|
10619
10216
|
notificationService.info(desc, "\u6B63\u5728\u5B89\u88C5...", 5e3);
|
|
10620
10217
|
const result = await execAsync(cmd);
|
|
10621
10218
|
if (result.error) {
|
|
@@ -10626,15 +10223,15 @@ async function installPortAudio() {
|
|
|
10626
10223
|
"# \u8BF7\u5728WSL\u7EC8\u7AEF\u6267\u884C\u4E00\u6B21\nsudo visudo\n# \u6DFB\u52A0\u884C\uFF1Ayour user name ALL=(ALL) NOPASSWD: ALL",
|
|
10627
10224
|
1e4
|
|
10628
10225
|
);
|
|
10629
|
-
|
|
10226
|
+
logger8.error({ err: result.error }, "\u9700\u8981 sudo \u6743\u9650 \u8BF7\u5728WSL\u7EC8\u7AEF\u6267\u884C\u4E00\u6B21sudo visudo # \u6DFB\u52A0\u884C\uFF1Ayour user name ALL=(ALL) NOPASSWD: ALL");
|
|
10630
10227
|
return false;
|
|
10631
10228
|
}
|
|
10632
10229
|
if (msg.includes("already") || msg.includes("is already")) {
|
|
10633
|
-
|
|
10230
|
+
logger8.info("already installed");
|
|
10634
10231
|
return true;
|
|
10635
10232
|
}
|
|
10636
10233
|
notificationService.error(desc + " \u5931\u8D25", msg.substring(0, 100), 8e3);
|
|
10637
|
-
|
|
10234
|
+
logger8.error({ err: result.error }, `install failed: ${desc}`);
|
|
10638
10235
|
return false;
|
|
10639
10236
|
}
|
|
10640
10237
|
return true;
|
|
@@ -10643,7 +10240,7 @@ async function installPortAudio() {
|
|
|
10643
10240
|
notificationService.info("\u68C0\u6D4B ffmpeg...", "\u97F3\u9891\u540E\u7AEF", 5e3);
|
|
10644
10241
|
const ffmpegCheck = execCmd2("which ffplay");
|
|
10645
10242
|
if (ffmpegCheck.success) {
|
|
10646
|
-
|
|
10243
|
+
logger8.info("ffmpeg already available");
|
|
10647
10244
|
notificationService.success("ffmpeg \u5C31\u7EEA", "ffplay \u53EF\u7528\u4E8E\u65E0\u58F0\u5361\u64AD\u653E", 5e3);
|
|
10648
10245
|
} else {
|
|
10649
10246
|
notificationService.info("\u5B89\u88C5 ffmpeg...", "\u97F3\u9891\u540E\u7AEF", 5e3);
|
|
@@ -10659,7 +10256,7 @@ async function installPortAudio() {
|
|
|
10659
10256
|
}
|
|
10660
10257
|
notificationService.info("\u68C0\u6D4B\u97F3\u9891\u8BBE\u5907...", "\u97F3\u9891\u540E\u7AEF", 5e3);
|
|
10661
10258
|
if (checkAlsa()) {
|
|
10662
|
-
|
|
10259
|
+
logger8.info("alsa-utils already available and working");
|
|
10663
10260
|
notificationService.success("alsa-utils \u5C31\u7EEA", "\u97F3\u9891\u540E\u7AEF\u5DF2\u53EF\u7528", 5e3);
|
|
10664
10261
|
return { success: true, message: "alsa" };
|
|
10665
10262
|
}
|
|
@@ -10850,7 +10447,7 @@ var server = (async (input, _options) => {
|
|
|
10850
10447
|
});
|
|
10851
10448
|
} catch (err) {
|
|
10852
10449
|
initError = err instanceof Error ? err : new Error(String(err));
|
|
10853
|
-
|
|
10450
|
+
logger8.error({ error: initError }, "initialization failed");
|
|
10854
10451
|
}
|
|
10855
10452
|
initAsync();
|
|
10856
10453
|
await ensureNaudiodonCompiled();
|