@mingxy/ocosay 1.1.24 → 1.1.26

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/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: createRequire2 } = __require("module");
2440
+ var { createRequire: createRequire3 } = __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 = createRequire2(context).resolve(origin);
2643
+ fixTarget2 = createRequire3(context).resolve(origin);
2644
2644
  break;
2645
2645
  } catch (err) {
2646
2646
  continue;
@@ -9901,25 +9901,383 @@ import { readFileSync as readFileSync2, existsSync as existsSync7, writeFileSync
9901
9901
  import { fileURLToPath } from "url";
9902
9902
  import { dirname as dirname2, join as join8 } from "path";
9903
9903
  import { homedir as homedir3 } from "os";
9904
+ import { exec, execSync as execSync4 } from "child_process";
9905
+ import { createRequire as createRequire2 } from "module";
9906
+
9907
+ // src/core/dependency-detector.ts
9904
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(cmd, cwd) {
9937
+ const result = execCapture(cmd, cwd);
9938
+ const missingHeaders = parseMissingHeaders(result.stderr);
9939
+ return {
9940
+ missingHeaders,
9941
+ rawOutput: result.stderr
9942
+ };
9943
+ }
9944
+
9945
+ // src/core/dependency-mapper.ts
9905
9946
  import { createRequire } from "module";
9906
- var logger8 = createModuleLogger("Plugin");
9907
9947
  var require2 = createRequire(import.meta.url);
9908
- var opencodeBinPath = execSync2("which opencode").toString().trim();
9948
+ var HEADER_TO_PACKAGE = [
9949
+ // ALSA - Linux音频库
9950
+ {
9951
+ header: "alsa/asoundlib.h",
9952
+ package: "libasound2-dev"
9953
+ },
9954
+ // PortAudio - 跨平台音频I/O库
9955
+ {
9956
+ header: "portaudio.h",
9957
+ package: "libportaudio-dev",
9958
+ packageMac: "portaudio",
9959
+ packageWin: "portaudio"
9960
+ },
9961
+ // FFmpeg相关
9962
+ {
9963
+ header: "libavcodec/avcodec.h",
9964
+ package: "libavcodec-dev",
9965
+ packageMac: "ffmpeg",
9966
+ packageWin: "ffmpeg"
9967
+ },
9968
+ {
9969
+ header: "libavformat/avformat.h",
9970
+ package: "libavformat-dev",
9971
+ packageMac: "ffmpeg",
9972
+ packageWin: "ffmpeg"
9973
+ },
9974
+ {
9975
+ header: "libavutil/avutil.h",
9976
+ package: "libavutil-dev",
9977
+ packageMac: "ffmpeg",
9978
+ packageWin: "ffmpeg"
9979
+ },
9980
+ {
9981
+ header: "libswresample/swresample.h",
9982
+ package: "libswresample-dev",
9983
+ packageMac: "ffmpeg",
9984
+ packageWin: "ffmpeg"
9985
+ },
9986
+ // OpenAL - 3D音频API
9987
+ {
9988
+ header: "AL/al.h",
9989
+ package: "libopenal-dev",
9990
+ packageMac: "openal",
9991
+ packageWin: "openal"
9992
+ },
9993
+ // SDL - 多媒体库
9994
+ {
9995
+ header: "SDL2/SDL.h",
9996
+ package: "libsdl2-dev",
9997
+ packageMac: "sdl2",
9998
+ packageWin: "sdl2"
9999
+ },
10000
+ // PulseAudio - Linux音频服务
10001
+ {
10002
+ header: "pulse/pulseaudio.h",
10003
+ package: "libpulse-dev"
10004
+ },
10005
+ // CoreAudio - macOS音频框架 (无头文件,纯框架)
10006
+ {
10007
+ header: "CoreAudio/CoreAudio.h",
10008
+ package: "\u81FA",
10009
+ // macOS系统框架,无需安装包
10010
+ packageMac: ""
10011
+ },
10012
+ // Windows特定
10013
+ {
10014
+ header: "windows.h",
10015
+ package: "",
10016
+ // Linux上不存在
10017
+ packageWin: ""
10018
+ // Windows SDK自带
10019
+ }
10020
+ ];
10021
+ function detectPlatform() {
10022
+ const platform = process.platform;
10023
+ const isWsl3 = detectWsl();
10024
+ let packageManager;
10025
+ let installCommand;
10026
+ if (platform === "linux" || isWsl3) {
10027
+ packageManager = "apt-get";
10028
+ installCommand = "sudo apt-get install -y";
10029
+ } else if (platform === "darwin") {
10030
+ packageManager = "brew";
10031
+ installCommand = "brew install";
10032
+ } else {
10033
+ packageManager = "choco";
10034
+ installCommand = "choco install -y";
10035
+ }
10036
+ return {
10037
+ platform,
10038
+ isWsl: isWsl3,
10039
+ packageManager,
10040
+ installCommand
10041
+ };
10042
+ }
10043
+ function detectWsl() {
10044
+ if (process.platform !== "linux") return false;
10045
+ try {
10046
+ return require2("fs").readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft");
10047
+ } catch {
10048
+ return false;
10049
+ }
10050
+ }
10051
+ function getPackageForPlatform(mapping, platform, isWsl3) {
10052
+ if (platform === "win32") {
10053
+ if (isWsl3) {
10054
+ return mapping.package || null;
10055
+ }
10056
+ return mapping.packageWin || mapping.package || null;
10057
+ }
10058
+ if (platform === "darwin") {
10059
+ if (mapping.packageMac === "") return null;
10060
+ return mapping.packageMac || mapping.package || null;
10061
+ }
10062
+ if (mapping.package === "") return null;
10063
+ return mapping.package || null;
10064
+ }
10065
+ function mapHeaderToPackages(header, platform) {
10066
+ const exactMatch = HEADER_TO_PACKAGE.find(
10067
+ (m) => m.header.toLowerCase() === header.toLowerCase()
10068
+ );
10069
+ if (exactMatch) {
10070
+ const pkg = getPackageForPlatform(exactMatch, platform, detectWsl());
10071
+ if (pkg && pkg.length > 0 && pkg !== "\u81FA") {
10072
+ return [pkg];
10073
+ }
10074
+ return [];
10075
+ }
10076
+ const headerBasename = header.split("/").pop()?.toLowerCase() || "";
10077
+ const fuzzyMatch = HEADER_TO_PACKAGE.find((m) => {
10078
+ const mappingBasename = m.header.split("/").pop()?.toLowerCase() || "";
10079
+ return mappingBasename === headerBasename;
10080
+ });
10081
+ if (fuzzyMatch) {
10082
+ const pkg = getPackageForPlatform(fuzzyMatch, platform, detectWsl());
10083
+ if (pkg && pkg.length > 0 && pkg !== "\u81FA") {
10084
+ return [pkg];
10085
+ }
10086
+ return [];
10087
+ }
10088
+ return [];
10089
+ }
10090
+ function mapHeadersToPackages(headers, platform) {
10091
+ const packageSet = /* @__PURE__ */ new Set();
10092
+ for (const header of headers) {
10093
+ const packages = mapHeaderToPackages(header, platform);
10094
+ for (const pkg of packages) {
10095
+ if (pkg.length > 0) {
10096
+ packageSet.add(pkg);
10097
+ }
10098
+ }
10099
+ }
10100
+ return Array.from(packageSet);
10101
+ }
10102
+
10103
+ // src/core/dependency-installer.ts
10104
+ import { execSync as execSync3 } from "child_process";
10105
+ var logger8 = createModuleLogger("DependencyInstaller");
10106
+ function getInstallCommandPrefix() {
10107
+ const { platform, isWsl: isWsl3 } = detectPlatform();
10108
+ if (platform === "linux" || isWsl3) {
10109
+ return "sudo apt-get install -y";
10110
+ } else if (platform === "darwin") {
10111
+ return "brew install";
10112
+ }
10113
+ return "choco install -y";
10114
+ }
10115
+ function getCheckCommand(packageName) {
10116
+ const { platform, isWsl: isWsl3 } = detectPlatform();
10117
+ if (platform === "linux" || isWsl3) {
10118
+ return `dpkg -s ${packageName} 2>/dev/null | grep -q "Status: install ok installed"`;
10119
+ } else if (platform === "darwin") {
10120
+ return `brew list ${packageName} &>/dev/null`;
10121
+ }
10122
+ return `choco list --local-only ${packageName} &>/dev/null`;
10123
+ }
10124
+ function silentLog(level2, message, extra) {
10125
+ switch (level2) {
10126
+ case "info":
10127
+ logger8.info(extra || {}, message);
10128
+ break;
10129
+ case "warn":
10130
+ logger8.warn(extra || {}, message);
10131
+ break;
10132
+ case "error":
10133
+ logger8.error(extra || {}, message);
10134
+ break;
10135
+ }
10136
+ }
10137
+ function canSudoWithoutPassword() {
10138
+ try {
10139
+ execSync3("sudo -n true 2>&1", { stdio: "pipe" });
10140
+ return true;
10141
+ } catch {
10142
+ return false;
10143
+ }
10144
+ }
10145
+ async function installSystemPackages(packages, notifSvc) {
10146
+ const result = {
10147
+ success: false,
10148
+ installedPackages: [],
10149
+ failedPackages: []
10150
+ };
10151
+ const validPackages = packages.filter((p) => p && p.length > 0 && p !== "\u81FA");
10152
+ if (validPackages.length === 0) {
10153
+ silentLog("info", "\u6CA1\u6709\u9700\u8981\u5B89\u88C5\u7684\u5305");
10154
+ return { ...result, success: true };
10155
+ }
10156
+ silentLog("info", `\u5F00\u59CB\u5B89\u88C5\u7CFB\u7EDF\u4F9D\u8D56: ${validPackages.join(", ")}`, { packages: validPackages });
10157
+ notifSvc?.info("\u6B63\u5728\u5B89\u88C5\u7CFB\u7EDF\u4F9D\u8D56...", "Ocosay", 3e3);
10158
+ const installCommand = getInstallCommandPrefix();
10159
+ const fullCommand = `${installCommand} ${validPackages.join(" ")}`;
10160
+ silentLog("info", `\u6267\u884C\u5B89\u88C5\u547D\u4EE4: ${fullCommand}`);
10161
+ if (!canSudoWithoutPassword()) {
10162
+ const msg = "\u9700\u8981 sudo \u6743\u9650\uFF0C\u8BF7\u786E\u4FDD\u5DF2\u914D\u7F6E NOPASSWD";
10163
+ result.error = msg;
10164
+ 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);
10165
+ silentLog("error", msg);
10166
+ return result;
10167
+ }
10168
+ try {
10169
+ const { platform, isWsl: isWsl3 } = detectPlatform();
10170
+ if (platform === "linux" || isWsl3) {
10171
+ notifSvc?.info("\u6B63\u5728\u66F4\u65B0\u5305\u5217\u8868...", "Ocosay", 3e3);
10172
+ silentLog("info", "\u66F4\u65B0 apt \u5305\u5217\u8868");
10173
+ try {
10174
+ execSync3("sudo apt-get update", {
10175
+ timeout: 12e4,
10176
+ encoding: "utf8"
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
+ });
10188
+ silentLog("info", `\u5B89\u88C5\u8F93\u51FA: ${output.substring(0, 500)}`);
10189
+ const verifiedPackages = [];
10190
+ const failedPackages = [];
10191
+ for (const pkg of validPackages) {
10192
+ const isInstalled = await verifyInstallation([pkg]);
10193
+ if (isInstalled) {
10194
+ verifiedPackages.push(pkg);
10195
+ silentLog("info", `\u5305 ${pkg} \u5B89\u88C5\u9A8C\u8BC1\u6210\u529F`);
10196
+ } else {
10197
+ failedPackages.push(pkg);
10198
+ silentLog("warn", `\u5305 ${pkg} \u5B89\u88C5\u9A8C\u8BC1\u5931\u8D25`);
10199
+ }
10200
+ }
10201
+ result.installedPackages = verifiedPackages;
10202
+ result.failedPackages = failedPackages;
10203
+ result.success = failedPackages.length === 0;
10204
+ if (result.success) {
10205
+ notifSvc?.success("\u7CFB\u7EDF\u4F9D\u8D56\u5B89\u88C5\u6210\u529F", verifiedPackages.join(", "), 5e3);
10206
+ silentLog("info", `\u6240\u6709\u5305\u5B89\u88C5\u6210\u529F: ${verifiedPackages.join(", ")}`);
10207
+ } else {
10208
+ const errorMsg = failedPackages.length > 0 ? `\u4EE5\u4E0B\u5305\u5B89\u88C5\u5931\u8D25: ${failedPackages.join(", ")}` : "\u90E8\u5206\u5305\u5B89\u88C5\u5931\u8D25";
10209
+ result.error = errorMsg;
10210
+ notifSvc?.warning("\u90E8\u5206\u4F9D\u8D56\u5B89\u88C5\u5931\u8D25", errorMsg, 8e3);
10211
+ silentLog("warn", errorMsg);
10212
+ }
10213
+ } catch (err) {
10214
+ const errorMessage = err instanceof Error ? err.message : String(err);
10215
+ silentLog("error", `\u5B89\u88C5\u547D\u4EE4\u6267\u884C\u5931\u8D25: ${errorMessage}`, { error: errorMessage });
10216
+ if (errorMessage.includes("sudo") || errorMessage.toLowerCase().includes("password")) {
10217
+ result.error = "\u9700\u8981 sudo \u6743\u9650\uFF0C\u8BF7\u786E\u4FDD\u5DF2\u914D\u7F6E NOPASSWD";
10218
+ notifSvc?.error("\u9700\u8981 sudo \u6743\u9650", "\u8BF7\u5728\u7EC8\u7AEF\u6267\u884C: sudo visudo", 1e4);
10219
+ } else if (errorMessage.includes("already") || errorMessage.includes("is already")) {
10220
+ result.success = true;
10221
+ result.installedPackages = validPackages;
10222
+ notifSvc?.success("\u4F9D\u8D56\u5DF2\u5B58\u5728", "\u65E0\u9700\u91CD\u65B0\u5B89\u88C5", 3e3);
10223
+ } else {
10224
+ result.error = errorMessage;
10225
+ notifSvc?.error("\u4F9D\u8D56\u5B89\u88C5\u5931\u8D25", errorMessage.substring(0, 200), 8e3);
10226
+ }
10227
+ }
10228
+ return result;
10229
+ }
10230
+ async function verifyInstallation(packages) {
10231
+ if (packages.length === 0) {
10232
+ return true;
10233
+ }
10234
+ const validPackages = packages.filter((p) => p && p.length > 0 && p !== "\u81FA");
10235
+ if (validPackages.length === 0) {
10236
+ return true;
10237
+ }
10238
+ silentLog("info", `\u9A8C\u8BC1\u5305\u5B89\u88C5\u72B6\u6001: ${validPackages.join(", ")}`);
10239
+ try {
10240
+ for (const pkg of validPackages) {
10241
+ const checkCmd = getCheckCommand(pkg);
10242
+ const { success } = execCapture(checkCmd);
10243
+ if (!success) {
10244
+ silentLog("warn", `\u5305 ${pkg} \u9A8C\u8BC1\u5931\u8D25`);
10245
+ return false;
10246
+ }
10247
+ }
10248
+ silentLog("info", `\u6240\u6709\u5305\u9A8C\u8BC1\u901A\u8FC7: ${validPackages.join(", ")}`);
10249
+ return true;
10250
+ } catch (err) {
10251
+ silentLog("error", `\u9A8C\u8BC1\u8FC7\u7A0B\u5F02\u5E38: ${String(err)}`);
10252
+ return false;
10253
+ }
10254
+ }
10255
+
10256
+ // src/plugin.ts
10257
+ function execAsync(cmd, cwd) {
10258
+ return new Promise((resolve) => {
10259
+ exec(cmd, { cwd, encoding: "utf8" }, (error, stdout, stderr) => {
10260
+ resolve({ stdout: stdout || "", stderr: stderr || "", error: error || void 0 });
10261
+ });
10262
+ });
10263
+ }
10264
+ var logger9 = createModuleLogger("Plugin");
10265
+ var require3 = createRequire2(import.meta.url);
10266
+ var opencodeBinPath = execSync4("which opencode").toString().trim();
9909
10267
  var opencodeRoot = dirname2(dirname2(opencodeBinPath));
9910
10268
  var opencodeNodeModules = join8(opencodeRoot, "node_modules");
9911
- var pluginRequire = createRequire(join8(opencodeNodeModules, "package.json"));
10269
+ var pluginRequire = createRequire2(join8(opencodeNodeModules, "package.json"));
9912
10270
  function getSkipFilePath() {
9913
10271
  return join8(homedir3(), ".config", "opencode", ".naudiodon_skip");
9914
10272
  }
9915
10273
  function shouldSkipNaudiodon() {
9916
10274
  return existsSync7(getSkipFilePath());
9917
10275
  }
9918
- function markNaudiodonSkipped() {
10276
+ async function markNaudiodonSkipped() {
9919
10277
  try {
9920
10278
  const dir = join8(homedir3(), ".config", "opencode");
9921
10279
  if (!existsSync7(dir)) {
9922
- execSync2("mkdir -p", { cwd: dir });
10280
+ await execAsync("mkdir -p", dir);
9923
10281
  }
9924
10282
  writeFileSync6(getSkipFilePath(), Date.now().toString(), "utf-8");
9925
10283
  } catch {
@@ -9927,70 +10285,61 @@ function markNaudiodonSkipped() {
9927
10285
  }
9928
10286
  async function verifyNaudiodonLoad() {
9929
10287
  try {
9930
- require2("naudiodon");
9931
- logger8.info("naudiodon loaded successfully");
10288
+ require3("naudiodon");
10289
+ logger9.info("naudiodon loaded successfully");
9932
10290
  return true;
9933
10291
  } catch (err) {
9934
- logger8.warn({ err }, "naudiodon load failed after rebuild");
10292
+ logger9.warn({ err }, "naudiodon load failed after rebuild");
9935
10293
  return false;
9936
10294
  }
9937
10295
  }
9938
10296
  async function rebuildNaudiodonDependency(dep) {
9939
- const naudiodonPath = dirname2(require2.resolve("naudiodon"));
10297
+ const naudiodonPath = dirname2(require3.resolve("naudiodon"));
9940
10298
  try {
9941
10299
  notificationService.info(`\u6B63\u5728\u7F16\u8BD1 ${dep}...`, "Ocosay \u4F9D\u8D56", 4e3);
9942
- execSync2(`npm rebuild ${dep}`, {
9943
- cwd: naudiodonPath,
9944
- stdio: "inherit"
9945
- });
9946
- logger8.info(`${dep} rebuilt successfully`);
10300
+ await execAsync(`npm rebuild ${dep}`, naudiodonPath);
10301
+ logger9.info(`${dep} rebuilt successfully`);
9947
10302
  return true;
9948
10303
  } catch (err) {
9949
- logger8.warn({ err }, `${dep} rebuild failed`);
10304
+ logger9.warn({ err }, `${dep} rebuild failed`);
9950
10305
  return false;
9951
10306
  }
9952
10307
  }
9953
10308
  async function fixNaudiodonDependencies(maxRetries = 5) {
9954
- const naudiodonPath = dirname2(require2.resolve("naudiodon"));
10309
+ const naudiodonPath = dirname2(require3.resolve("naudiodon"));
9955
10310
  const criticalDeps = ["segfault-handler", "bindings", "node-pre-gyp"];
9956
10311
  for (let attempt = 0; attempt < maxRetries; attempt++) {
9957
10312
  if (await verifyNaudiodonLoad()) {
9958
10313
  return true;
9959
10314
  }
9960
- logger8.info({ attempt }, "naudiodon not loadable, trying dependency rebuild");
10315
+ logger9.info({ attempt }, "naudiodon not loadable, trying dependency rebuild");
9961
10316
  notificationService.info(`\u6B63\u5728\u68C0\u67E5\u4F9D\u8D56 (${attempt + 1}/${maxRetries})...`, "Ocosay", 3e3);
9962
10317
  let anySuccess = false;
9963
10318
  for (const dep of criticalDeps) {
9964
10319
  try {
9965
- require2.resolve(dep, { paths: [naudiodonPath] });
10320
+ require3.resolve(dep, { paths: [naudiodonPath] });
9966
10321
  if (await rebuildNaudiodonDependency(dep)) {
9967
10322
  anySuccess = true;
9968
10323
  }
9969
10324
  } catch {
9970
10325
  try {
9971
10326
  notificationService.info(`\u6B63\u5728\u5B89\u88C5 ${dep}...`, "Ocosay", 4e3);
9972
- execSync2(`npm install ${dep}`, {
9973
- cwd: naudiodonPath,
9974
- stdio: "inherit"
9975
- });
9976
- logger8.info(`${dep} installed successfully`);
10327
+ await execAsync(`npm install ${dep}`, naudiodonPath);
10328
+ logger9.info(`${dep} installed successfully`);
9977
10329
  anySuccess = true;
9978
10330
  } catch (installErr) {
9979
- logger8.warn({ err: installErr }, `${dep} install failed`);
10331
+ logger9.warn({ err: installErr }, `${dep} install failed`);
9980
10332
  }
9981
10333
  }
9982
10334
  }
9983
10335
  if (!anySuccess || !await verifyNaudiodonLoad()) {
9984
10336
  try {
9985
10337
  notificationService.info("\u6B63\u5728\u91CD\u65B0\u7F16\u8BD1 naudiodon...", "Ocosay", 4e3);
9986
- execSync2("npm rebuild naudiodon", {
9987
- cwd: naudiodonPath,
9988
- stdio: "inherit"
9989
- });
9990
- logger8.info("naudiodon rebuilt");
10338
+ await execAsync("npm rebuild naudiodon", naudiodonPath);
10339
+ logger9.info("naudiodon rebuilt");
9991
10340
  anySuccess = true;
9992
10341
  } catch (err) {
9993
- logger8.warn({ err }, "naudiodon rebuild failed");
10342
+ logger9.warn({ err }, "naudiodon rebuild failed");
9994
10343
  }
9995
10344
  }
9996
10345
  if (await verifyNaudiodonLoad()) {
@@ -10004,25 +10353,22 @@ async function fixNaudiodonDependencies(maxRetries = 5) {
10004
10353
  }
10005
10354
  async function ensureNaudiodonCompiled() {
10006
10355
  if (shouldSkipNaudiodon()) {
10007
- logger8.info("naudiodon skipped previously");
10356
+ logger9.info("naudiodon skipped previously");
10008
10357
  return;
10009
10358
  }
10010
10359
  try {
10011
- require2("naudiodon");
10012
- logger8.info("naudiodon already compiled");
10360
+ require3("naudiodon");
10361
+ logger9.info("naudiodon already compiled");
10013
10362
  return;
10014
10363
  } catch {
10015
- logger8.info("naudiodon not compiled, will attempt to compile");
10364
+ logger9.info("naudiodon not compiled, will attempt to compile");
10016
10365
  notificationService.info("\u6B63\u5728\u7F16\u8BD1 naudiodon...", "Ocosay \u97F3\u9891\u540E\u7AEF", 5e3);
10017
10366
  }
10018
10367
  try {
10019
- const naudiodonPath = dirname2(require2.resolve("naudiodon"));
10020
- logger8.info({ naudiodonPath }, "found naudiodon, rebuilding");
10021
- execSync2("npm rebuild naudiodon", {
10022
- cwd: naudiodonPath,
10023
- stdio: "inherit"
10024
- });
10025
- logger8.info("naudiodon compiled, verifying...");
10368
+ const naudiodonPath = dirname2(require3.resolve("naudiodon"));
10369
+ logger9.info({ naudiodonPath }, "found naudiodon, rebuilding");
10370
+ await execAsync("npm rebuild naudiodon", naudiodonPath);
10371
+ logger9.info("naudiodon compiled, verifying...");
10026
10372
  const loadSuccess = await verifyNaudiodonLoad();
10027
10373
  if (loadSuccess) {
10028
10374
  notificationService.success("naudiodon \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
@@ -10036,18 +10382,15 @@ async function ensureNaudiodonCompiled() {
10036
10382
  }
10037
10383
  }
10038
10384
  } catch (err) {
10039
- logger8.warn({ err }, "naudiodon rebuild failed, checking for PortAudio");
10385
+ logger9.warn({ err }, "naudiodon rebuild failed, checking for PortAudio");
10040
10386
  notificationService.warning("naudiodon \u7F16\u8BD1\u5931\u8D25", "\u6B63\u5728\u5C1D\u8BD5\u5B89\u88C5 PortAudio...", 5e3);
10041
10387
  const installed = await installPortAudio();
10042
10388
  if (installed.success) {
10043
10389
  try {
10044
- const naudiodonPath = dirname2(require2.resolve("naudiodon"));
10390
+ const naudiodonPath = dirname2(require3.resolve("naudiodon"));
10045
10391
  notificationService.info("\u6B63\u5728\u91CD\u65B0\u7F16\u8BD1 naudiodon...", "Ocosay", 5e3);
10046
- execSync2("npm rebuild naudiodon", {
10047
- cwd: naudiodonPath,
10048
- stdio: "inherit"
10049
- });
10050
- logger8.info("naudiodon compiled successfully after PortAudio install");
10392
+ await execAsync("npm rebuild naudiodon", naudiodonPath);
10393
+ logger9.info("naudiodon compiled successfully after PortAudio install");
10051
10394
  const loadSuccess = await verifyNaudiodonLoad();
10052
10395
  if (loadSuccess) {
10053
10396
  notificationService.success("naudiodon \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
@@ -10061,12 +10404,12 @@ async function ensureNaudiodonCompiled() {
10061
10404
  }
10062
10405
  }
10063
10406
  } catch (retryErr) {
10064
- logger8.error({ err: retryErr }, "naudiodon compile failed even after PortAudio install");
10407
+ logger9.error({ err: retryErr }, "naudiodon compile failed even after PortAudio install");
10065
10408
  notificationService.error("naudiodon \u7F16\u8BD1\u5931\u8D25", "\u81EA\u52A8\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u5C1D\u8BD5\u624B\u52A8\u5B89\u88C5", 8e3);
10066
10409
  markNaudiodonSkipped();
10067
10410
  }
10068
10411
  } else {
10069
- logger8.error("PortAudio install failed");
10412
+ logger9.error("PortAudio install failed");
10070
10413
  notificationService.error("PortAudio \u5B89\u88C5\u5931\u8D25", "\u81EA\u52A8\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u5C1D\u8BD5\u624B\u52A8\u5B89\u88C5", 8e3);
10071
10414
  markNaudiodonSkipped();
10072
10415
  }
@@ -10083,46 +10426,101 @@ function isModuleInstalled(moduleName) {
10083
10426
  async function verifyModuleLoad(dep) {
10084
10427
  try {
10085
10428
  pluginRequire(dep);
10086
- logger8.info(`${dep} loaded successfully`);
10429
+ logger9.info(`${dep} loaded successfully`);
10087
10430
  return true;
10088
10431
  } catch (err) {
10089
- logger8.warn({ err }, `${dep} load failed`);
10432
+ logger9.warn({ err }, `${dep} load failed`);
10090
10433
  return false;
10091
10434
  }
10092
10435
  }
10436
+ async function tryCompileSpeaker() {
10437
+ const dep = "speaker";
10438
+ const result = { success: false, stderr: "" };
10439
+ if (isModuleInstalled(dep)) {
10440
+ if (await verifyModuleLoad(dep)) {
10441
+ result.success = true;
10442
+ return result;
10443
+ }
10444
+ }
10445
+ if (!isModuleInstalled(dep)) {
10446
+ try {
10447
+ await execAsync("npm install speaker", opencodeNodeModules);
10448
+ } catch {
10449
+ }
10450
+ }
10451
+ try {
10452
+ await execAsync("npm rebuild speaker", opencodeNodeModules);
10453
+ if (await verifyModuleLoad(dep)) {
10454
+ result.success = true;
10455
+ }
10456
+ } catch (err) {
10457
+ result.stderr = err.stderr?.toString() || err.message || "";
10458
+ }
10459
+ return result;
10460
+ }
10461
+ async function ensureSpeakerCompiledAsync() {
10462
+ const compileResult = await tryCompileSpeaker();
10463
+ if (compileResult.success) {
10464
+ logger9.info("speaker compiled successfully");
10465
+ return;
10466
+ }
10467
+ const detectResult = detectMissingDependencies(compileResult.stderr);
10468
+ if (detectResult.missingHeaders.length === 0) {
10469
+ logger9.info("speaker compile failed with unknown error");
10470
+ return;
10471
+ }
10472
+ logger9.info({ missingHeaders: detectResult.missingHeaders }, "detected missing headers");
10473
+ const platformInfo = detectPlatform();
10474
+ const packages = mapHeadersToPackages(detectResult.missingHeaders, platformInfo.platform);
10475
+ if (packages.length === 0) {
10476
+ logger9.info("no known packages for missing headers");
10477
+ return;
10478
+ }
10479
+ logger9.info({ packages }, "mapped headers to packages, installing");
10480
+ await installSystemPackages(packages, notificationService);
10481
+ const retryResult = await tryCompileSpeaker();
10482
+ if (retryResult.success) {
10483
+ logger9.info("speaker compiled successfully after installing dependencies");
10484
+ } else {
10485
+ logger9.warn("speaker compile still failed after dependency installation");
10486
+ }
10487
+ }
10488
+ async function ensureSpeakerInstalledAsync() {
10489
+ await ensurePlaySoundInstalled();
10490
+ }
10491
+ async function initAsync() {
10492
+ setTimeout(async () => {
10493
+ await ensureSpeakerCompiledAsync();
10494
+ await ensureSpeakerInstalledAsync();
10495
+ }, 100);
10496
+ }
10093
10497
  async function ensureSpeakerCompiled(maxRetries = 5) {
10094
10498
  const dep = "speaker";
10095
10499
  if (isModuleInstalled(dep)) {
10096
- logger8.info("speaker already installed");
10500
+ logger9.info("speaker already installed");
10097
10501
  if (await verifyModuleLoad(dep)) {
10098
10502
  return;
10099
10503
  }
10100
- logger8.info("speaker installed but not loadable, rebuilding");
10504
+ logger9.info("speaker installed but not loadable, rebuilding");
10101
10505
  notificationService.info("\u6B63\u5728\u7F16\u8BD1 speaker...", "Ocosay \u97F3\u9891\u540E\u7AEF", 5e3);
10102
10506
  try {
10103
- execSync2("npm rebuild speaker", {
10104
- cwd: opencodeNodeModules,
10105
- stdio: "inherit"
10106
- });
10107
- logger8.info("speaker rebuilt");
10507
+ await execAsync("npm rebuild speaker", opencodeNodeModules);
10508
+ logger9.info("speaker rebuilt");
10108
10509
  } catch (err) {
10109
- logger8.warn({ err }, "speaker rebuild failed");
10510
+ logger9.warn({ err }, "speaker rebuild failed");
10110
10511
  }
10111
10512
  if (await verifyModuleLoad(dep)) {
10112
10513
  notificationService.success("speaker \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
10113
10514
  return;
10114
10515
  }
10115
10516
  } else {
10116
- logger8.info("speaker not found, installing");
10517
+ logger9.info("speaker not found, installing");
10117
10518
  notificationService.info("\u6B63\u5728\u5B89\u88C5 speaker...", "Ocosay \u97F3\u9891\u540E\u7AEF", 5e3);
10118
10519
  try {
10119
- execSync2("npm install speaker", {
10120
- cwd: opencodeNodeModules,
10121
- stdio: "inherit"
10122
- });
10123
- logger8.info("speaker installed");
10520
+ await execAsync("npm install speaker", opencodeNodeModules);
10521
+ logger9.info("speaker installed");
10124
10522
  } catch (err) {
10125
- logger8.warn({ err }, "speaker install failed");
10523
+ logger9.warn({ err }, "speaker install failed");
10126
10524
  }
10127
10525
  }
10128
10526
  for (let attempt = 0; attempt < maxRetries; attempt++) {
@@ -10130,16 +10528,13 @@ async function ensureSpeakerCompiled(maxRetries = 5) {
10130
10528
  notificationService.success("speaker \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
10131
10529
  return;
10132
10530
  }
10133
- logger8.info({ attempt, dep }, "speaker not loadable, trying rebuild");
10531
+ logger9.info({ attempt, dep }, "speaker not loadable, trying rebuild");
10134
10532
  notificationService.info(`\u6B63\u5728\u91CD\u65B0\u7F16\u8BD1 speaker (${attempt + 1}/${maxRetries})...`, "Ocosay", 3e3);
10135
10533
  try {
10136
- execSync2("npm rebuild speaker", {
10137
- cwd: opencodeNodeModules,
10138
- stdio: "inherit"
10139
- });
10140
- logger8.info("speaker rebuilt");
10534
+ await execAsync("npm rebuild speaker", opencodeNodeModules);
10535
+ logger9.info("speaker rebuilt");
10141
10536
  } catch (err) {
10142
- logger8.warn({ err }, "speaker rebuild failed");
10537
+ logger9.warn({ err }, "speaker rebuild failed");
10143
10538
  }
10144
10539
  if (await verifyModuleLoad(dep)) {
10145
10540
  notificationService.success("speaker \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA", 5e3);
@@ -10149,27 +10544,24 @@ async function ensureSpeakerCompiled(maxRetries = 5) {
10149
10544
  await new Promise((resolve) => setTimeout(resolve, 1e3));
10150
10545
  }
10151
10546
  }
10152
- logger8.error({ dep }, "speaker could not be compiled");
10547
+ logger9.error({ dep }, "speaker could not be compiled");
10153
10548
  notificationService.error("speaker \u7F16\u8BD1\u5931\u8D25", "\u8BF7\u624B\u52A8\u8FD0\u884C: npm install speaker && npm rebuild speaker", 8e3);
10154
10549
  }
10155
10550
  async function ensurePlaySoundInstalled() {
10156
10551
  const dep = "play-sound";
10157
10552
  if (isModuleInstalled(dep)) {
10158
- logger8.info("play-sound already installed");
10553
+ logger9.info("play-sound already installed");
10159
10554
  if (await verifyModuleLoad(dep)) {
10160
10555
  return;
10161
10556
  }
10162
10557
  }
10163
- logger8.info("play-sound not found, installing");
10558
+ logger9.info("play-sound not found, installing");
10164
10559
  notificationService.info("\u6B63\u5728\u5B89\u88C5 play-sound...", "Ocosay \u97F3\u9891\u540E\u7AEF", 5e3);
10165
10560
  try {
10166
- execSync2("npm install play-sound", {
10167
- cwd: opencodeNodeModules,
10168
- stdio: "inherit"
10169
- });
10170
- logger8.info("play-sound installed");
10561
+ await execAsync("npm install play-sound", opencodeNodeModules);
10562
+ logger9.info("play-sound installed");
10171
10563
  } catch (err) {
10172
- logger8.warn({ err }, "play-sound install failed");
10564
+ logger9.warn({ err }, "play-sound install failed");
10173
10565
  notificationService.warning(
10174
10566
  "play-sound \u5B89\u88C5\u5931\u8D25",
10175
10567
  "\u8BF7\u624B\u52A8\u8FD0\u884C: npm install play-sound",
@@ -10193,7 +10585,7 @@ async function ensureOptionalDepsInstalled() {
10193
10585
  }
10194
10586
  function execCmd2(cmd) {
10195
10587
  try {
10196
- const output = execSync2(cmd, { stdio: "pipe", encoding: "utf8" });
10588
+ const output = execSync4(cmd, { stdio: "pipe", encoding: "utf8" });
10197
10589
  return { success: true, output };
10198
10590
  } catch (err) {
10199
10591
  return { success: false, output: err.message || "" };
@@ -10202,7 +10594,7 @@ function execCmd2(cmd) {
10202
10594
  function isWsl2() {
10203
10595
  if (process.platform !== "linux") return false;
10204
10596
  try {
10205
- return require2("fs").readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft");
10597
+ return require3("fs").readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft");
10206
10598
  } catch {
10207
10599
  return false;
10208
10600
  }
@@ -10239,12 +10631,12 @@ async function checkAudioEnvironmentForBackend() {
10239
10631
  async function installPortAudio() {
10240
10632
  const platform = process.platform;
10241
10633
  const wsl = isWsl2();
10242
- logger8.info({ platform, wsl }, "installing PortAudio");
10634
+ logger9.info({ platform, wsl }, "installing PortAudio");
10243
10635
  const runInstall = async (cmd, desc) => {
10244
- logger8.info(`Running: ${cmd}`);
10636
+ logger9.info(`Running: ${cmd}`);
10245
10637
  notificationService.info(desc, "\u6B63\u5728\u5B89\u88C5...", 5e3);
10246
10638
  try {
10247
- execSync2(cmd, { stdio: "inherit" });
10639
+ await execAsync(cmd);
10248
10640
  return true;
10249
10641
  } catch (err) {
10250
10642
  const msg = err.message || "";
@@ -10254,15 +10646,15 @@ async function installPortAudio() {
10254
10646
  "# \u8BF7\u5728WSL\u7EC8\u7AEF\u6267\u884C\u4E00\u6B21\nsudo visudo\n# \u6DFB\u52A0\u884C\uFF1Ayour user name ALL=(ALL) NOPASSWD: ALL",
10255
10647
  1e4
10256
10648
  );
10257
- logger8.error({ err }, "\u9700\u8981 sudo \u6743\u9650 \u8BF7\u5728WSL\u7EC8\u7AEF\u6267\u884C\u4E00\u6B21sudo visudo # \u6DFB\u52A0\u884C\uFF1Ayour user name ALL=(ALL) NOPASSWD: ALL");
10649
+ logger9.error({ err }, "\u9700\u8981 sudo \u6743\u9650 \u8BF7\u5728WSL\u7EC8\u7AEF\u6267\u884C\u4E00\u6B21sudo visudo # \u6DFB\u52A0\u884C\uFF1Ayour user name ALL=(ALL) NOPASSWD: ALL");
10258
10650
  return false;
10259
10651
  }
10260
10652
  if (msg.includes("already") || msg.includes("is already")) {
10261
- logger8.info("already installed");
10653
+ logger9.info("already installed");
10262
10654
  return true;
10263
10655
  }
10264
10656
  notificationService.error(desc + " \u5931\u8D25", msg.substring(0, 100), 8e3);
10265
- logger8.error({ err }, `install failed: ${desc}`);
10657
+ logger9.error({ err }, `install failed: ${desc}`);
10266
10658
  return false;
10267
10659
  }
10268
10660
  };
@@ -10270,7 +10662,7 @@ async function installPortAudio() {
10270
10662
  notificationService.info("\u68C0\u6D4B ffmpeg...", "\u97F3\u9891\u540E\u7AEF", 5e3);
10271
10663
  const ffmpegCheck = execCmd2("which ffplay");
10272
10664
  if (ffmpegCheck.success) {
10273
- logger8.info("ffmpeg already available");
10665
+ logger9.info("ffmpeg already available");
10274
10666
  notificationService.success("ffmpeg \u5C31\u7EEA", "ffplay \u53EF\u7528\u4E8E\u65E0\u58F0\u5361\u64AD\u653E", 5e3);
10275
10667
  } else {
10276
10668
  notificationService.info("\u5B89\u88C5 ffmpeg...", "\u97F3\u9891\u540E\u7AEF", 5e3);
@@ -10286,7 +10678,7 @@ async function installPortAudio() {
10286
10678
  }
10287
10679
  notificationService.info("\u68C0\u6D4B\u97F3\u9891\u8BBE\u5907...", "\u97F3\u9891\u540E\u7AEF", 5e3);
10288
10680
  if (checkAlsa()) {
10289
- logger8.info("alsa-utils already available and working");
10681
+ logger9.info("alsa-utils already available and working");
10290
10682
  notificationService.success("alsa-utils \u5C31\u7EEA", "\u97F3\u9891\u540E\u7AEF\u5DF2\u53EF\u7528", 5e3);
10291
10683
  return { success: true, message: "alsa" };
10292
10684
  }
@@ -10477,8 +10869,9 @@ var server = (async (input, _options) => {
10477
10869
  });
10478
10870
  } catch (err) {
10479
10871
  initError = err instanceof Error ? err : new Error(String(err));
10480
- logger8.error({ error: initError }, "initialization failed");
10872
+ logger9.error({ error: initError }, "initialization failed");
10481
10873
  }
10874
+ initAsync();
10482
10875
  await ensureNaudiodonCompiled();
10483
10876
  await ensureOptionalDepsInstalled();
10484
10877
  await checkAudioEnvironmentForBackend();