@yoooclaw/phone-notifications 1.11.4-beta.3 → 1.11.4-beta.4

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/index.cjs CHANGED
@@ -579,20 +579,20 @@ function selectModelByMemory(availableGB, isAppleSilicon = false) {
579
579
  return "tiny";
580
580
  }
581
581
  function resolveModelsDir(dataDir) {
582
- const dir = (0, import_node_path21.join)(dataDir, WHISPER_MODELS_DIR);
583
- (0, import_node_fs25.mkdirSync)(dir, { recursive: true });
582
+ const dir = (0, import_node_path20.join)(dataDir, WHISPER_MODELS_DIR);
583
+ (0, import_node_fs24.mkdirSync)(dir, { recursive: true });
584
584
  return dir;
585
585
  }
586
586
  function resolveBinDir(dataDir) {
587
- const dir = (0, import_node_path21.join)(dataDir, WHISPER_BIN_DIR);
588
- (0, import_node_fs25.mkdirSync)(dir, { recursive: true });
587
+ const dir = (0, import_node_path20.join)(dataDir, WHISPER_BIN_DIR);
588
+ (0, import_node_fs24.mkdirSync)(dir, { recursive: true });
589
589
  return dir;
590
590
  }
591
591
  function isModelDownloaded(modelsDir, modelSize) {
592
- const modelPath = (0, import_node_path21.join)(modelsDir, MODEL_FILENAMES[modelSize]);
593
- if (!(0, import_node_fs25.existsSync)(modelPath)) return false;
592
+ const modelPath = (0, import_node_path20.join)(modelsDir, MODEL_FILENAMES[modelSize]);
593
+ if (!(0, import_node_fs24.existsSync)(modelPath)) return false;
594
594
  try {
595
- const stat = (0, import_node_fs25.statSync)(modelPath);
595
+ const stat = (0, import_node_fs24.statSync)(modelPath);
596
596
  const expectedSize = MODEL_DISK_SIZES[modelSize];
597
597
  return stat.size >= expectedSize * 0.8;
598
598
  } catch {
@@ -601,7 +601,7 @@ function isModelDownloaded(modelsDir, modelSize) {
601
601
  }
602
602
  async function downloadModel(modelsDir, modelSize, logger, modelSource, mirrorUrl) {
603
603
  const filename = MODEL_FILENAMES[modelSize];
604
- const modelPath = (0, import_node_path21.join)(modelsDir, filename);
604
+ const modelPath = (0, import_node_path20.join)(modelsDir, filename);
605
605
  if (isModelDownloaded(modelsDir, modelSize)) {
606
606
  logger.info(`[whisper-local] \u6A21\u578B\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u4E0B\u8F7D: ${filename}`);
607
607
  return { ok: true, modelPath };
@@ -667,7 +667,7 @@ async function probeUrl(url, logger) {
667
667
  async function downloadFromUrl(url, modelPath, logger) {
668
668
  const tmpPath = `${modelPath}.downloading`;
669
669
  try {
670
- (0, import_node_fs25.mkdirSync)((0, import_node_path21.dirname)(modelPath), { recursive: true });
670
+ (0, import_node_fs24.mkdirSync)((0, import_node_path20.dirname)(modelPath), { recursive: true });
671
671
  const controller = new AbortController();
672
672
  const timer = setTimeout(() => controller.abort(), 30 * 60 * 1e3);
673
673
  try {
@@ -683,7 +683,7 @@ async function downloadFromUrl(url, modelPath, logger) {
683
683
  return { ok: false, modelPath, error: "\u6A21\u578B\u4E0B\u8F7D\u5931\u8D25: \u54CD\u5E94\u4F53\u4E3A\u7A7A" };
684
684
  }
685
685
  const contentLength = Number(res.headers.get("content-length") ?? 0);
686
- const writeStream = (0, import_node_fs25.createWriteStream)(tmpPath);
686
+ const writeStream = (0, import_node_fs24.createWriteStream)(tmpPath);
687
687
  const readable = import_node_stream2.Readable.fromWeb(res.body);
688
688
  let downloaded = 0;
689
689
  let lastLogPercent = 0;
@@ -705,14 +705,14 @@ async function downloadFromUrl(url, modelPath, logger) {
705
705
  }
706
706
  const { renameSync: renameSync2 } = await import("fs");
707
707
  renameSync2(tmpPath, modelPath);
708
- const fileSize = (0, import_node_fs25.statSync)(modelPath).size;
708
+ const fileSize = (0, import_node_fs24.statSync)(modelPath).size;
709
709
  logger.info(
710
- `[whisper-local] \u6A21\u578B\u4E0B\u8F7D\u5B8C\u6210: ${(0, import_node_path21.basename)(modelPath)} (${formatBytes2(fileSize)})`
710
+ `[whisper-local] \u6A21\u578B\u4E0B\u8F7D\u5B8C\u6210: ${(0, import_node_path20.basename)(modelPath)} (${formatBytes2(fileSize)})`
711
711
  );
712
712
  return { ok: true, modelPath };
713
713
  } catch (err2) {
714
714
  try {
715
- if ((0, import_node_fs25.existsSync)(tmpPath)) (0, import_node_fs25.unlinkSync)(tmpPath);
715
+ if ((0, import_node_fs24.existsSync)(tmpPath)) (0, import_node_fs24.unlinkSync)(tmpPath);
716
716
  } catch {
717
717
  }
718
718
  const msg = err2?.name === "AbortError" ? "\u6A21\u578B\u4E0B\u8F7D\u8D85\u65F6\uFF0830 \u5206\u949F\uFF09" : err2?.message ?? String(err2);
@@ -724,8 +724,8 @@ function findWhisperBinary(dataDir, logger) {
724
724
  const binDir = resolveBinDir(dataDir);
725
725
  const binNames = (0, import_node_os4.platform)() === "win32" ? ["whisper-cli.exe", "whisper.exe", "main.exe"] : ["whisper-cli", "whisper", "main"];
726
726
  for (const name of binNames) {
727
- const binPath = (0, import_node_path21.join)(binDir, name);
728
- if ((0, import_node_fs25.existsSync)(binPath)) {
727
+ const binPath = (0, import_node_path20.join)(binDir, name);
728
+ if ((0, import_node_fs24.existsSync)(binPath)) {
729
729
  logger.info(`[whisper-local] \u627E\u5230\u672C\u5730\u4E8C\u8FDB\u5236: ${binPath}`);
730
730
  return binPath;
731
731
  }
@@ -776,7 +776,7 @@ async function transcribeWithWhisperLocal(audioFilePath, localConfig, dataDir, l
776
776
  return { ok: false, error: `\u6A21\u578B\u4E0B\u8F7D\u5931\u8D25: ${downloadResult.error}` };
777
777
  }
778
778
  }
779
- const modelPath = (0, import_node_path21.join)(modelsDir, MODEL_FILENAMES[modelSize]);
779
+ const modelPath = (0, import_node_path20.join)(modelsDir, MODEL_FILENAMES[modelSize]);
780
780
  let inputPath = audioFilePath;
781
781
  let tmpWavPath = null;
782
782
  const actualFmt = detectAudioFormat(audioFilePath);
@@ -828,10 +828,10 @@ async function transcribeWithWhisperLocal(audioFilePath, localConfig, dataDir, l
828
828
  logger.info(`[whisper-local] \u8F6C\u5199\u8017\u65F6: ${Math.round(elapsed / 1e3)}s`);
829
829
  const jsonPath = inputPath + ".json";
830
830
  let jsonContent;
831
- if ((0, import_node_fs25.existsSync)(jsonPath)) {
832
- jsonContent = (0, import_node_fs25.readFileSync)(jsonPath, "utf-8");
831
+ if ((0, import_node_fs24.existsSync)(jsonPath)) {
832
+ jsonContent = (0, import_node_fs24.readFileSync)(jsonPath, "utf-8");
833
833
  try {
834
- (0, import_node_fs25.unlinkSync)(jsonPath);
834
+ (0, import_node_fs24.unlinkSync)(jsonPath);
835
835
  } catch {
836
836
  }
837
837
  } else {
@@ -909,10 +909,10 @@ function getPhysicalCoreCount() {
909
909
  }
910
910
  function detectAudioFormat(filePath) {
911
911
  try {
912
- const fd = (0, import_node_fs25.openSync)(filePath, "r");
912
+ const fd = (0, import_node_fs24.openSync)(filePath, "r");
913
913
  const buf = Buffer.alloc(12);
914
- (0, import_node_fs25.readSync)(fd, buf, 0, 12, 0);
915
- (0, import_node_fs25.closeSync)(fd);
914
+ (0, import_node_fs24.readSync)(fd, buf, 0, 12, 0);
915
+ (0, import_node_fs24.closeSync)(fd);
916
916
  const header = buf.toString("ascii", 0, 4);
917
917
  const header8 = buf.toString("ascii", 0, 8);
918
918
  if (header === "RIFF" && buf.toString("ascii", 8, 12) === "WAVE") return ".wav";
@@ -947,7 +947,7 @@ function convertToWav(inputPath, outputPath, actualFmt, logger) {
947
947
  timeout: 12e4,
948
948
  stdio: ["pipe", "pipe", "pipe"]
949
949
  });
950
- if (ffmpegResult.status === 0 && (0, import_node_fs25.existsSync)(outputPath)) {
950
+ if (ffmpegResult.status === 0 && (0, import_node_fs24.existsSync)(outputPath)) {
951
951
  logger.info(`[whisper-local] ffmpeg \u8F6C\u6362\u5B8C\u6210: ${outputPath}`);
952
952
  return { ok: true };
953
953
  }
@@ -960,7 +960,7 @@ function convertToWav(inputPath, outputPath, actualFmt, logger) {
960
960
  ["--rate", "16000", "--mono", inputPath, outputPath],
961
961
  { encoding: "utf-8", timeout: 12e4, stdio: ["pipe", "pipe", "pipe"] }
962
962
  );
963
- if (opusResult.status === 0 && (0, import_node_fs25.existsSync)(outputPath)) {
963
+ if (opusResult.status === 0 && (0, import_node_fs24.existsSync)(outputPath)) {
964
964
  logger.info(`[whisper-local] opusdec \u8F6C\u6362\u5B8C\u6210: ${outputPath}`);
965
965
  return { ok: true };
966
966
  }
@@ -974,7 +974,7 @@ function convertToWav(inputPath, outputPath, actualFmt, logger) {
974
974
  if (detectedExt && !inputPath.endsWith(detectedExt)) {
975
975
  tmpCopy = inputPath + ".detected" + detectedExt;
976
976
  try {
977
- (0, import_node_fs25.copyFileSync)(inputPath, tmpCopy);
977
+ (0, import_node_fs24.copyFileSync)(inputPath, tmpCopy);
978
978
  actualInputPath = tmpCopy;
979
979
  logger.info(
980
980
  `[whisper-local] \u68C0\u6D4B\u5230\u5B9E\u9645\u683C\u5F0F ${detectedExt}\uFF0C\u4E34\u65F6\u91CD\u547D\u540D`
@@ -998,22 +998,22 @@ function convertToWav(inputPath, outputPath, actualFmt, logger) {
998
998
  timeout: 12e4,
999
999
  stdio: ["pipe", "pipe", "pipe"]
1000
1000
  });
1001
- if (tmpCopy && (0, import_node_fs25.existsSync)(tmpCopy)) {
1001
+ if (tmpCopy && (0, import_node_fs24.existsSync)(tmpCopy)) {
1002
1002
  try {
1003
- (0, import_node_fs25.unlinkSync)(tmpCopy);
1003
+ (0, import_node_fs24.unlinkSync)(tmpCopy);
1004
1004
  } catch {
1005
1005
  }
1006
1006
  }
1007
- if (afResult.status === 0 && (0, import_node_fs25.existsSync)(outputPath)) {
1007
+ if (afResult.status === 0 && (0, import_node_fs24.existsSync)(outputPath)) {
1008
1008
  logger.info(`[whisper-local] afconvert \u8F6C\u6362\u5B8C\u6210: ${outputPath}`);
1009
1009
  return { ok: true };
1010
1010
  }
1011
1011
  const stderr = afResult.stderr?.slice(0, 200) ?? "";
1012
1012
  return { ok: false, error: `afconvert \u8F6C\u6362\u5931\u8D25 (exit ${afResult.status}): ${stderr}` };
1013
1013
  } catch (err2) {
1014
- if (tmpCopy && (0, import_node_fs25.existsSync)(tmpCopy)) {
1014
+ if (tmpCopy && (0, import_node_fs24.existsSync)(tmpCopy)) {
1015
1015
  try {
1016
- (0, import_node_fs25.unlinkSync)(tmpCopy);
1016
+ (0, import_node_fs24.unlinkSync)(tmpCopy);
1017
1017
  } catch {
1018
1018
  }
1019
1019
  }
@@ -1024,9 +1024,9 @@ function convertToWav(inputPath, outputPath, actualFmt, logger) {
1024
1024
  return { ok: false, error: `\u65E0\u6CD5\u5C06\u97F3\u9891\u8F6C\u6362\u4E3A WAV \u683C\u5F0F\u3002${fmtHint}` };
1025
1025
  }
1026
1026
  function cleanupTmpWav(path2) {
1027
- if (path2 && (0, import_node_fs25.existsSync)(path2)) {
1027
+ if (path2 && (0, import_node_fs24.existsSync)(path2)) {
1028
1028
  try {
1029
- (0, import_node_fs25.unlinkSync)(path2);
1029
+ (0, import_node_fs24.unlinkSync)(path2);
1030
1030
  } catch {
1031
1031
  }
1032
1032
  }
@@ -1112,13 +1112,13 @@ function formatBytes2(bytes) {
1112
1112
  if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
1113
1113
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)}GB`;
1114
1114
  }
1115
- var import_node_child_process3, import_node_fs25, import_node_path21, import_promises3, import_node_stream2, import_node_os4, WHISPER_MODELS_DIR, WHISPER_BIN_DIR, HF_MODEL_URL_TEMPLATE, MODELSCOPE_MODEL_URL_TEMPLATE, PROBE_TIMEOUT_MS, MODEL_FILENAMES, MODEL_DISK_SIZES;
1115
+ var import_node_child_process3, import_node_fs24, import_node_path20, import_promises3, import_node_stream2, import_node_os4, WHISPER_MODELS_DIR, WHISPER_BIN_DIR, HF_MODEL_URL_TEMPLATE, MODELSCOPE_MODEL_URL_TEMPLATE, PROBE_TIMEOUT_MS, MODEL_FILENAMES, MODEL_DISK_SIZES;
1116
1116
  var init_whisper_local = __esm({
1117
1117
  "src/recording/whisper-local.ts"() {
1118
1118
  "use strict";
1119
1119
  import_node_child_process3 = require("child_process");
1120
- import_node_fs25 = require("fs");
1121
- import_node_path21 = require("path");
1120
+ import_node_fs24 = require("fs");
1121
+ import_node_path20 = require("path");
1122
1122
  import_promises3 = require("stream/promises");
1123
1123
  import_node_stream2 = require("stream");
1124
1124
  import_node_os4 = require("os");
@@ -1273,7 +1273,7 @@ async function initializeAsr(config, dataDir, logger) {
1273
1273
  }
1274
1274
  }
1275
1275
  async function transcribeAudio(audioFilePath, config, logger, options = {}) {
1276
- if (!(0, import_node_fs26.existsSync)(audioFilePath)) {
1276
+ if (!(0, import_node_fs25.existsSync)(audioFilePath)) {
1277
1277
  return { ok: false, error: `\u97F3\u9891\u6587\u4EF6\u4E0D\u5B58\u5728: ${audioFilePath}` };
1278
1278
  }
1279
1279
  logger.info(
@@ -1419,8 +1419,8 @@ async function runTranscriptionWorkflow(params) {
1419
1419
  createdAt
1420
1420
  );
1421
1421
  const transcriptDataFilename = buildTranscriptDataFilename(recordingId);
1422
- const transcriptDataPath = (0, import_node_path22.join)(transcriptDataDir, transcriptDataFilename);
1423
- (0, import_node_fs26.writeFileSync)(
1422
+ const transcriptDataPath = (0, import_node_path21.join)(transcriptDataDir, transcriptDataFilename);
1423
+ (0, import_node_fs25.writeFileSync)(
1424
1424
  transcriptDataPath,
1425
1425
  JSON.stringify(transcriptData, null, 2),
1426
1426
  "utf-8"
@@ -1428,14 +1428,14 @@ async function runTranscriptionWorkflow(params) {
1428
1428
  logger.info(`[asr] \u8F6C\u5199 JSON \u5DF2\u5199\u5165: ${transcriptDataPath}`);
1429
1429
  const safeSummary = title.replace(/[/\\:*?"<>|]/g, "").trim().slice(0, 20);
1430
1430
  const filename = safeSummary ? `${recordingId}_${safeSummary}.md` : `${recordingId}.md`;
1431
- const filePath = (0, import_node_path22.join)(transcriptsDir, filename);
1432
- (0, import_node_fs26.writeFileSync)(filePath, markdown, "utf-8");
1431
+ const filePath = (0, import_node_path21.join)(transcriptsDir, filename);
1432
+ (0, import_node_fs25.writeFileSync)(filePath, markdown, "utf-8");
1433
1433
  logger.info(`[asr] \u8F6C\u5199\u6587\u672C\u5DF2\u5199\u5165: ${filePath}`);
1434
1434
  let summaryFilename;
1435
1435
  if (summary) {
1436
1436
  summaryFilename = `${recordingId}.md`;
1437
- const summaryFilePath = (0, import_node_path22.join)(summariesDir, summaryFilename);
1438
- (0, import_node_fs26.writeFileSync)(summaryFilePath, summary, "utf-8");
1437
+ const summaryFilePath = (0, import_node_path21.join)(summariesDir, summaryFilename);
1438
+ (0, import_node_fs25.writeFileSync)(summaryFilePath, summary, "utf-8");
1439
1439
  logger.info(`[asr] \u6458\u8981\u6587\u672C\u5DF2\u5199\u5165: ${summaryFilePath}`);
1440
1440
  }
1441
1441
  return {
@@ -1521,7 +1521,7 @@ async function transcribeWithModelProxy(audioOssUrl, audioDurationMs, apiConfig,
1521
1521
  }
1522
1522
  async function transcribeWithWhisperLocal2(audioFilePath, config, logger) {
1523
1523
  const { transcribeWithWhisperLocal: runLocal } = await Promise.resolve().then(() => (init_whisper_local(), whisper_local_exports));
1524
- const dataDir = process.env.OPENCLAW_STATE_DIR ?? process.env.QCLAW_STATE_DIR ?? (0, import_node_path22.join)(audioFilePath, "..", "..", "..");
1524
+ const dataDir = process.env.OPENCLAW_STATE_DIR ?? process.env.QCLAW_STATE_DIR ?? (0, import_node_path21.join)(audioFilePath, "..", "..", "..");
1525
1525
  const localConfig = config.local ?? {};
1526
1526
  const result = await runLocal(
1527
1527
  audioFilePath,
@@ -1830,12 +1830,12 @@ function formatTranscriptSegmentText(segment) {
1830
1830
  }
1831
1831
  return text;
1832
1832
  }
1833
- var import_node_fs26, import_node_path22, DEFAULT_LONG_RECORDING_POLL_INTERVAL_MS, DEFAULT_LONG_RECORDING_MAX_POLL_ATTEMPTS, LONG_RECORDING_RUNNING_STATUSES, LONG_RECORDING_TERMINAL_FAILURE_STATUSES;
1833
+ var import_node_fs25, import_node_path21, DEFAULT_LONG_RECORDING_POLL_INTERVAL_MS, DEFAULT_LONG_RECORDING_MAX_POLL_ATTEMPTS, LONG_RECORDING_RUNNING_STATUSES, LONG_RECORDING_TERMINAL_FAILURE_STATUSES;
1834
1834
  var init_asr = __esm({
1835
1835
  "src/recording/asr.ts"() {
1836
1836
  "use strict";
1837
- import_node_fs26 = require("fs");
1838
- import_node_path22 = require("path");
1837
+ import_node_fs25 = require("fs");
1838
+ import_node_path21 = require("path");
1839
1839
  init_credentials();
1840
1840
  init_env();
1841
1841
  init_transcript_document();
@@ -5480,7 +5480,7 @@ function readBuildInjectedVersion() {
5480
5480
  if (false) {
5481
5481
  return void 0;
5482
5482
  }
5483
- const version = "1.11.4-beta.3".trim();
5483
+ const version = "1.11.4-beta.4".trim();
5484
5484
  return version || void 0;
5485
5485
  }
5486
5486
  function readPluginVersionFromPackageJson() {
@@ -7427,6 +7427,43 @@ function resolveUpdateChannel(params) {
7427
7427
  // src/update/index.ts
7428
7428
  var import_node_path8 = require("path");
7429
7429
 
7430
+ // src/plugin/runtime-state.ts
7431
+ init_host();
7432
+
7433
+ // src/plugin/shared.ts
7434
+ function readBody(req) {
7435
+ return new Promise((resolve, reject) => {
7436
+ const chunks = [];
7437
+ req.on("data", (chunk) => chunks.push(chunk));
7438
+ req.on("end", () => resolve(Buffer.concat(chunks).toString()));
7439
+ req.on("error", reject);
7440
+ });
7441
+ }
7442
+ function trimToUndefined2(value) {
7443
+ if (typeof value !== "string") {
7444
+ return void 0;
7445
+ }
7446
+ const trimmed = value.trim();
7447
+ return trimmed || void 0;
7448
+ }
7449
+
7450
+ // src/plugin/runtime-state.ts
7451
+ function tryResolveRuntimeStateDir(api) {
7452
+ const runtimeState = api?.runtime?.state;
7453
+ const resolveStateDir2 = runtimeState?.resolveStateDir;
7454
+ if (typeof resolveStateDir2 !== "function") {
7455
+ return void 0;
7456
+ }
7457
+ try {
7458
+ return trimToUndefined2(resolveStateDir2.call(runtimeState));
7459
+ } catch {
7460
+ return void 0;
7461
+ }
7462
+ }
7463
+ function resolvePluginStateDir(api) {
7464
+ return tryResolveRuntimeStateDir(api) ?? resolveStateDir();
7465
+ }
7466
+
7430
7467
  // src/update/checker.ts
7431
7468
  function parseSemver(v) {
7432
7469
  const [main, pre = null] = v.split("-", 2);
@@ -7647,7 +7684,7 @@ function resolveTargetDir(api) {
7647
7684
  if (installPath) return installPath;
7648
7685
  } catch {
7649
7686
  }
7650
- return (0, import_node_path8.join)(api.runtime.state.resolveStateDir(), "extensions", PLUGIN_ID);
7687
+ return (0, import_node_path8.join)(resolvePluginStateDir(api), "extensions", PLUGIN_ID);
7651
7688
  }
7652
7689
  async function updateConfigRecord(api, version, targetDir, tgzUrl) {
7653
7690
  const configApi = api.runtime.config;
@@ -7857,7 +7894,7 @@ function registerAutoUpdateLifecycle(deps) {
7857
7894
  }
7858
7895
 
7859
7896
  // src/plugin/cli.ts
7860
- var import_node_path16 = require("path");
7897
+ var import_node_path15 = require("path");
7861
7898
 
7862
7899
  // src/cli/auth.ts
7863
7900
  var import_node_fs9 = require("fs");
@@ -8532,21 +8569,155 @@ function registerLightSetupTools(light) {
8532
8569
  }
8533
8570
 
8534
8571
  // src/cli/tunnel-status.ts
8535
- var import_node_fs13 = require("fs");
8536
- var import_node_path12 = require("path");
8537
8572
  init_credentials();
8538
8573
  init_env();
8539
- var STATUS_REL_PATH = (0, import_node_path12.join)("plugins", "phone-notifications", "tunnel-status.json");
8540
- function readTunnelStatus(ctx) {
8541
- if (!ctx.stateDir) return null;
8542
- const filePath = (0, import_node_path12.join)(ctx.stateDir, STATUS_REL_PATH);
8543
- if (!(0, import_node_fs13.existsSync)(filePath)) return null;
8574
+
8575
+ // src/tunnel/status.ts
8576
+ var import_node_fs13 = require("fs");
8577
+ var import_node_path12 = require("path");
8578
+ var TUNNEL_STATUS_REL_PATH = (0, import_node_path12.join)(
8579
+ "plugins",
8580
+ "phone-notifications",
8581
+ "tunnel-status.json"
8582
+ );
8583
+ var TUNNEL_LOCK_REL_PATH = (0, import_node_path12.join)(
8584
+ "plugins",
8585
+ "phone-notifications",
8586
+ "relay-tunnel.lock"
8587
+ );
8588
+ function isTunnelState(value) {
8589
+ return value === "connected" || value === "connecting" || value === "disconnected" || value === "stopped";
8590
+ }
8591
+ function isTunnelStatusInfo(value) {
8592
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
8593
+ const obj = value;
8594
+ return isTunnelState(obj.state) && typeof obj.since === "string" && typeof obj.reconnectAttempt === "number" && (obj.lastDisconnectReason === void 0 || typeof obj.lastDisconnectReason === "string");
8595
+ }
8596
+ function parseTunnelLockInfo(value) {
8597
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
8598
+ const obj = value;
8599
+ return typeof obj.pid === "number" && typeof obj.startedAt === "string" ? { pid: obj.pid, startedAt: obj.startedAt } : null;
8600
+ }
8601
+ function isProcessAlive(pid) {
8602
+ if (!Number.isInteger(pid) || pid <= 0) return false;
8603
+ if (pid === process.pid) return true;
8604
+ try {
8605
+ process.kill(pid, 0);
8606
+ return true;
8607
+ } catch (err2) {
8608
+ return err2?.code === "EPERM";
8609
+ }
8610
+ }
8611
+ function isStatusOlderThanLock(statusSince, lockStartedAt) {
8612
+ const statusTs = Date.parse(statusSince);
8613
+ const lockTs = Date.parse(lockStartedAt);
8614
+ if (Number.isNaN(statusTs) || Number.isNaN(lockTs)) return false;
8615
+ return statusTs + 1e3 < lockTs;
8616
+ }
8617
+ function staleStatusMessage(status, lock) {
8618
+ const prefix = `\u96A7\u9053\u72B6\u6001\u6587\u4EF6\u663E\u793A\u5DF2\u8FDE\u63A5\uFF08\u81EA ${status.since}\uFF09`;
8619
+ if (!lock.exists) {
8620
+ return `${prefix}\uFF0C\u4F46\u672A\u627E\u5230\u672C\u5730\u8FD0\u884C\u9501\uFF0C\u72B6\u6001\u53EF\u80FD\u5DF2\u8FC7\u671F\u3002\u8BF7\u91CD\u542F openclaw \u4E3B\u8FDB\u7A0B\u3002`;
8621
+ }
8622
+ if (lock.pid === null || lock.startedAt === null) {
8623
+ return `${prefix}\uFF0C\u4F46\u672C\u5730\u8FD0\u884C\u9501\u5185\u5BB9\u4E0D\u5B8C\u6574\uFF0C\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D\u8FDE\u63A5\u662F\u5426\u4ECD\u7136\u6709\u6548\u3002\u8BF7\u91CD\u542F openclaw \u4E3B\u8FDB\u7A0B\u3002`;
8624
+ }
8625
+ if (lock.active === false) {
8626
+ return `${prefix}\uFF0C\u4F46\u672C\u5730\u8FD0\u884C\u9501\u4ECD\u6307\u5411\u5DF2\u9000\u51FA\u7684\u8FDB\u7A0B pid=${lock.pid}\uFF0C\u72B6\u6001\u53EF\u80FD\u5DF2\u8FC7\u671F\u3002\u8BF7\u91CD\u542F openclaw \u4E3B\u8FDB\u7A0B\u3002`;
8627
+ }
8628
+ return `${prefix}\uFF0C\u4F46\u5F53\u524D\u8FD0\u884C\u9501\u542F\u52A8\u4E8E ${lock.startedAt}\uFF0C\u665A\u4E8E\u72B6\u6001\u65F6\u95F4\uFF0C\u8BF4\u660E\u72B6\u6001\u672A\u968F\u65B0\u8FDB\u7A0B\u5237\u65B0\u3002\u8BF7\u91CD\u542F openclaw \u4E3B\u8FDB\u7A0B\u3002`;
8629
+ }
8630
+ function assessTunnelStatus(stateDir) {
8631
+ const statusFilePath = (0, import_node_path12.join)(stateDir, TUNNEL_STATUS_REL_PATH);
8632
+ const lockFilePath = (0, import_node_path12.join)(stateDir, TUNNEL_LOCK_REL_PATH);
8633
+ if (!(0, import_node_fs13.existsSync)(statusFilePath)) {
8634
+ return {
8635
+ status: null,
8636
+ issueCode: "STATUS_NOT_FOUND",
8637
+ issueMessage: "\u672A\u627E\u5230\u96A7\u9053\u72B6\u6001\u6587\u4EF6\uFF0C\u96A7\u9053\u670D\u52A1\u53EF\u80FD\u5C1A\u672A\u542F\u52A8\u8FC7\u3002\u8BF7\u786E\u8BA4 openclaw \u4E3B\u8FDB\u7A0B\u6B63\u5728\u8FD0\u884C\u3002",
8638
+ statusFilePath,
8639
+ lockFilePath,
8640
+ lock: {
8641
+ exists: (0, import_node_fs13.existsSync)(lockFilePath),
8642
+ pid: null,
8643
+ startedAt: null,
8644
+ active: null
8645
+ }
8646
+ };
8647
+ }
8648
+ let rawStatus;
8544
8649
  try {
8545
- return JSON.parse((0, import_node_fs13.readFileSync)(filePath, "utf-8"));
8650
+ rawStatus = JSON.parse((0, import_node_fs13.readFileSync)(statusFilePath, "utf-8"));
8546
8651
  } catch {
8547
- return null;
8652
+ return {
8653
+ status: null,
8654
+ issueCode: "STATUS_INVALID",
8655
+ issueMessage: "\u96A7\u9053\u72B6\u6001\u6587\u4EF6\u89E3\u6790\u5931\u8D25\uFF0C\u6587\u4EF6\u5185\u5BB9\u635F\u574F\u6216\u683C\u5F0F\u4E0D\u6B63\u786E\u3002\u8BF7\u91CD\u542F openclaw \u4E3B\u8FDB\u7A0B\u3002",
8656
+ statusFilePath,
8657
+ lockFilePath,
8658
+ lock: {
8659
+ exists: (0, import_node_fs13.existsSync)(lockFilePath),
8660
+ pid: null,
8661
+ startedAt: null,
8662
+ active: null
8663
+ }
8664
+ };
8665
+ }
8666
+ if (!isTunnelStatusInfo(rawStatus)) {
8667
+ return {
8668
+ status: null,
8669
+ issueCode: "STATUS_INVALID",
8670
+ issueMessage: "\u96A7\u9053\u72B6\u6001\u6587\u4EF6\u7F3A\u5C11\u5FC5\u8981\u5B57\u6BB5\u6216\u5B57\u6BB5\u7C7B\u578B\u4E0D\u6B63\u786E\u3002\u8BF7\u91CD\u542F openclaw \u4E3B\u8FDB\u7A0B\u3002",
8671
+ statusFilePath,
8672
+ lockFilePath,
8673
+ lock: {
8674
+ exists: (0, import_node_fs13.existsSync)(lockFilePath),
8675
+ pid: null,
8676
+ startedAt: null,
8677
+ active: null
8678
+ }
8679
+ };
8680
+ }
8681
+ const status = rawStatus;
8682
+ const lockExists = (0, import_node_fs13.existsSync)(lockFilePath);
8683
+ let lockInfo = null;
8684
+ if (lockExists) {
8685
+ try {
8686
+ lockInfo = parseTunnelLockInfo(JSON.parse((0, import_node_fs13.readFileSync)(lockFilePath, "utf-8")));
8687
+ } catch {
8688
+ lockInfo = null;
8689
+ }
8548
8690
  }
8691
+ const lock = {
8692
+ exists: lockExists,
8693
+ pid: lockInfo?.pid ?? null,
8694
+ startedAt: lockInfo?.startedAt ?? null,
8695
+ active: lockInfo ? isProcessAlive(lockInfo.pid) : null
8696
+ };
8697
+ if (status.state === "connected") {
8698
+ const isStale = !lock.exists || lock.pid === null || lock.startedAt === null || lock.active === false || isStatusOlderThanLock(status.since, lock.startedAt);
8699
+ if (isStale) {
8700
+ return {
8701
+ status,
8702
+ issueCode: "STATUS_STALE",
8703
+ issueMessage: staleStatusMessage(status, lock),
8704
+ statusFilePath,
8705
+ lockFilePath,
8706
+ lock
8707
+ };
8708
+ }
8709
+ }
8710
+ return {
8711
+ status,
8712
+ issueCode: null,
8713
+ issueMessage: null,
8714
+ statusFilePath,
8715
+ lockFilePath,
8716
+ lock
8717
+ };
8549
8718
  }
8719
+
8720
+ // src/cli/tunnel-status.ts
8550
8721
  function formatMessage(status) {
8551
8722
  switch (status.state) {
8552
8723
  case "connected":
@@ -8577,13 +8748,23 @@ function registerTunnelStatus(ntf, ctx) {
8577
8748
  "API Key \u672A\u8BBE\u7F6E\uFF0C\u96A7\u9053\u65E0\u6CD5\u8FDE\u63A5\u3002\u8BF7\u6267\u884C openclaw ntf auth set-api-key <apiKey>"
8578
8749
  );
8579
8750
  }
8580
- const status = readTunnelStatus(ctx);
8751
+ if (!ctx.stateDir) {
8752
+ exitError(
8753
+ "STATE_DIR_UNAVAILABLE",
8754
+ "\u65E0\u6CD5\u786E\u5B9A\u72B6\u6001\u76EE\u5F55\uFF0C\u65E0\u6CD5\u68C0\u67E5\u96A7\u9053\u72B6\u6001\u3002\u8BF7\u5728 openclaw \u4E3B\u73AF\u5883\u4E2D\u6267\u884C\u8BE5\u547D\u4EE4\u3002"
8755
+ );
8756
+ }
8757
+ const assessment = assessTunnelStatus(ctx.stateDir);
8758
+ const status = assessment.status;
8581
8759
  if (!status) {
8582
8760
  exitError(
8583
- "STATUS_NOT_FOUND",
8584
- "\u672A\u627E\u5230\u96A7\u9053\u72B6\u6001\u6587\u4EF6\uFF0C\u96A7\u9053\u670D\u52A1\u53EF\u80FD\u5C1A\u672A\u542F\u52A8\u8FC7\u3002\u8BF7\u786E\u8BA4 openclaw \u4E3B\u8FDB\u7A0B\u6B63\u5728\u8FD0\u884C\u3002"
8761
+ assessment.issueCode ?? "STATUS_NOT_FOUND",
8762
+ assessment.issueMessage ?? "\u672A\u627E\u5230\u96A7\u9053\u72B6\u6001\u6587\u4EF6\uFF0C\u96A7\u9053\u670D\u52A1\u53EF\u80FD\u5C1A\u672A\u542F\u52A8\u8FC7\u3002\u8BF7\u786E\u8BA4 openclaw \u4E3B\u8FDB\u7A0B\u6B63\u5728\u8FD0\u884C\u3002"
8585
8763
  );
8586
8764
  }
8765
+ if (assessment.issueCode) {
8766
+ exitError(assessment.issueCode, assessment.issueMessage ?? formatMessage(status));
8767
+ }
8587
8768
  const ok2 = status.state === "connected";
8588
8769
  output({
8589
8770
  ok: ok2,
@@ -8594,6 +8775,11 @@ function registerTunnelStatus(ntf, ctx) {
8594
8775
  reconnectAttempt: status.reconnectAttempt,
8595
8776
  lastDisconnectReason: status.lastDisconnectReason
8596
8777
  },
8778
+ runtime: {
8779
+ lockOwnerPid: assessment.lock.pid,
8780
+ lockOwnerStartedAt: assessment.lock.startedAt,
8781
+ lockOwnerActive: assessment.lock.active
8782
+ },
8597
8783
  message: formatMessage(status)
8598
8784
  });
8599
8785
  if (!ok2) process.exit(1);
@@ -8773,7 +8959,7 @@ function registerEnvCli(ntf, deps = {}) {
8773
8959
  }
8774
8960
 
8775
8961
  // src/cli/doctor.ts
8776
- var import_node_fs18 = require("fs");
8962
+ var import_node_fs17 = require("fs");
8777
8963
  var import_node_readline = require("readline");
8778
8964
  init_host();
8779
8965
 
@@ -8922,39 +9108,40 @@ var checkCredentials = () => {
8922
9108
  };
8923
9109
 
8924
9110
  // src/cli/doctor/check-tunnel.ts
8925
- var import_node_fs17 = require("fs");
8926
- var import_node_path14 = require("path");
8927
- var STATUS_REL_PATH2 = (0, import_node_path14.join)(
8928
- "plugins",
8929
- "phone-notifications",
8930
- "tunnel-status.json"
8931
- );
8932
9111
  var checkTunnel = ({ stateDir }) => {
8933
- const filePath = (0, import_node_path14.join)(stateDir, STATUS_REL_PATH2);
8934
- if (!(0, import_node_fs17.existsSync)(filePath)) {
9112
+ const assessment = assessTunnelStatus(stateDir);
9113
+ const status = assessment.status;
9114
+ if (assessment.issueCode === "STATUS_NOT_FOUND") {
8935
9115
  return {
8936
9116
  id: "tunnel",
8937
9117
  severity: "warn",
8938
9118
  title: "\u96A7\u9053\u72B6\u6001\u6587\u4EF6\u4E0D\u5B58\u5728",
8939
- detail: "\u96A7\u9053\u670D\u52A1\u53EF\u80FD\u5C1A\u672A\u542F\u52A8\u8FC7\uFF0C\u6216\u72B6\u6001\u6587\u4EF6\u5DF2\u88AB\u5220\u9664\u3002",
9119
+ detail: assessment.issueMessage ?? "\u96A7\u9053\u670D\u52A1\u53EF\u80FD\u5C1A\u672A\u542F\u52A8\u8FC7\uFF0C\u6216\u72B6\u6001\u6587\u4EF6\u5DF2\u88AB\u5220\u9664\u3002",
8940
9120
  fixDescription: "\u8BF7\u786E\u8BA4 openclaw \u4E3B\u8FDB\u7A0B\u6B63\u5728\u8FD0\u884C",
8941
9121
  fix: null
8942
9122
  };
8943
9123
  }
8944
- let status;
8945
- try {
8946
- status = JSON.parse((0, import_node_fs17.readFileSync)(filePath, "utf-8"));
8947
- } catch {
9124
+ if (assessment.issueCode === "STATUS_INVALID") {
8948
9125
  return {
8949
9126
  id: "tunnel",
8950
9127
  severity: "warn",
8951
9128
  title: "\u96A7\u9053\u72B6\u6001\u6587\u4EF6\u89E3\u6790\u5931\u8D25",
8952
- detail: "\u6587\u4EF6\u5185\u5BB9\u635F\u574F\u6216\u683C\u5F0F\u4E0D\u6B63\u786E\u3002",
9129
+ detail: assessment.issueMessage ?? "\u6587\u4EF6\u5185\u5BB9\u635F\u574F\u6216\u683C\u5F0F\u4E0D\u6B63\u786E\u3002",
9130
+ fixDescription: "\u8BF7\u91CD\u542F openclaw \u4E3B\u8FDB\u7A0B",
9131
+ fix: null
9132
+ };
9133
+ }
9134
+ if (assessment.issueCode === "STATUS_STALE") {
9135
+ return {
9136
+ id: "tunnel",
9137
+ severity: "warn",
9138
+ title: "\u96A7\u9053\u72B6\u6001\u5DF2\u8FC7\u671F",
9139
+ detail: assessment.issueMessage ?? "\u96A7\u9053\u72B6\u6001\u6587\u4EF6\u4ECD\u663E\u793A\u5DF2\u8FDE\u63A5\uFF0C\u4F46\u8FD0\u884C\u65F6\u72B6\u6001\u5DF2\u5931\u6548\u3002",
8953
9140
  fixDescription: "\u8BF7\u91CD\u542F openclaw \u4E3B\u8FDB\u7A0B",
8954
9141
  fix: null
8955
9142
  };
8956
9143
  }
8957
- if (status.state === "connected") return null;
9144
+ if (!status || status.state === "connected") return null;
8958
9145
  const reasonSuffix = status.lastDisconnectReason ? `\uFF0C\u539F\u56E0: ${status.lastDisconnectReason}` : "";
8959
9146
  return {
8960
9147
  id: "tunnel",
@@ -9036,9 +9223,9 @@ function isObject5(v) {
9036
9223
  return !!v && typeof v === "object" && !Array.isArray(v);
9037
9224
  }
9038
9225
  function readConfig(configPath) {
9039
- if (!(0, import_node_fs18.existsSync)(configPath)) return {};
9226
+ if (!(0, import_node_fs17.existsSync)(configPath)) return {};
9040
9227
  try {
9041
- const parsed = JSON.parse((0, import_node_fs18.readFileSync)(configPath, "utf-8"));
9228
+ const parsed = JSON.parse((0, import_node_fs17.readFileSync)(configPath, "utf-8"));
9042
9229
  return isObject5(parsed) ? parsed : {};
9043
9230
  } catch {
9044
9231
  return {};
@@ -9241,7 +9428,7 @@ function registerRecStoragePath(rec, ctx) {
9241
9428
 
9242
9429
  // src/cli/rec-setup.ts
9243
9430
  var import_node_readline2 = require("readline");
9244
- var import_node_fs19 = require("fs");
9431
+ var import_node_fs18 = require("fs");
9245
9432
  function ask(rl, question) {
9246
9433
  return new Promise((resolve) => rl.question(question, resolve));
9247
9434
  }
@@ -9327,9 +9514,9 @@ async function setupLocal(rl) {
9327
9514
  function registerRecSetup(rec, ctx) {
9328
9515
  rec.command("setup").description("\u4EA4\u4E92\u5F0F\u914D\u7F6E ASR \u8F6C\u5199\u53C2\u6570\uFF0C\u4FDD\u5B58\u5230\u672C\u5730\u914D\u7F6E\u6587\u4EF6").action(async () => {
9329
9516
  const configPath = resolveAsrConfigPath(ctx);
9330
- if ((0, import_node_fs19.existsSync)(configPath)) {
9517
+ if ((0, import_node_fs18.existsSync)(configPath)) {
9331
9518
  try {
9332
- const existing = JSON.parse((0, import_node_fs19.readFileSync)(configPath, "utf-8"));
9519
+ const existing = JSON.parse((0, import_node_fs18.readFileSync)(configPath, "utf-8"));
9333
9520
  process.stderr.write(`\u5F53\u524D\u5DF2\u6709\u914D\u7F6E\uFF1Amode = ${existing.mode}`);
9334
9521
  if (existing.updatedAt) process.stderr.write(`\uFF0C\u66F4\u65B0\u4E8E ${existing.updatedAt}`);
9335
9522
  process.stderr.write("\n");
@@ -9342,7 +9529,7 @@ function registerRecSetup(rec, ctx) {
9342
9529
  const modeIdx = await askChoice(rl, "\u9009\u62E9\u6A21\u5F0F", ["api\uFF08\u4E91\u7AEF model-proxy \u957F\u5F55\u97F3\uFF09", "local\uFF08\u672C\u5730 Whisper\uFF09"]);
9343
9530
  const config = modeIdx === 0 ? await setupApi(rl) : await setupLocal(rl);
9344
9531
  const stored = { ...config, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
9345
- (0, import_node_fs19.writeFileSync)(configPath, JSON.stringify(stored, null, 2), "utf-8");
9532
+ (0, import_node_fs18.writeFileSync)(configPath, JSON.stringify(stored, null, 2), "utf-8");
9346
9533
  process.stderr.write(`
9347
9534
  \u2713 \u914D\u7F6E\u5DF2\u4FDD\u5B58\u5230 ${configPath}
9348
9535
 
@@ -9356,8 +9543,8 @@ function registerRecSetup(rec, ctx) {
9356
9543
 
9357
9544
  // src/cli/update.ts
9358
9545
  var import_node_child_process2 = require("child_process");
9359
- var import_node_fs20 = require("fs");
9360
- var import_node_path15 = require("path");
9546
+ var import_node_fs19 = require("fs");
9547
+ var import_node_path14 = require("path");
9361
9548
  var import_node_os3 = __toESM(require("os"), 1);
9362
9549
  init_host();
9363
9550
  var BASE_URL2 = "https://artifact.yoooclaw.com/plugin";
@@ -9416,9 +9603,9 @@ async function runUpdate(ctx, opts) {
9416
9603
  `);
9417
9604
  process.exit(1);
9418
9605
  }
9419
- const tmpScript = (0, import_node_path15.join)(import_node_os3.default.tmpdir(), `openclaw-install-${Date.now()}.mjs`);
9606
+ const tmpScript = (0, import_node_path14.join)(import_node_os3.default.tmpdir(), `openclaw-install-${Date.now()}.mjs`);
9420
9607
  try {
9421
- (0, import_node_fs20.writeFileSync)(tmpScript, installScript, "utf-8");
9608
+ (0, import_node_fs19.writeFileSync)(tmpScript, installScript, "utf-8");
9422
9609
  } catch (err2) {
9423
9610
  const msg = `\u5199\u5165\u4E34\u65F6\u6587\u4EF6\u5931\u8D25: ${err2?.message ?? String(err2)}`;
9424
9611
  if (json) {
@@ -9436,7 +9623,7 @@ async function runUpdate(ctx, opts) {
9436
9623
  { stdio: "inherit" }
9437
9624
  );
9438
9625
  try {
9439
- (0, import_node_fs20.unlinkSync)(tmpScript);
9626
+ (0, import_node_fs19.unlinkSync)(tmpScript);
9440
9627
  } catch {
9441
9628
  }
9442
9629
  if (result.error) {
@@ -9500,17 +9687,17 @@ function inferOpenClawRootDir(workspaceDir) {
9500
9687
  if (!workspaceDir) {
9501
9688
  return void 0;
9502
9689
  }
9503
- if ((0, import_node_path16.basename)(workspaceDir) !== "workspace") {
9690
+ if ((0, import_node_path15.basename)(workspaceDir) !== "workspace") {
9504
9691
  return void 0;
9505
9692
  }
9506
- return (0, import_node_path16.dirname)(workspaceDir);
9693
+ return (0, import_node_path15.dirname)(workspaceDir);
9507
9694
  }
9508
9695
  function registerPluginCli(api, params) {
9509
9696
  const { logger, openclawDir } = params;
9510
9697
  const registerAlias = (command) => {
9511
9698
  api.registerCli(
9512
9699
  (ctx) => {
9513
- const effectiveStateDir = openclawDir ?? inferOpenClawRootDir(ctx.workspaceDir);
9700
+ const effectiveStateDir = openclawDir ?? ctx.stateDir ?? inferOpenClawRootDir(ctx.workspaceDir);
9514
9701
  registerAllCli(
9515
9702
  ctx.program,
9516
9703
  {
@@ -9754,12 +9941,12 @@ function registerLightControlTool(api, logger) {
9754
9941
  }
9755
9942
 
9756
9943
  // src/plugin/lifecycle.ts
9757
- var import_node_fs30 = require("fs");
9944
+ var import_node_fs29 = require("fs");
9758
9945
  init_host();
9759
9946
 
9760
9947
  // src/notification/app-name-map.ts
9761
- var import_node_fs21 = require("fs");
9762
- var import_node_path17 = require("path");
9948
+ var import_node_fs20 = require("fs");
9949
+ var import_node_path16 = require("path");
9763
9950
  init_credentials();
9764
9951
  init_env();
9765
9952
  var PLUGIN_STATE_DIR = "phone-notifications";
@@ -9780,7 +9967,7 @@ function isAppNameMapApiResponse(v) {
9780
9967
  );
9781
9968
  }
9782
9969
  function getCachePath(stateDir) {
9783
- return (0, import_node_path17.join)(stateDir, "plugins", PLUGIN_STATE_DIR, CACHE_FILE);
9970
+ return (0, import_node_path16.join)(stateDir, "plugins", PLUGIN_STATE_DIR, CACHE_FILE);
9784
9971
  }
9785
9972
  function createAppNameMapProvider(opts) {
9786
9973
  const { stateDir, logger } = opts;
@@ -9792,9 +9979,9 @@ function createAppNameMapProvider(opts) {
9792
9979
  let inFlightFetch = null;
9793
9980
  function loadFromDisk() {
9794
9981
  const path2 = getCachePath(stateDir);
9795
- if (!(0, import_node_fs21.existsSync)(path2)) return;
9982
+ if (!(0, import_node_fs20.existsSync)(path2)) return;
9796
9983
  try {
9797
- const raw = JSON.parse((0, import_node_fs21.readFileSync)(path2, "utf-8"));
9984
+ const raw = JSON.parse((0, import_node_fs20.readFileSync)(path2, "utf-8"));
9798
9985
  if (!isRecordOfStrings(raw)) return;
9799
9986
  map.clear();
9800
9987
  for (const [k, v] of Object.entries(raw)) map.set(k, v);
@@ -9838,10 +10025,10 @@ function createAppNameMapProvider(opts) {
9838
10025
  logger.warn("[app-name-map] refresh succeeded but got 0 entries");
9839
10026
  return;
9840
10027
  }
9841
- const dir = (0, import_node_path17.join)(stateDir, "plugins", PLUGIN_STATE_DIR);
9842
- (0, import_node_fs21.mkdirSync)(dir, { recursive: true });
10028
+ const dir = (0, import_node_path16.join)(stateDir, "plugins", PLUGIN_STATE_DIR);
10029
+ (0, import_node_fs20.mkdirSync)(dir, { recursive: true });
9843
10030
  const cachePath = getCachePath(stateDir);
9844
- (0, import_node_fs21.writeFileSync)(cachePath, JSON.stringify(Object.fromEntries(map), null, 2), "utf-8");
10031
+ (0, import_node_fs20.writeFileSync)(cachePath, JSON.stringify(Object.fromEntries(map), null, 2), "utf-8");
9845
10032
  logger.info(`[app-name-map] refreshed ${map.size} entries from server and saved: ${cachePath}`);
9846
10033
  } catch (e) {
9847
10034
  const message = e instanceof Error ? e.message : String(e);
@@ -9885,9 +10072,9 @@ function createAppNameMapProvider(opts) {
9885
10072
  }
9886
10073
 
9887
10074
  // src/notification/storage.ts
9888
- var import_node_fs22 = require("fs");
10075
+ var import_node_fs21 = require("fs");
9889
10076
  var import_node_crypto2 = require("crypto");
9890
- var import_node_path18 = require("path");
10077
+ var import_node_path17 = require("path");
9891
10078
 
9892
10079
  // src/notification/feishu-normalize.ts
9893
10080
  function normalizeOptionalText(value) {
@@ -10015,7 +10202,7 @@ var NOTIFICATION_DIR_NAME = "notifications";
10015
10202
  var ID_INDEX_DIR_NAME = ".ids";
10016
10203
  var CONTENT_KEY_INDEX_DIR_NAME = ".keys";
10017
10204
  function getStateFallbackNotificationDir(stateDir) {
10018
- return (0, import_node_path18.join)(stateDir, "plugins", "phone-notifications", NOTIFICATION_DIR_NAME);
10205
+ return (0, import_node_path17.join)(stateDir, "plugins", "phone-notifications", NOTIFICATION_DIR_NAME);
10019
10206
  }
10020
10207
  function buildFallbackContent(n) {
10021
10208
  const body = n.body?.trim();
@@ -10033,8 +10220,8 @@ function buildFallbackContent(n) {
10033
10220
  }
10034
10221
  function ensureWritableDirectory(dir) {
10035
10222
  try {
10036
- (0, import_node_fs22.mkdirSync)(dir, { recursive: true });
10037
- (0, import_node_fs22.accessSync)(dir, import_node_fs22.constants.R_OK | import_node_fs22.constants.W_OK);
10223
+ (0, import_node_fs21.mkdirSync)(dir, { recursive: true });
10224
+ (0, import_node_fs21.accessSync)(dir, import_node_fs21.constants.R_OK | import_node_fs21.constants.W_OK);
10038
10225
  return true;
10039
10226
  } catch {
10040
10227
  return false;
@@ -10047,7 +10234,7 @@ function resolveNotificationStorageDir(ctx, logger) {
10047
10234
  return stateNotifDir;
10048
10235
  }
10049
10236
  if (ctx.workspaceDir) {
10050
- const workspaceDir = (0, import_node_path18.join)(ctx.workspaceDir, NOTIFICATION_DIR_NAME);
10237
+ const workspaceDir = (0, import_node_path17.join)(ctx.workspaceDir, NOTIFICATION_DIR_NAME);
10051
10238
  if (ensureWritableDirectory(workspaceDir)) {
10052
10239
  logger.warn(
10053
10240
  `stateDir \u4E0D\u53EF\u7528\uFF0C\u901A\u77E5\u5DF2\u56DE\u9000\u5230 workspace \u8DEF\u5F84: ${workspaceDir}`
@@ -10062,8 +10249,8 @@ var NotificationStorage = class {
10062
10249
  this.config = config;
10063
10250
  this.logger = logger;
10064
10251
  this.dir = dir;
10065
- this.idIndexDir = (0, import_node_path18.join)(dir, ID_INDEX_DIR_NAME);
10066
- this.contentKeyIndexDir = (0, import_node_path18.join)(dir, CONTENT_KEY_INDEX_DIR_NAME);
10252
+ this.idIndexDir = (0, import_node_path17.join)(dir, ID_INDEX_DIR_NAME);
10253
+ this.contentKeyIndexDir = (0, import_node_path17.join)(dir, CONTENT_KEY_INDEX_DIR_NAME);
10067
10254
  this.resolveDisplayName = resolveDisplayName;
10068
10255
  }
10069
10256
  dir;
@@ -10074,10 +10261,10 @@ var NotificationStorage = class {
10074
10261
  dateWriteChains = /* @__PURE__ */ new Map();
10075
10262
  resolveDisplayName;
10076
10263
  async init() {
10077
- (0, import_node_fs22.mkdirSync)(this.dir, { recursive: true });
10078
- (0, import_node_fs22.mkdirSync)(this.idIndexDir, { recursive: true });
10079
- (0, import_node_fs22.rmSync)(this.contentKeyIndexDir, { recursive: true, force: true });
10080
- (0, import_node_fs22.mkdirSync)(this.contentKeyIndexDir, { recursive: true });
10264
+ (0, import_node_fs21.mkdirSync)(this.dir, { recursive: true });
10265
+ (0, import_node_fs21.mkdirSync)(this.idIndexDir, { recursive: true });
10266
+ (0, import_node_fs21.rmSync)(this.contentKeyIndexDir, { recursive: true, force: true });
10267
+ (0, import_node_fs21.mkdirSync)(this.contentKeyIndexDir, { recursive: true });
10081
10268
  }
10082
10269
  async ingest(items) {
10083
10270
  const result = {
@@ -10116,7 +10303,7 @@ var NotificationStorage = class {
10116
10303
  return { kind: "invalid" };
10117
10304
  }
10118
10305
  const dateKey = this.formatDate(ts);
10119
- const filePath = (0, import_node_path18.join)(this.dir, `${dateKey}.json`);
10306
+ const filePath = (0, import_node_path17.join)(this.dir, `${dateKey}.json`);
10120
10307
  const normalizedId = typeof n.id === "string" ? n.id.trim() : "";
10121
10308
  const entry = this.buildStoredNotification(n);
10122
10309
  return this.withDateWriteLock(dateKey, async () => {
@@ -10133,7 +10320,7 @@ var NotificationStorage = class {
10133
10320
  };
10134
10321
  const arr = this.readStoredNotifications(filePath);
10135
10322
  arr.push(storedEntry);
10136
- (0, import_node_fs22.writeFileSync)(filePath, JSON.stringify(arr, null, 2), "utf-8");
10323
+ (0, import_node_fs21.writeFileSync)(filePath, JSON.stringify(arr, null, 2), "utf-8");
10137
10324
  if (normalizedId) {
10138
10325
  this.recordNotificationId(dateKey, normalizedId);
10139
10326
  }
@@ -10167,7 +10354,7 @@ var NotificationStorage = class {
10167
10354
  return `${year}-${month}-${day}`;
10168
10355
  }
10169
10356
  getIdIndexPath(dateKey) {
10170
- return (0, import_node_path18.join)(this.idIndexDir, `${dateKey}.ids`);
10357
+ return (0, import_node_path17.join)(this.idIndexDir, `${dateKey}.ids`);
10171
10358
  }
10172
10359
  getIdSet(dateKey) {
10173
10360
  const cached = this.idCache.get(dateKey);
@@ -10176,8 +10363,8 @@ var NotificationStorage = class {
10176
10363
  }
10177
10364
  const idPath = this.getIdIndexPath(dateKey);
10178
10365
  const ids = /* @__PURE__ */ new Set();
10179
- if ((0, import_node_fs22.existsSync)(idPath)) {
10180
- const lines = (0, import_node_fs22.readFileSync)(idPath, "utf-8").split(/\r?\n/);
10366
+ if ((0, import_node_fs21.existsSync)(idPath)) {
10367
+ const lines = (0, import_node_fs21.readFileSync)(idPath, "utf-8").split(/\r?\n/);
10181
10368
  for (const line of lines) {
10182
10369
  const id = line.trim();
10183
10370
  if (id) {
@@ -10192,7 +10379,7 @@ var NotificationStorage = class {
10192
10379
  return this.getIdSet(dateKey).has(id);
10193
10380
  }
10194
10381
  getContentKeyIndexPath(dateKey) {
10195
- return (0, import_node_path18.join)(this.contentKeyIndexDir, `${dateKey}.keys`);
10382
+ return (0, import_node_path17.join)(this.contentKeyIndexDir, `${dateKey}.keys`);
10196
10383
  }
10197
10384
  getContentKeySet(dateKey, filePath) {
10198
10385
  const cached = this.contentKeyCache.get(dateKey);
@@ -10201,16 +10388,16 @@ var NotificationStorage = class {
10201
10388
  }
10202
10389
  const keyPath = this.getContentKeyIndexPath(dateKey);
10203
10390
  const keys = /* @__PURE__ */ new Set();
10204
- if ((0, import_node_fs22.existsSync)(filePath)) {
10391
+ if ((0, import_node_fs21.existsSync)(filePath)) {
10205
10392
  for (const item of this.readStoredNotifications(filePath)) {
10206
10393
  keys.add(this.buildNotificationContentKey(item));
10207
10394
  }
10208
10395
  }
10209
10396
  if (keys.size > 0) {
10210
- (0, import_node_fs22.writeFileSync)(keyPath, `${Array.from(keys).join("\n")}
10397
+ (0, import_node_fs21.writeFileSync)(keyPath, `${Array.from(keys).join("\n")}
10211
10398
  `, "utf-8");
10212
- } else if ((0, import_node_fs22.existsSync)(keyPath)) {
10213
- (0, import_node_fs22.rmSync)(keyPath, { force: true });
10399
+ } else if ((0, import_node_fs21.existsSync)(keyPath)) {
10400
+ (0, import_node_fs21.rmSync)(keyPath, { force: true });
10214
10401
  }
10215
10402
  this.contentKeyCache.set(dateKey, keys);
10216
10403
  return keys;
@@ -10225,7 +10412,7 @@ var NotificationStorage = class {
10225
10412
  if (ids.has(id)) {
10226
10413
  return;
10227
10414
  }
10228
- (0, import_node_fs22.appendFileSync)(this.getIdIndexPath(dateKey), `${id}
10415
+ (0, import_node_fs21.appendFileSync)(this.getIdIndexPath(dateKey), `${id}
10229
10416
  `, "utf-8");
10230
10417
  ids.add(id);
10231
10418
  }
@@ -10235,7 +10422,7 @@ var NotificationStorage = class {
10235
10422
  if (keys.has(key)) {
10236
10423
  return;
10237
10424
  }
10238
- (0, import_node_fs22.appendFileSync)(this.getContentKeyIndexPath(dateKey), `${key}
10425
+ (0, import_node_fs21.appendFileSync)(this.getContentKeyIndexPath(dateKey), `${key}
10239
10426
  `, "utf-8");
10240
10427
  keys.add(key);
10241
10428
  }
@@ -10243,11 +10430,11 @@ var NotificationStorage = class {
10243
10430
  return (0, import_node_crypto2.createHash)("sha256").update(entry.appName).update("").update(entry.title).update("").update(entry.content).update("").update(entry.timestamp).digest("hex");
10244
10431
  }
10245
10432
  readStoredNotifications(filePath) {
10246
- if (!(0, import_node_fs22.existsSync)(filePath)) {
10433
+ if (!(0, import_node_fs21.existsSync)(filePath)) {
10247
10434
  return [];
10248
10435
  }
10249
10436
  try {
10250
- const parsed = JSON.parse((0, import_node_fs22.readFileSync)(filePath, "utf-8"));
10437
+ const parsed = JSON.parse((0, import_node_fs21.readFileSync)(filePath, "utf-8"));
10251
10438
  return Array.isArray(parsed) ? parsed : [];
10252
10439
  } catch {
10253
10440
  return [];
@@ -10287,14 +10474,14 @@ var NotificationStorage = class {
10287
10474
  const dateFilePattern = /^(\d{4}-\d{2}-\d{2})\.(json|md)$/;
10288
10475
  const dateDirPattern = /^\d{4}-\d{2}-\d{2}$/;
10289
10476
  try {
10290
- for (const entry of (0, import_node_fs22.readdirSync)(this.dir, { withFileTypes: true })) {
10477
+ for (const entry of (0, import_node_fs21.readdirSync)(this.dir, { withFileTypes: true })) {
10291
10478
  if (entry.isFile()) {
10292
10479
  const match = dateFilePattern.exec(entry.name);
10293
10480
  if (match && match[1] < cutoffDate) {
10294
- (0, import_node_fs22.rmSync)((0, import_node_path18.join)(this.dir, entry.name), { force: true });
10481
+ (0, import_node_fs21.rmSync)((0, import_node_path17.join)(this.dir, entry.name), { force: true });
10295
10482
  }
10296
10483
  } else if (entry.isDirectory() && dateDirPattern.test(entry.name) && entry.name < cutoffDate) {
10297
- (0, import_node_fs22.rmSync)((0, import_node_path18.join)(this.dir, entry.name), { recursive: true, force: true });
10484
+ (0, import_node_fs21.rmSync)((0, import_node_path17.join)(this.dir, entry.name), { recursive: true, force: true });
10298
10485
  }
10299
10486
  }
10300
10487
  } catch {
@@ -10303,11 +10490,11 @@ var NotificationStorage = class {
10303
10490
  /** Remove expired .ids index files */
10304
10491
  pruneIdIndex(cutoffDate) {
10305
10492
  try {
10306
- for (const entry of (0, import_node_fs22.readdirSync)(this.idIndexDir, { withFileTypes: true })) {
10493
+ for (const entry of (0, import_node_fs21.readdirSync)(this.idIndexDir, { withFileTypes: true })) {
10307
10494
  if (!entry.isFile()) continue;
10308
10495
  const match = /^(\d{4}-\d{2}-\d{2})\.ids$/.exec(entry.name);
10309
10496
  if (match && match[1] < cutoffDate) {
10310
- (0, import_node_fs22.rmSync)((0, import_node_path18.join)(this.idIndexDir, entry.name), { force: true });
10497
+ (0, import_node_fs21.rmSync)((0, import_node_path17.join)(this.idIndexDir, entry.name), { force: true });
10311
10498
  this.idCache.delete(match[1]);
10312
10499
  }
10313
10500
  }
@@ -10316,11 +10503,11 @@ var NotificationStorage = class {
10316
10503
  }
10317
10504
  pruneContentKeyIndex(cutoffDate) {
10318
10505
  try {
10319
- for (const entry of (0, import_node_fs22.readdirSync)(this.contentKeyIndexDir, { withFileTypes: true })) {
10506
+ for (const entry of (0, import_node_fs21.readdirSync)(this.contentKeyIndexDir, { withFileTypes: true })) {
10320
10507
  if (!entry.isFile()) continue;
10321
10508
  const match = /^(\d{4}-\d{2}-\d{2})\.keys$/.exec(entry.name);
10322
10509
  if (match && match[1] < cutoffDate) {
10323
- (0, import_node_fs22.rmSync)((0, import_node_path18.join)(this.contentKeyIndexDir, entry.name), { force: true });
10510
+ (0, import_node_fs21.rmSync)((0, import_node_path17.join)(this.contentKeyIndexDir, entry.name), { force: true });
10324
10511
  this.contentKeyCache.delete(match[1]);
10325
10512
  }
10326
10513
  }
@@ -10335,8 +10522,8 @@ var NotificationStorage = class {
10335
10522
  };
10336
10523
 
10337
10524
  // src/recording/storage.ts
10338
- var import_node_fs23 = require("fs");
10339
- var import_node_path19 = require("path");
10525
+ var import_node_fs22 = require("fs");
10526
+ var import_node_path18 = require("path");
10340
10527
 
10341
10528
  // src/recording/state-machine.ts
10342
10529
  var VALID_TRANSITIONS = /* @__PURE__ */ new Map([
@@ -10398,7 +10585,7 @@ function stripMarkdownFence(markdown) {
10398
10585
  }
10399
10586
  function deriveTitleFromTranscriptPath(transcriptFile, recordingId) {
10400
10587
  if (!transcriptFile) return void 0;
10401
- const name = (0, import_node_path19.basename)(transcriptFile, ".md");
10588
+ const name = (0, import_node_path18.basename)(transcriptFile, ".md");
10402
10589
  const prefix = `${recordingId}_`;
10403
10590
  if (name.startsWith(prefix)) {
10404
10591
  const derived = name.slice(prefix.length).trim();
@@ -10426,22 +10613,22 @@ function extractTranscriptContent(markdown) {
10426
10613
  return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
10427
10614
  }
10428
10615
  function resolveRecordingStorageDir(ctx, logger) {
10429
- const stateRecDir = (0, import_node_path19.join)(
10616
+ const stateRecDir = (0, import_node_path18.join)(
10430
10617
  ctx.stateDir,
10431
10618
  "plugins",
10432
10619
  "phone-notifications",
10433
10620
  RECORDINGS_DIR
10434
10621
  );
10435
10622
  try {
10436
- (0, import_node_fs23.mkdirSync)(stateRecDir, { recursive: true });
10623
+ (0, import_node_fs22.mkdirSync)(stateRecDir, { recursive: true });
10437
10624
  logger.info(`\u5F55\u97F3\u5C06\u5199\u5165 stateDir \u8DEF\u5F84: ${stateRecDir}`);
10438
10625
  return stateRecDir;
10439
10626
  } catch {
10440
10627
  }
10441
10628
  if (ctx.workspaceDir) {
10442
- const wsRecDir = (0, import_node_path19.join)(ctx.workspaceDir, RECORDINGS_DIR);
10629
+ const wsRecDir = (0, import_node_path18.join)(ctx.workspaceDir, RECORDINGS_DIR);
10443
10630
  try {
10444
- (0, import_node_fs23.mkdirSync)(wsRecDir, { recursive: true });
10631
+ (0, import_node_fs22.mkdirSync)(wsRecDir, { recursive: true });
10445
10632
  logger.warn(`stateDir \u4E0D\u53EF\u7528\uFF0C\u5F55\u97F3\u5DF2\u56DE\u9000\u5230 workspace \u8DEF\u5F84: ${wsRecDir}`);
10446
10633
  return wsRecDir;
10447
10634
  } catch {
@@ -10453,11 +10640,11 @@ var RecordingStorage = class {
10453
10640
  constructor(dir, logger) {
10454
10641
  this.logger = logger;
10455
10642
  this.dir = dir;
10456
- this.audioDir = (0, import_node_path19.join)(dir, AUDIO_DIR);
10457
- this.transcriptDataDir = (0, import_node_path19.join)(dir, TRANSCRIPT_DATA_DIR);
10458
- this.transcriptsDir = (0, import_node_path19.join)(dir, TRANSCRIPTS_DIR);
10459
- this.summariesDir = (0, import_node_path19.join)(dir, SUMMARIES_DIR);
10460
- this.indexPath = (0, import_node_path19.join)(dir, INDEX_FILE);
10643
+ this.audioDir = (0, import_node_path18.join)(dir, AUDIO_DIR);
10644
+ this.transcriptDataDir = (0, import_node_path18.join)(dir, TRANSCRIPT_DATA_DIR);
10645
+ this.transcriptsDir = (0, import_node_path18.join)(dir, TRANSCRIPTS_DIR);
10646
+ this.summariesDir = (0, import_node_path18.join)(dir, SUMMARIES_DIR);
10647
+ this.indexPath = (0, import_node_path18.join)(dir, INDEX_FILE);
10461
10648
  }
10462
10649
  dir;
10463
10650
  audioDir;
@@ -10468,10 +10655,10 @@ var RecordingStorage = class {
10468
10655
  index = { recordings: [] };
10469
10656
  /** 初始化目录结构并加载索引 */
10470
10657
  async init() {
10471
- (0, import_node_fs23.mkdirSync)(this.audioDir, { recursive: true });
10472
- (0, import_node_fs23.mkdirSync)(this.transcriptDataDir, { recursive: true });
10473
- (0, import_node_fs23.mkdirSync)(this.transcriptsDir, { recursive: true });
10474
- (0, import_node_fs23.mkdirSync)(this.summariesDir, { recursive: true });
10658
+ (0, import_node_fs22.mkdirSync)(this.audioDir, { recursive: true });
10659
+ (0, import_node_fs22.mkdirSync)(this.transcriptDataDir, { recursive: true });
10660
+ (0, import_node_fs22.mkdirSync)(this.transcriptsDir, { recursive: true });
10661
+ (0, import_node_fs22.mkdirSync)(this.summariesDir, { recursive: true });
10475
10662
  this.loadIndex();
10476
10663
  this.logger.info(
10477
10664
  `\u5F55\u97F3\u5B58\u50A8\u5DF2\u521D\u59CB\u5316: ${this.dir}\uFF08\u5171 ${this.index.recordings.length} \u6761\u8BB0\u5F55\uFF09`
@@ -10515,13 +10702,13 @@ var RecordingStorage = class {
10515
10702
  return id;
10516
10703
  }
10517
10704
  if (existing.transcriptDataFile) {
10518
- (0, import_node_fs23.rmSync)((0, import_node_path19.join)(this.dir, existing.transcriptDataFile), { force: true });
10705
+ (0, import_node_fs22.rmSync)((0, import_node_path18.join)(this.dir, existing.transcriptDataFile), { force: true });
10519
10706
  }
10520
10707
  if (existing.transcriptFile) {
10521
- (0, import_node_fs23.rmSync)((0, import_node_path19.join)(this.dir, existing.transcriptFile), { force: true });
10708
+ (0, import_node_fs22.rmSync)((0, import_node_path18.join)(this.dir, existing.transcriptFile), { force: true });
10522
10709
  }
10523
10710
  if (existing.summaryFile) {
10524
- (0, import_node_fs23.rmSync)((0, import_node_path19.join)(this.dir, existing.summaryFile), { force: true });
10711
+ (0, import_node_fs22.rmSync)((0, import_node_path18.join)(this.dir, existing.summaryFile), { force: true });
10525
10712
  }
10526
10713
  existing.metadata = metadata;
10527
10714
  existing.status = "syncing_openclaw";
@@ -10590,7 +10777,7 @@ var RecordingStorage = class {
10590
10777
  if (!entry) return;
10591
10778
  const nextTranscriptDataFile = `${TRANSCRIPT_DATA_DIR}/${filename}`;
10592
10779
  if (entry.transcriptDataFile && entry.transcriptDataFile !== nextTranscriptDataFile) {
10593
- (0, import_node_fs23.rmSync)((0, import_node_path19.join)(this.dir, entry.transcriptDataFile), { force: true });
10780
+ (0, import_node_fs22.rmSync)((0, import_node_path18.join)(this.dir, entry.transcriptDataFile), { force: true });
10594
10781
  }
10595
10782
  entry.transcriptDataFile = nextTranscriptDataFile;
10596
10783
  entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -10604,7 +10791,7 @@ var RecordingStorage = class {
10604
10791
  if (!entry) return;
10605
10792
  const nextTranscriptFile = `${TRANSCRIPTS_DIR}/${filename}`;
10606
10793
  if (entry.transcriptFile && entry.transcriptFile !== nextTranscriptFile) {
10607
- (0, import_node_fs23.rmSync)((0, import_node_path19.join)(this.dir, entry.transcriptFile), { force: true });
10794
+ (0, import_node_fs22.rmSync)((0, import_node_path18.join)(this.dir, entry.transcriptFile), { force: true });
10608
10795
  }
10609
10796
  entry.transcriptFile = nextTranscriptFile;
10610
10797
  entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -10618,7 +10805,7 @@ var RecordingStorage = class {
10618
10805
  if (!entry) return;
10619
10806
  const nextSummaryFile = `${SUMMARIES_DIR}/${filename}`;
10620
10807
  if (entry.summaryFile && entry.summaryFile !== nextSummaryFile) {
10621
- (0, import_node_fs23.rmSync)((0, import_node_path19.join)(this.dir, entry.summaryFile), { force: true });
10808
+ (0, import_node_fs22.rmSync)((0, import_node_path18.join)(this.dir, entry.summaryFile), { force: true });
10622
10809
  }
10623
10810
  entry.summaryFile = nextSummaryFile;
10624
10811
  entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -10726,24 +10913,24 @@ var RecordingStorage = class {
10726
10913
  const entry = this.findById(recordingId);
10727
10914
  if (!entry) return false;
10728
10915
  if (entry.audioFile) {
10729
- const audioPath = (0, import_node_path19.join)(this.dir, entry.audioFile);
10730
- (0, import_node_fs23.rmSync)(audioPath, { force: true });
10916
+ const audioPath = (0, import_node_path18.join)(this.dir, entry.audioFile);
10917
+ (0, import_node_fs22.rmSync)(audioPath, { force: true });
10731
10918
  }
10732
10919
  if (entry.srtFile) {
10733
- const srtPath = (0, import_node_path19.join)(this.dir, entry.srtFile);
10734
- (0, import_node_fs23.rmSync)(srtPath, { force: true });
10920
+ const srtPath = (0, import_node_path18.join)(this.dir, entry.srtFile);
10921
+ (0, import_node_fs22.rmSync)(srtPath, { force: true });
10735
10922
  }
10736
10923
  if (entry.transcriptDataFile) {
10737
- const transcriptDataPath = (0, import_node_path19.join)(this.dir, entry.transcriptDataFile);
10738
- (0, import_node_fs23.rmSync)(transcriptDataPath, { force: true });
10924
+ const transcriptDataPath = (0, import_node_path18.join)(this.dir, entry.transcriptDataFile);
10925
+ (0, import_node_fs22.rmSync)(transcriptDataPath, { force: true });
10739
10926
  }
10740
10927
  if (entry.transcriptFile) {
10741
- const transcriptPath = (0, import_node_path19.join)(this.dir, entry.transcriptFile);
10742
- (0, import_node_fs23.rmSync)(transcriptPath, { force: true });
10928
+ const transcriptPath = (0, import_node_path18.join)(this.dir, entry.transcriptFile);
10929
+ (0, import_node_fs22.rmSync)(transcriptPath, { force: true });
10743
10930
  }
10744
10931
  if (entry.summaryFile) {
10745
- const summaryPath = (0, import_node_path19.join)(this.dir, entry.summaryFile);
10746
- (0, import_node_fs23.rmSync)(summaryPath, { force: true });
10932
+ const summaryPath = (0, import_node_path18.join)(this.dir, entry.summaryFile);
10933
+ (0, import_node_fs22.rmSync)(summaryPath, { force: true });
10747
10934
  }
10748
10935
  if (opts?.localOnly) {
10749
10936
  entry.audioFile = void 0;
@@ -10801,34 +10988,34 @@ var RecordingStorage = class {
10801
10988
  * 获取音频文件的绝对路径。ossUrl 用于推断文件扩展名
10802
10989
  */
10803
10990
  getAudioFilePath(recordingId, ossUrl) {
10804
- return (0, import_node_path19.join)(this.audioDir, this.buildAudioFilename(recordingId, ossUrl));
10991
+ return (0, import_node_path18.join)(this.audioDir, this.buildAudioFilename(recordingId, ossUrl));
10805
10992
  }
10806
10993
  /**
10807
10994
  * 获取打点文件的绝对路径
10808
10995
  */
10809
10996
  getSrtFilePath(recordingId) {
10810
- return (0, import_node_path19.join)(this.audioDir, this.buildSrtFilename(recordingId));
10997
+ return (0, import_node_path18.join)(this.audioDir, this.buildSrtFilename(recordingId));
10811
10998
  }
10812
10999
  /**
10813
11000
  * 获取转写 JSON 文件的绝对路径
10814
11001
  */
10815
11002
  getTranscriptDataFilePath(recordingId) {
10816
- return (0, import_node_path19.join)(this.transcriptDataDir, this.buildTranscriptDataFilename(recordingId));
11003
+ return (0, import_node_path18.join)(this.transcriptDataDir, this.buildTranscriptDataFilename(recordingId));
10817
11004
  }
10818
11005
  /**
10819
11006
  * 获取摘要文件的绝对路径
10820
11007
  */
10821
11008
  getSummaryFilePath(recordingId) {
10822
- return (0, import_node_path19.join)(this.summariesDir, this.buildSummaryFilename(recordingId));
11009
+ return (0, import_node_path18.join)(this.summariesDir, this.buildSummaryFilename(recordingId));
10823
11010
  }
10824
11011
  // ─── Persistence ───
10825
11012
  loadIndex() {
10826
- if (!(0, import_node_fs23.existsSync)(this.indexPath)) {
11013
+ if (!(0, import_node_fs22.existsSync)(this.indexPath)) {
10827
11014
  this.index = { recordings: [] };
10828
11015
  return;
10829
11016
  }
10830
11017
  try {
10831
- const raw = JSON.parse((0, import_node_fs23.readFileSync)(this.indexPath, "utf-8"));
11018
+ const raw = JSON.parse((0, import_node_fs22.readFileSync)(this.indexPath, "utf-8"));
10832
11019
  if (raw && Array.isArray(raw.recordings)) {
10833
11020
  let needsRewrite = false;
10834
11021
  const normalized = raw.recordings.filter((entry) => entry && typeof entry === "object").map((entry) => {
@@ -10866,8 +11053,8 @@ var RecordingStorage = class {
10866
11053
  segments: []
10867
11054
  });
10868
11055
  const transcriptDataFilename = this.buildTranscriptDataFilename(compacted.id);
10869
- (0, import_node_fs23.writeFileSync)(
10870
- (0, import_node_path19.join)(this.transcriptDataDir, transcriptDataFilename),
11056
+ (0, import_node_fs22.writeFileSync)(
11057
+ (0, import_node_path18.join)(this.transcriptDataDir, transcriptDataFilename),
10871
11058
  JSON.stringify(transcriptDoc, null, 2),
10872
11059
  "utf-8"
10873
11060
  );
@@ -10879,8 +11066,8 @@ var RecordingStorage = class {
10879
11066
  compacted.summaryFile = entry.summaryFile;
10880
11067
  } else if (typeof entry.summary === "string" && entry.summary.trim()) {
10881
11068
  const summaryFilename = this.buildSummaryFilename(entry.id);
10882
- (0, import_node_fs23.writeFileSync)(
10883
- (0, import_node_path19.join)(this.summariesDir, summaryFilename),
11069
+ (0, import_node_fs22.writeFileSync)(
11070
+ (0, import_node_path18.join)(this.summariesDir, summaryFilename),
10884
11071
  entry.summary.trim(),
10885
11072
  "utf-8"
10886
11073
  );
@@ -10890,8 +11077,8 @@ var RecordingStorage = class {
10890
11077
  const summaryFromDocument = extractTranscriptSummaryFromDocument(transcriptDoc);
10891
11078
  if (summaryFromDocument) {
10892
11079
  const summaryFilename = this.buildSummaryFilename(entry.id);
10893
- (0, import_node_fs23.writeFileSync)(
10894
- (0, import_node_path19.join)(this.summariesDir, summaryFilename),
11080
+ (0, import_node_fs22.writeFileSync)(
11081
+ (0, import_node_path18.join)(this.summariesDir, summaryFilename),
10895
11082
  summaryFromDocument,
10896
11083
  "utf-8"
10897
11084
  );
@@ -10934,7 +11121,7 @@ var RecordingStorage = class {
10934
11121
  }
10935
11122
  readRelativeTextFile(relativePath) {
10936
11123
  try {
10937
- return (0, import_node_fs23.readFileSync)((0, import_node_path19.join)(this.dir, relativePath), "utf-8");
11124
+ return (0, import_node_fs22.readFileSync)((0, import_node_path18.join)(this.dir, relativePath), "utf-8");
10938
11125
  } catch {
10939
11126
  return void 0;
10940
11127
  }
@@ -10947,7 +11134,7 @@ var RecordingStorage = class {
10947
11134
  return parseTranscriptDocument(raw);
10948
11135
  }
10949
11136
  saveIndex() {
10950
- (0, import_node_fs23.writeFileSync)(
11137
+ (0, import_node_fs22.writeFileSync)(
10951
11138
  this.indexPath,
10952
11139
  JSON.stringify(this.index, null, 2),
10953
11140
  "utf-8"
@@ -10961,8 +11148,8 @@ var RecordingStorage = class {
10961
11148
  init_transcript_document();
10962
11149
 
10963
11150
  // src/recording/downloader.ts
10964
- var import_node_fs24 = require("fs");
10965
- var import_node_path20 = require("path");
11151
+ var import_node_fs23 = require("fs");
11152
+ var import_node_path19 = require("path");
10966
11153
  var import_promises2 = require("stream/promises");
10967
11154
  var import_node_stream = require("stream");
10968
11155
  var DEFAULT_TIMEOUT_MS2 = 5 * 60 * 1e3;
@@ -10972,7 +11159,7 @@ async function downloadFile(url, destPath, logger, options) {
10972
11159
  const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
10973
11160
  const maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;
10974
11161
  const retryBackoffMs = options?.retryBackoffMs ?? DEFAULT_RETRY_BACKOFF_MS;
10975
- (0, import_node_fs24.mkdirSync)((0, import_node_path20.dirname)(destPath), { recursive: true });
11162
+ (0, import_node_fs23.mkdirSync)((0, import_node_path19.dirname)(destPath), { recursive: true });
10976
11163
  let lastError;
10977
11164
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
10978
11165
  const startMs = Date.now();
@@ -10990,11 +11177,11 @@ async function downloadFile(url, destPath, logger, options) {
10990
11177
  if (!res.body) {
10991
11178
  throw new Error("\u54CD\u5E94\u4F53\u4E3A\u7A7A");
10992
11179
  }
10993
- const writeStream = (0, import_node_fs24.createWriteStream)(destPath);
11180
+ const writeStream = (0, import_node_fs23.createWriteStream)(destPath);
10994
11181
  const readable = import_node_stream.Readable.fromWeb(res.body);
10995
11182
  await (0, import_promises2.pipeline)(readable, writeStream);
10996
11183
  const elapsed = Date.now() - startMs;
10997
- const fileSize = (0, import_node_fs24.existsSync)(destPath) ? (0, import_node_fs24.statSync)(destPath).size : 0;
11184
+ const fileSize = (0, import_node_fs23.existsSync)(destPath) ? (0, import_node_fs23.statSync)(destPath).size : 0;
10998
11185
  logger.info(
10999
11186
  `[downloader] \u4E0B\u8F7D\u5B8C\u6210: ${destPath} (${formatBytes(fileSize)}, ${elapsed}ms)`
11000
11187
  );
@@ -11005,7 +11192,7 @@ async function downloadFile(url, destPath, logger, options) {
11005
11192
  } catch (err2) {
11006
11193
  lastError = err2?.message ?? String(err2);
11007
11194
  try {
11008
- if ((0, import_node_fs24.existsSync)(destPath)) (0, import_node_fs24.unlinkSync)(destPath);
11195
+ if ((0, import_node_fs23.existsSync)(destPath)) (0, import_node_fs23.unlinkSync)(destPath);
11009
11196
  } catch {
11010
11197
  }
11011
11198
  const isAbort = err2?.name === "AbortError";
@@ -11321,13 +11508,13 @@ async function triggerTranscription(recordingId, storage, asrConfig, logger, opt
11321
11508
  }
11322
11509
 
11323
11510
  // src/tunnel/service.ts
11324
- var import_node_fs29 = require("fs");
11325
- var import_node_path25 = require("path");
11511
+ var import_node_fs28 = require("fs");
11512
+ var import_node_path24 = require("path");
11326
11513
  init_credentials();
11327
11514
 
11328
11515
  // src/tunnel/relay-client.ts
11329
- var import_node_fs27 = require("fs");
11330
- var import_node_path23 = require("path");
11516
+ var import_node_fs26 = require("fs");
11517
+ var import_node_path22 = require("path");
11331
11518
 
11332
11519
  // node_modules/.pnpm/ws@8.19.0/node_modules/ws/wrapper.mjs
11333
11520
  var import_stream = __toESM(require_stream(), 1);
@@ -11371,8 +11558,8 @@ var RelayClient = class {
11371
11558
  lastDisconnectReason
11372
11559
  };
11373
11560
  try {
11374
- (0, import_node_fs27.mkdirSync)((0, import_node_path23.dirname)(this.opts.statusFilePath), { recursive: true });
11375
- (0, import_node_fs27.writeFileSync)(this.opts.statusFilePath, JSON.stringify(info, null, 2));
11561
+ (0, import_node_fs26.mkdirSync)((0, import_node_path22.dirname)(this.opts.statusFilePath), { recursive: true });
11562
+ (0, import_node_fs26.writeFileSync)(this.opts.statusFilePath, JSON.stringify(info, null, 2));
11376
11563
  } catch {
11377
11564
  }
11378
11565
  }
@@ -11754,8 +11941,8 @@ init_host();
11754
11941
 
11755
11942
  // src/tunnel/device-identity.ts
11756
11943
  var import_node_crypto3 = __toESM(require("crypto"), 1);
11757
- var import_node_fs28 = __toESM(require("fs"), 1);
11758
- var import_node_path24 = __toESM(require("path"), 1);
11944
+ var import_node_fs27 = __toESM(require("fs"), 1);
11945
+ var import_node_path23 = __toESM(require("path"), 1);
11759
11946
  init_host();
11760
11947
  var ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
11761
11948
  function base64UrlEncode(buf) {
@@ -11800,10 +11987,10 @@ function resolveClientStateDir(stateDir) {
11800
11987
  return stateDir ?? resolveStateDir();
11801
11988
  }
11802
11989
  function ensureDir(filePath) {
11803
- import_node_fs28.default.mkdirSync(import_node_path24.default.dirname(filePath), { recursive: true });
11990
+ import_node_fs27.default.mkdirSync(import_node_path23.default.dirname(filePath), { recursive: true });
11804
11991
  }
11805
11992
  function resolveIdentityPath(stateDir) {
11806
- return import_node_path24.default.join(stateDir, "identity", "device.json");
11993
+ return import_node_path23.default.join(stateDir, "identity", "device.json");
11807
11994
  }
11808
11995
  function normalizeDeviceAuthRole(role) {
11809
11996
  return role.trim();
@@ -11819,12 +12006,12 @@ function normalizeDeviceAuthScopes(scopes) {
11819
12006
  return [...out].sort();
11820
12007
  }
11821
12008
  function resolveDeviceAuthPath(stateDir) {
11822
- return import_node_path24.default.join(stateDir, "identity", "device-auth.json");
12009
+ return import_node_path23.default.join(stateDir, "identity", "device-auth.json");
11823
12010
  }
11824
12011
  function readDeviceAuthStore(filePath) {
11825
12012
  try {
11826
- if (!import_node_fs28.default.existsSync(filePath)) return null;
11827
- const raw = import_node_fs28.default.readFileSync(filePath, "utf8");
12013
+ if (!import_node_fs27.default.existsSync(filePath)) return null;
12014
+ const raw = import_node_fs27.default.readFileSync(filePath, "utf8");
11828
12015
  const parsed = JSON.parse(raw);
11829
12016
  if (parsed?.version !== 1 || typeof parsed.deviceId !== "string") return null;
11830
12017
  if (!parsed.tokens || typeof parsed.tokens !== "object") return null;
@@ -11835,12 +12022,12 @@ function readDeviceAuthStore(filePath) {
11835
12022
  }
11836
12023
  function writeDeviceAuthStore(filePath, store) {
11837
12024
  ensureDir(filePath);
11838
- import_node_fs28.default.writeFileSync(filePath, `${JSON.stringify(store, null, 2)}
12025
+ import_node_fs27.default.writeFileSync(filePath, `${JSON.stringify(store, null, 2)}
11839
12026
  `, {
11840
12027
  mode: 384
11841
12028
  });
11842
12029
  try {
11843
- import_node_fs28.default.chmodSync(filePath, 384);
12030
+ import_node_fs27.default.chmodSync(filePath, 384);
11844
12031
  } catch {
11845
12032
  }
11846
12033
  }
@@ -11887,8 +12074,8 @@ function clearDeviceAuthToken(params) {
11887
12074
  function loadOrCreateDeviceIdentity(stateDir) {
11888
12075
  const filePath = resolveIdentityPath(stateDir);
11889
12076
  try {
11890
- if (import_node_fs28.default.existsSync(filePath)) {
11891
- const raw = import_node_fs28.default.readFileSync(filePath, "utf8");
12077
+ if (import_node_fs27.default.existsSync(filePath)) {
12078
+ const raw = import_node_fs27.default.readFileSync(filePath, "utf8");
11892
12079
  const parsed = JSON.parse(raw);
11893
12080
  if (parsed?.version === 1 && typeof parsed.deviceId === "string" && typeof parsed.publicKeyPem === "string" && typeof parsed.privateKeyPem === "string") {
11894
12081
  const derivedId = fingerprintPublicKey(parsed.publicKeyPem);
@@ -11909,14 +12096,14 @@ function loadOrCreateDeviceIdentity(stateDir) {
11909
12096
  publicKeyPem,
11910
12097
  privateKeyPem
11911
12098
  };
11912
- import_node_fs28.default.mkdirSync(import_node_path24.default.dirname(filePath), { recursive: true });
12099
+ import_node_fs27.default.mkdirSync(import_node_path23.default.dirname(filePath), { recursive: true });
11913
12100
  const stored = {
11914
12101
  version: 1,
11915
12102
  ...identity,
11916
12103
  createdAtMs: Date.now()
11917
12104
  };
11918
12105
  ensureDir(filePath);
11919
- import_node_fs28.default.writeFileSync(filePath, `${JSON.stringify(stored, null, 2)}
12106
+ import_node_fs27.default.writeFileSync(filePath, `${JSON.stringify(stored, null, 2)}
11920
12107
  `, {
11921
12108
  mode: 384
11922
12109
  });
@@ -12800,7 +12987,7 @@ function createTunnelService(opts) {
12800
12987
  );
12801
12988
  client.sendRaw(frame);
12802
12989
  }
12803
- function isProcessAlive(pid) {
12990
+ function isProcessAlive2(pid) {
12804
12991
  if (!Number.isInteger(pid) || pid <= 0) return false;
12805
12992
  if (pid === process.pid) return true;
12806
12993
  try {
@@ -12812,7 +12999,7 @@ function createTunnelService(opts) {
12812
12999
  }
12813
13000
  function readLockOwner(filePath) {
12814
13001
  try {
12815
- const parsed = JSON.parse((0, import_node_fs29.readFileSync)(filePath, "utf-8"));
13002
+ const parsed = JSON.parse((0, import_node_fs28.readFileSync)(filePath, "utf-8"));
12816
13003
  return typeof parsed.pid === "number" ? parsed.pid : null;
12817
13004
  } catch {
12818
13005
  return null;
@@ -12825,23 +13012,23 @@ function createTunnelService(opts) {
12825
13012
  lockFd = null;
12826
13013
  if (fd !== null) {
12827
13014
  try {
12828
- (0, import_node_fs29.closeSync)(fd);
13015
+ (0, import_node_fs28.closeSync)(fd);
12829
13016
  } catch {
12830
13017
  }
12831
13018
  }
12832
13019
  if (filePath) {
12833
13020
  try {
12834
- (0, import_node_fs29.unlinkSync)(filePath);
13021
+ (0, import_node_fs28.unlinkSync)(filePath);
12835
13022
  } catch {
12836
13023
  }
12837
13024
  }
12838
13025
  }
12839
13026
  function acquireLock(filePath) {
12840
- (0, import_node_fs29.mkdirSync)((0, import_node_path25.dirname)(filePath), { recursive: true });
13027
+ (0, import_node_fs28.mkdirSync)((0, import_node_path24.dirname)(filePath), { recursive: true });
12841
13028
  for (let attempt = 0; attempt < 2; attempt++) {
12842
13029
  try {
12843
- const fd = (0, import_node_fs29.openSync)(filePath, "wx", 384);
12844
- (0, import_node_fs29.writeFileSync)(
13030
+ const fd = (0, import_node_fs28.openSync)(filePath, "wx", 384);
13031
+ (0, import_node_fs28.writeFileSync)(
12845
13032
  fd,
12846
13033
  JSON.stringify({
12847
13034
  pid: process.pid,
@@ -12860,12 +13047,12 @@ function createTunnelService(opts) {
12860
13047
  return false;
12861
13048
  }
12862
13049
  const ownerPid = readLockOwner(filePath);
12863
- if (ownerPid && !isProcessAlive(ownerPid)) {
13050
+ if (ownerPid && !isProcessAlive2(ownerPid)) {
12864
13051
  opts.logger.warn(
12865
13052
  `Relay tunnel: removing stale local lock owned by dead pid=${ownerPid}`
12866
13053
  );
12867
13054
  try {
12868
- (0, import_node_fs29.unlinkSync)(filePath);
13055
+ (0, import_node_fs28.unlinkSync)(filePath);
12869
13056
  } catch {
12870
13057
  }
12871
13058
  continue;
@@ -12910,12 +13097,12 @@ function createTunnelService(opts) {
12910
13097
  return;
12911
13098
  }
12912
13099
  const { logger } = opts;
12913
- const baseStateDir = (0, import_node_path25.join)(ctx.stateDir, "plugins", "phone-notifications");
13100
+ const baseStateDir = (0, import_node_path24.join)(ctx.stateDir, "plugins", "phone-notifications");
12914
13101
  logger.info(
12915
13102
  `Relay tunnel: starting (pid=${process.pid}, url=${opts.tunnelUrl}, heartbeat=${opts.heartbeatSec ?? DEFAULT_HEARTBEAT_SEC}s, backoff=${opts.reconnectBackoffMs ?? DEFAULT_RECONNECT_BACKOFF_MS}ms, gateway=${opts.gatewayBaseUrl}, hasGatewayToken=${!!opts.gatewayToken}, hasGatewayPwd=${!!opts.gatewayPassword})`
12916
13103
  );
12917
- const statusFilePath = (0, import_node_path25.join)(baseStateDir, "tunnel-status.json");
12918
- const lockPath = (0, import_node_path25.join)(baseStateDir, "relay-tunnel.lock");
13104
+ const statusFilePath = (0, import_node_path24.join)(baseStateDir, "tunnel-status.json");
13105
+ const lockPath = (0, import_node_path24.join)(baseStateDir, "relay-tunnel.lock");
12919
13106
  if (!acquireLock(lockPath)) {
12920
13107
  return;
12921
13108
  }
@@ -12980,25 +13167,6 @@ function createTunnelService(opts) {
12980
13167
 
12981
13168
  // src/plugin/lifecycle.ts
12982
13169
  init_env();
12983
-
12984
- // src/plugin/shared.ts
12985
- function readBody(req) {
12986
- return new Promise((resolve, reject) => {
12987
- const chunks = [];
12988
- req.on("data", (chunk) => chunks.push(chunk));
12989
- req.on("end", () => resolve(Buffer.concat(chunks).toString()));
12990
- req.on("error", reject);
12991
- });
12992
- }
12993
- function trimToUndefined2(value) {
12994
- if (typeof value !== "string") {
12995
- return void 0;
12996
- }
12997
- const trimmed = value.trim();
12998
- return trimmed || void 0;
12999
- }
13000
-
13001
- // src/plugin/lifecycle.ts
13002
13170
  function registerStorageLifecycle(deps) {
13003
13171
  const {
13004
13172
  api,
@@ -13055,7 +13223,7 @@ function readHostGatewayConfig(params) {
13055
13223
  let configData;
13056
13224
  if (configPath) {
13057
13225
  try {
13058
- configData = JSON.parse((0, import_node_fs30.readFileSync)(configPath, "utf-8"));
13226
+ configData = JSON.parse((0, import_node_fs29.readFileSync)(configPath, "utf-8"));
13059
13227
  } catch (err2) {
13060
13228
  if (err2?.code !== "ENOENT") {
13061
13229
  params.logger.warn(
@@ -13732,7 +13900,7 @@ var index_default = {
13732
13900
  let broadcastFn = null;
13733
13901
  let autoUpdateLifecycle = null;
13734
13902
  let tunnelService = null;
13735
- const openclawDir = api.runtime.state.resolveStateDir();
13903
+ const openclawDir = resolvePluginStateDir(api);
13736
13904
  const logger = openclawDir ? new PluginFileLogger(api.logger, openclawDir) : createVersionAwareLogger(api.logger);
13737
13905
  const lightRuleCtx = {
13738
13906
  stateDir: openclawDir