@yoooclaw/phone-notifications 1.11.0 → 1.11.2-beta.0

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
@@ -535,20 +535,20 @@ function selectModelByMemory(availableGB, isAppleSilicon = false) {
535
535
  return "tiny";
536
536
  }
537
537
  function resolveModelsDir(dataDir) {
538
- const dir = (0, import_node_path22.join)(dataDir, WHISPER_MODELS_DIR);
539
- (0, import_node_fs26.mkdirSync)(dir, { recursive: true });
538
+ const dir = (0, import_node_path21.join)(dataDir, WHISPER_MODELS_DIR);
539
+ (0, import_node_fs25.mkdirSync)(dir, { recursive: true });
540
540
  return dir;
541
541
  }
542
542
  function resolveBinDir(dataDir) {
543
- const dir = (0, import_node_path22.join)(dataDir, WHISPER_BIN_DIR);
544
- (0, import_node_fs26.mkdirSync)(dir, { recursive: true });
543
+ const dir = (0, import_node_path21.join)(dataDir, WHISPER_BIN_DIR);
544
+ (0, import_node_fs25.mkdirSync)(dir, { recursive: true });
545
545
  return dir;
546
546
  }
547
547
  function isModelDownloaded(modelsDir, modelSize) {
548
- const modelPath = (0, import_node_path22.join)(modelsDir, MODEL_FILENAMES[modelSize]);
549
- if (!(0, import_node_fs26.existsSync)(modelPath)) return false;
548
+ const modelPath = (0, import_node_path21.join)(modelsDir, MODEL_FILENAMES[modelSize]);
549
+ if (!(0, import_node_fs25.existsSync)(modelPath)) return false;
550
550
  try {
551
- const stat = (0, import_node_fs26.statSync)(modelPath);
551
+ const stat = (0, import_node_fs25.statSync)(modelPath);
552
552
  const expectedSize = MODEL_DISK_SIZES[modelSize];
553
553
  return stat.size >= expectedSize * 0.8;
554
554
  } catch {
@@ -557,7 +557,7 @@ function isModelDownloaded(modelsDir, modelSize) {
557
557
  }
558
558
  async function downloadModel(modelsDir, modelSize, logger, modelSource, mirrorUrl) {
559
559
  const filename = MODEL_FILENAMES[modelSize];
560
- const modelPath = (0, import_node_path22.join)(modelsDir, filename);
560
+ const modelPath = (0, import_node_path21.join)(modelsDir, filename);
561
561
  if (isModelDownloaded(modelsDir, modelSize)) {
562
562
  logger.info(`[whisper-local] \u6A21\u578B\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u4E0B\u8F7D: ${filename}`);
563
563
  return { ok: true, modelPath };
@@ -623,7 +623,7 @@ async function probeUrl(url, logger) {
623
623
  async function downloadFromUrl(url, modelPath, logger) {
624
624
  const tmpPath = `${modelPath}.downloading`;
625
625
  try {
626
- (0, import_node_fs26.mkdirSync)((0, import_node_path22.dirname)(modelPath), { recursive: true });
626
+ (0, import_node_fs25.mkdirSync)((0, import_node_path21.dirname)(modelPath), { recursive: true });
627
627
  const controller = new AbortController();
628
628
  const timer = setTimeout(() => controller.abort(), 30 * 60 * 1e3);
629
629
  try {
@@ -639,7 +639,7 @@ async function downloadFromUrl(url, modelPath, logger) {
639
639
  return { ok: false, modelPath, error: "\u6A21\u578B\u4E0B\u8F7D\u5931\u8D25: \u54CD\u5E94\u4F53\u4E3A\u7A7A" };
640
640
  }
641
641
  const contentLength = Number(res.headers.get("content-length") ?? 0);
642
- const writeStream = (0, import_node_fs26.createWriteStream)(tmpPath);
642
+ const writeStream = (0, import_node_fs25.createWriteStream)(tmpPath);
643
643
  const readable = import_node_stream2.Readable.fromWeb(res.body);
644
644
  let downloaded = 0;
645
645
  let lastLogPercent = 0;
@@ -661,14 +661,14 @@ async function downloadFromUrl(url, modelPath, logger) {
661
661
  }
662
662
  const { renameSync: renameSync2 } = await import("fs");
663
663
  renameSync2(tmpPath, modelPath);
664
- const fileSize = (0, import_node_fs26.statSync)(modelPath).size;
664
+ const fileSize = (0, import_node_fs25.statSync)(modelPath).size;
665
665
  logger.info(
666
- `[whisper-local] \u6A21\u578B\u4E0B\u8F7D\u5B8C\u6210: ${(0, import_node_path22.basename)(modelPath)} (${formatBytes2(fileSize)})`
666
+ `[whisper-local] \u6A21\u578B\u4E0B\u8F7D\u5B8C\u6210: ${(0, import_node_path21.basename)(modelPath)} (${formatBytes2(fileSize)})`
667
667
  );
668
668
  return { ok: true, modelPath };
669
669
  } catch (err2) {
670
670
  try {
671
- if ((0, import_node_fs26.existsSync)(tmpPath)) (0, import_node_fs26.unlinkSync)(tmpPath);
671
+ if ((0, import_node_fs25.existsSync)(tmpPath)) (0, import_node_fs25.unlinkSync)(tmpPath);
672
672
  } catch {
673
673
  }
674
674
  const msg = err2?.name === "AbortError" ? "\u6A21\u578B\u4E0B\u8F7D\u8D85\u65F6\uFF0830 \u5206\u949F\uFF09" : err2?.message ?? String(err2);
@@ -680,8 +680,8 @@ function findWhisperBinary(dataDir, logger) {
680
680
  const binDir = resolveBinDir(dataDir);
681
681
  const binNames = (0, import_node_os4.platform)() === "win32" ? ["whisper-cli.exe", "whisper.exe", "main.exe"] : ["whisper-cli", "whisper", "main"];
682
682
  for (const name of binNames) {
683
- const binPath = (0, import_node_path22.join)(binDir, name);
684
- if ((0, import_node_fs26.existsSync)(binPath)) {
683
+ const binPath = (0, import_node_path21.join)(binDir, name);
684
+ if ((0, import_node_fs25.existsSync)(binPath)) {
685
685
  logger.info(`[whisper-local] \u627E\u5230\u672C\u5730\u4E8C\u8FDB\u5236: ${binPath}`);
686
686
  return binPath;
687
687
  }
@@ -732,7 +732,7 @@ async function transcribeWithWhisperLocal(audioFilePath, localConfig, dataDir, l
732
732
  return { ok: false, error: `\u6A21\u578B\u4E0B\u8F7D\u5931\u8D25: ${downloadResult.error}` };
733
733
  }
734
734
  }
735
- const modelPath = (0, import_node_path22.join)(modelsDir, MODEL_FILENAMES[modelSize]);
735
+ const modelPath = (0, import_node_path21.join)(modelsDir, MODEL_FILENAMES[modelSize]);
736
736
  let inputPath = audioFilePath;
737
737
  let tmpWavPath = null;
738
738
  const actualFmt = detectAudioFormat(audioFilePath);
@@ -784,10 +784,10 @@ async function transcribeWithWhisperLocal(audioFilePath, localConfig, dataDir, l
784
784
  logger.info(`[whisper-local] \u8F6C\u5199\u8017\u65F6: ${Math.round(elapsed / 1e3)}s`);
785
785
  const jsonPath = inputPath + ".json";
786
786
  let jsonContent;
787
- if ((0, import_node_fs26.existsSync)(jsonPath)) {
788
- jsonContent = (0, import_node_fs26.readFileSync)(jsonPath, "utf-8");
787
+ if ((0, import_node_fs25.existsSync)(jsonPath)) {
788
+ jsonContent = (0, import_node_fs25.readFileSync)(jsonPath, "utf-8");
789
789
  try {
790
- (0, import_node_fs26.unlinkSync)(jsonPath);
790
+ (0, import_node_fs25.unlinkSync)(jsonPath);
791
791
  } catch {
792
792
  }
793
793
  } else {
@@ -865,10 +865,10 @@ function getPhysicalCoreCount() {
865
865
  }
866
866
  function detectAudioFormat(filePath) {
867
867
  try {
868
- const fd = (0, import_node_fs26.openSync)(filePath, "r");
868
+ const fd = (0, import_node_fs25.openSync)(filePath, "r");
869
869
  const buf = Buffer.alloc(12);
870
- (0, import_node_fs26.readSync)(fd, buf, 0, 12, 0);
871
- (0, import_node_fs26.closeSync)(fd);
870
+ (0, import_node_fs25.readSync)(fd, buf, 0, 12, 0);
871
+ (0, import_node_fs25.closeSync)(fd);
872
872
  const header = buf.toString("ascii", 0, 4);
873
873
  const header8 = buf.toString("ascii", 0, 8);
874
874
  if (header === "RIFF" && buf.toString("ascii", 8, 12) === "WAVE") return ".wav";
@@ -903,7 +903,7 @@ function convertToWav(inputPath, outputPath, actualFmt, logger) {
903
903
  timeout: 12e4,
904
904
  stdio: ["pipe", "pipe", "pipe"]
905
905
  });
906
- if (ffmpegResult.status === 0 && (0, import_node_fs26.existsSync)(outputPath)) {
906
+ if (ffmpegResult.status === 0 && (0, import_node_fs25.existsSync)(outputPath)) {
907
907
  logger.info(`[whisper-local] ffmpeg \u8F6C\u6362\u5B8C\u6210: ${outputPath}`);
908
908
  return { ok: true };
909
909
  }
@@ -916,7 +916,7 @@ function convertToWav(inputPath, outputPath, actualFmt, logger) {
916
916
  ["--rate", "16000", "--mono", inputPath, outputPath],
917
917
  { encoding: "utf-8", timeout: 12e4, stdio: ["pipe", "pipe", "pipe"] }
918
918
  );
919
- if (opusResult.status === 0 && (0, import_node_fs26.existsSync)(outputPath)) {
919
+ if (opusResult.status === 0 && (0, import_node_fs25.existsSync)(outputPath)) {
920
920
  logger.info(`[whisper-local] opusdec \u8F6C\u6362\u5B8C\u6210: ${outputPath}`);
921
921
  return { ok: true };
922
922
  }
@@ -930,7 +930,7 @@ function convertToWav(inputPath, outputPath, actualFmt, logger) {
930
930
  if (detectedExt && !inputPath.endsWith(detectedExt)) {
931
931
  tmpCopy = inputPath + ".detected" + detectedExt;
932
932
  try {
933
- (0, import_node_fs26.copyFileSync)(inputPath, tmpCopy);
933
+ (0, import_node_fs25.copyFileSync)(inputPath, tmpCopy);
934
934
  actualInputPath = tmpCopy;
935
935
  logger.info(
936
936
  `[whisper-local] \u68C0\u6D4B\u5230\u5B9E\u9645\u683C\u5F0F ${detectedExt}\uFF0C\u4E34\u65F6\u91CD\u547D\u540D`
@@ -954,22 +954,22 @@ function convertToWav(inputPath, outputPath, actualFmt, logger) {
954
954
  timeout: 12e4,
955
955
  stdio: ["pipe", "pipe", "pipe"]
956
956
  });
957
- if (tmpCopy && (0, import_node_fs26.existsSync)(tmpCopy)) {
957
+ if (tmpCopy && (0, import_node_fs25.existsSync)(tmpCopy)) {
958
958
  try {
959
- (0, import_node_fs26.unlinkSync)(tmpCopy);
959
+ (0, import_node_fs25.unlinkSync)(tmpCopy);
960
960
  } catch {
961
961
  }
962
962
  }
963
- if (afResult.status === 0 && (0, import_node_fs26.existsSync)(outputPath)) {
963
+ if (afResult.status === 0 && (0, import_node_fs25.existsSync)(outputPath)) {
964
964
  logger.info(`[whisper-local] afconvert \u8F6C\u6362\u5B8C\u6210: ${outputPath}`);
965
965
  return { ok: true };
966
966
  }
967
967
  const stderr = afResult.stderr?.slice(0, 200) ?? "";
968
968
  return { ok: false, error: `afconvert \u8F6C\u6362\u5931\u8D25 (exit ${afResult.status}): ${stderr}` };
969
969
  } catch (err2) {
970
- if (tmpCopy && (0, import_node_fs26.existsSync)(tmpCopy)) {
970
+ if (tmpCopy && (0, import_node_fs25.existsSync)(tmpCopy)) {
971
971
  try {
972
- (0, import_node_fs26.unlinkSync)(tmpCopy);
972
+ (0, import_node_fs25.unlinkSync)(tmpCopy);
973
973
  } catch {
974
974
  }
975
975
  }
@@ -980,9 +980,9 @@ function convertToWav(inputPath, outputPath, actualFmt, logger) {
980
980
  return { ok: false, error: `\u65E0\u6CD5\u5C06\u97F3\u9891\u8F6C\u6362\u4E3A WAV \u683C\u5F0F\u3002${fmtHint}` };
981
981
  }
982
982
  function cleanupTmpWav(path2) {
983
- if (path2 && (0, import_node_fs26.existsSync)(path2)) {
983
+ if (path2 && (0, import_node_fs25.existsSync)(path2)) {
984
984
  try {
985
- (0, import_node_fs26.unlinkSync)(path2);
985
+ (0, import_node_fs25.unlinkSync)(path2);
986
986
  } catch {
987
987
  }
988
988
  }
@@ -1068,13 +1068,13 @@ function formatBytes2(bytes) {
1068
1068
  if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
1069
1069
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)}GB`;
1070
1070
  }
1071
- var import_node_child_process2, import_node_fs26, import_node_path22, 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;
1071
+ var import_node_child_process2, 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;
1072
1072
  var init_whisper_local = __esm({
1073
1073
  "src/recording/whisper-local.ts"() {
1074
1074
  "use strict";
1075
1075
  import_node_child_process2 = require("child_process");
1076
- import_node_fs26 = require("fs");
1077
- import_node_path22 = require("path");
1076
+ import_node_fs25 = require("fs");
1077
+ import_node_path21 = require("path");
1078
1078
  import_promises3 = require("stream/promises");
1079
1079
  import_node_stream2 = require("stream");
1080
1080
  import_node_os4 = require("os");
@@ -1229,7 +1229,7 @@ async function initializeAsr(config, dataDir, logger) {
1229
1229
  }
1230
1230
  }
1231
1231
  async function transcribeAudio(audioFilePath, config, logger, options = {}) {
1232
- if (!(0, import_node_fs27.existsSync)(audioFilePath)) {
1232
+ if (!(0, import_node_fs26.existsSync)(audioFilePath)) {
1233
1233
  return { ok: false, error: `\u97F3\u9891\u6587\u4EF6\u4E0D\u5B58\u5728: ${audioFilePath}` };
1234
1234
  }
1235
1235
  logger.info(
@@ -1240,6 +1240,7 @@ async function transcribeAudio(audioFilePath, config, logger, options = {}) {
1240
1240
  case "api":
1241
1241
  return await transcribeWithModelProxy(
1242
1242
  options.audioOssUrl,
1243
+ options.audioDurationMs,
1243
1244
  config.api,
1244
1245
  logger
1245
1246
  );
@@ -1339,7 +1340,8 @@ async function runTranscriptionWorkflow(params) {
1339
1340
  logger
1340
1341
  } = params;
1341
1342
  const result = await transcribeAudio(audioFilePath, config, logger, {
1342
- audioOssUrl
1343
+ audioOssUrl,
1344
+ audioDurationMs: Math.max(0, Math.round(durationSec * 1e3))
1343
1345
  });
1344
1346
  if (!result.ok) {
1345
1347
  return { ok: false, error: result.error };
@@ -1373,8 +1375,8 @@ async function runTranscriptionWorkflow(params) {
1373
1375
  createdAt
1374
1376
  );
1375
1377
  const transcriptDataFilename = buildTranscriptDataFilename(recordingId);
1376
- const transcriptDataPath = (0, import_node_path23.join)(transcriptDataDir, transcriptDataFilename);
1377
- (0, import_node_fs27.writeFileSync)(
1378
+ const transcriptDataPath = (0, import_node_path22.join)(transcriptDataDir, transcriptDataFilename);
1379
+ (0, import_node_fs26.writeFileSync)(
1378
1380
  transcriptDataPath,
1379
1381
  JSON.stringify(transcriptData, null, 2),
1380
1382
  "utf-8"
@@ -1382,14 +1384,14 @@ async function runTranscriptionWorkflow(params) {
1382
1384
  logger.info(`[asr] \u8F6C\u5199 JSON \u5DF2\u5199\u5165: ${transcriptDataPath}`);
1383
1385
  const safeSummary = title.replace(/[/\\:*?"<>|]/g, "").trim().slice(0, 20);
1384
1386
  const filename = safeSummary ? `${recordingId}_${safeSummary}.md` : `${recordingId}.md`;
1385
- const filePath = (0, import_node_path23.join)(transcriptsDir, filename);
1386
- (0, import_node_fs27.writeFileSync)(filePath, markdown, "utf-8");
1387
+ const filePath = (0, import_node_path22.join)(transcriptsDir, filename);
1388
+ (0, import_node_fs26.writeFileSync)(filePath, markdown, "utf-8");
1387
1389
  logger.info(`[asr] \u8F6C\u5199\u6587\u672C\u5DF2\u5199\u5165: ${filePath}`);
1388
1390
  let summaryFilename;
1389
1391
  if (summary) {
1390
1392
  summaryFilename = `${recordingId}.md`;
1391
- const summaryFilePath = (0, import_node_path23.join)(summariesDir, summaryFilename);
1392
- (0, import_node_fs27.writeFileSync)(summaryFilePath, summary, "utf-8");
1393
+ const summaryFilePath = (0, import_node_path22.join)(summariesDir, summaryFilename);
1394
+ (0, import_node_fs26.writeFileSync)(summaryFilePath, summary, "utf-8");
1393
1395
  logger.info(`[asr] \u6458\u8981\u6587\u672C\u5DF2\u5199\u5165: ${summaryFilePath}`);
1394
1396
  }
1395
1397
  return {
@@ -1402,7 +1404,7 @@ async function runTranscriptionWorkflow(params) {
1402
1404
  title
1403
1405
  };
1404
1406
  }
1405
- async function transcribeWithModelProxy(audioOssUrl, apiConfig, logger) {
1407
+ async function transcribeWithModelProxy(audioOssUrl, audioDurationMs, apiConfig, logger) {
1406
1408
  const normalizedAudioOssUrl = normalizeOptionalText2(audioOssUrl);
1407
1409
  if (!normalizedAudioOssUrl) {
1408
1410
  return { ok: false, error: "API \u6A21\u5F0F\u7F3A\u5C11 audioOssUrl\uFF0C\u65E0\u6CD5\u8C03\u7528 model-proxy" };
@@ -1468,13 +1470,14 @@ async function transcribeWithModelProxy(audioOssUrl, apiConfig, logger) {
1468
1470
  apiKey,
1469
1471
  taskId,
1470
1472
  initialRequestId: requestId,
1473
+ audioDurationMs,
1471
1474
  apiConfig,
1472
1475
  logger
1473
1476
  });
1474
1477
  }
1475
1478
  async function transcribeWithWhisperLocal2(audioFilePath, config, logger) {
1476
1479
  const { transcribeWithWhisperLocal: runLocal } = await Promise.resolve().then(() => (init_whisper_local(), whisper_local_exports));
1477
- const dataDir = process.env.OPENCLAW_STATE_DIR ?? process.env.QCLAW_STATE_DIR ?? (0, import_node_path23.join)(audioFilePath, "..", "..", "..");
1480
+ const dataDir = process.env.OPENCLAW_STATE_DIR ?? process.env.QCLAW_STATE_DIR ?? (0, import_node_path22.join)(audioFilePath, "..", "..", "..");
1478
1481
  const localConfig = config.local ?? {};
1479
1482
  const result = await runLocal(
1480
1483
  audioFilePath,
@@ -1530,6 +1533,7 @@ async function pollLongRecordingTaskResult(params) {
1530
1533
  apiKey,
1531
1534
  taskId,
1532
1535
  initialRequestId,
1536
+ audioDurationMs,
1533
1537
  apiConfig,
1534
1538
  logger
1535
1539
  } = params;
@@ -1577,7 +1581,13 @@ async function pollLongRecordingTaskResult(params) {
1577
1581
  lastStatus = status;
1578
1582
  }
1579
1583
  if (status === "SUCCEEDED") {
1580
- return buildLongRecordingSuccessResult(taskId, requestId, data, logger);
1584
+ return buildLongRecordingSuccessResult(
1585
+ taskId,
1586
+ requestId,
1587
+ data,
1588
+ audioDurationMs,
1589
+ logger
1590
+ );
1581
1591
  }
1582
1592
  if (LONG_RECORDING_TERMINAL_FAILURE_STATUSES.has(status)) {
1583
1593
  return {
@@ -1600,11 +1610,11 @@ async function pollLongRecordingTaskResult(params) {
1600
1610
  error: `Model Proxy ASR \u8F6E\u8BE2\u8D85\u65F6: taskId=${taskId}, waited=${DEFAULT_LONG_RECORDING_MAX_POLL_ATTEMPTS * DEFAULT_LONG_RECORDING_POLL_INTERVAL_MS}ms`
1601
1611
  };
1602
1612
  }
1603
- function buildLongRecordingSuccessResult(taskId, requestId, data, logger) {
1613
+ function buildLongRecordingSuccessResult(taskId, requestId, data, audioDurationMs, logger) {
1604
1614
  const sourceTextList = normalizeLongRecordingSourceTextList(
1605
1615
  data?.recordResult?.sourceTextList
1606
1616
  );
1607
- const listResult = extractLongRecordingTextFromList(sourceTextList);
1617
+ const listResult = extractLongRecordingTextFromList(sourceTextList, audioDurationMs);
1608
1618
  const sourceText = normalizeOptionalText2(data?.recordResult?.sourceText);
1609
1619
  const summaryText = normalizeOptionalText2(data?.recordResult?.summaryResult) ?? "";
1610
1620
  const title = normalizeOptionalText2(data?.recordResult?.title);
@@ -1701,11 +1711,12 @@ function normalizeLongRecordingSourceTextList(value) {
1701
1711
  return [{
1702
1712
  content,
1703
1713
  speakerId: normalizeOptionalInteger2(record.speakerId),
1704
- startTime: normalizeOptionalNonNegativeNumber(record.startTime)
1714
+ startTime: normalizeOptionalNonNegativeNumber(record.startTime),
1715
+ endTime: normalizeOptionalNonNegativeNumber(record.endTime)
1705
1716
  }];
1706
1717
  });
1707
1718
  }
1708
- function extractLongRecordingTextFromList(items) {
1719
+ function extractLongRecordingTextFromList(items, finalFallbackEndMs) {
1709
1720
  if (items.length === 0) {
1710
1721
  return {
1711
1722
  segments: []
@@ -1713,10 +1724,13 @@ function extractLongRecordingTextFromList(items) {
1713
1724
  }
1714
1725
  const segments = items.map((item, index) => {
1715
1726
  const startMs = item.startTime ?? 0;
1727
+ const explicitEndMs = item.endTime;
1716
1728
  const nextStart = items[index + 1]?.startTime;
1729
+ const fallbackEndMs = index === items.length - 1 ? normalizeOptionalNonNegativeNumber(finalFallbackEndMs) : void 0;
1730
+ const endMs = explicitEndMs ?? nextStart ?? fallbackEndMs ?? startMs;
1717
1731
  return {
1718
1732
  start_ms: startMs,
1719
- end_ms: nextStart ?? startMs,
1733
+ end_ms: Math.max(startMs, endMs),
1720
1734
  text: item.content,
1721
1735
  speaker_id: item.speakerId
1722
1736
  };
@@ -1772,12 +1786,12 @@ function formatTranscriptSegmentText(segment) {
1772
1786
  }
1773
1787
  return text;
1774
1788
  }
1775
- var import_node_fs27, import_node_path23, DEFAULT_LONG_RECORDING_POLL_INTERVAL_MS, DEFAULT_LONG_RECORDING_MAX_POLL_ATTEMPTS, LONG_RECORDING_RUNNING_STATUSES, LONG_RECORDING_TERMINAL_FAILURE_STATUSES;
1789
+ 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;
1776
1790
  var init_asr = __esm({
1777
1791
  "src/recording/asr.ts"() {
1778
1792
  "use strict";
1779
- import_node_fs27 = require("fs");
1780
- import_node_path23 = require("path");
1793
+ import_node_fs26 = require("fs");
1794
+ import_node_path22 = require("path");
1781
1795
  init_credentials();
1782
1796
  init_env();
1783
1797
  init_transcript_document();
@@ -5422,7 +5436,7 @@ function readBuildInjectedVersion() {
5422
5436
  if (false) {
5423
5437
  return void 0;
5424
5438
  }
5425
- const version = "1.11.0".trim();
5439
+ const version = "1.11.2-beta.0".trim();
5426
5440
  return version || void 0;
5427
5441
  }
5428
5442
  function readPluginVersionFromPackageJson() {
@@ -6035,20 +6049,17 @@ function readOptionalString(value) {
6035
6049
  const trimmed = value.trim();
6036
6050
  return trimmed || void 0;
6037
6051
  }
6038
- function isLegacyLightRuleWithoutType(raw) {
6039
- return raw.type === void 0 && readOptionalString(raw.name) !== void 0 && readOptionalString(raw.description) !== void 0 && Array.isArray(raw.segments);
6040
- }
6041
6052
  function readMeta(taskDir) {
6042
6053
  const metaPath = (0, import_node_path3.join)(taskDir, "meta.json");
6043
6054
  if (!(0, import_node_fs4.existsSync)(metaPath)) return null;
6044
6055
  try {
6045
6056
  const raw = JSON.parse((0, import_node_fs4.readFileSync)(metaPath, "utf-8"));
6046
6057
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
6047
- if (raw.type !== "light-rule" && !isLegacyLightRuleWithoutType(raw)) return null;
6058
+ if (raw.type !== "light-rule") return null;
6048
6059
  if (!Array.isArray(raw.segments)) return null;
6049
6060
  const name = readOptionalString(raw.name) ?? (0, import_node_path3.basename)(taskDir);
6050
6061
  const title = readOptionalString(raw.title) ?? name;
6051
- const description = readOptionalString(raw.description) ?? readOptionalString(raw.reason) ?? name;
6062
+ const description = readOptionalString(raw.description) ?? name;
6052
6063
  const createdAt = readOptionalString(raw.createdAt) ?? (0, import_node_fs4.statSync)(metaPath).birthtime.toISOString();
6053
6064
  const enabled = typeof raw.enabled === "boolean" ? raw.enabled : true;
6054
6065
  const repeatTimes = normalizeRepeatTimes({
@@ -6939,12 +6950,13 @@ init_credentials();
6939
6950
  // src/light/sender.ts
6940
6951
  var import_node_crypto = require("crypto");
6941
6952
  init_env();
6942
- async function sendLightEffect(apiKey, segments, logger, repeatInput, reason) {
6953
+ async function sendLightEffect(apiKey, segments, logger, repeatInput, reason, title) {
6943
6954
  const apiUrl = getEnvUrls().lightApiUrl;
6944
6955
  const appKey = "7Q617S1G5WD274JI";
6945
6956
  const templateId = "1990771146010017800";
6957
+ const resolvedTitle = resolveLightTitle(title, reason, segments);
6946
6958
  logger?.info(
6947
- `Light sender: apiUrl=${apiUrl ?? "UNSET"}, appKey=${appKey ? appKey.substring(0, 8) + "\u2026" : "UNSET"}, templateId=${templateId ?? "UNSET"}, apiKey=${apiKey ? apiKey.substring(0, 20) + "\u2026" : "EMPTY"}, segments=${JSON.stringify(segments)}`
6959
+ `Light sender: apiUrl=${apiUrl ?? "UNSET"}, appKey=${appKey ? appKey.substring(0, 8) + "\u2026" : "UNSET"}, templateId=${templateId ?? "UNSET"}, apiKey=${apiKey ? apiKey.substring(0, 20) + "\u2026" : "EMPTY"}, title=${resolvedTitle}, reason=${reason ?? ""}, segments=${JSON.stringify(segments)}`
6948
6960
  );
6949
6961
  if (!apiUrl || !appKey || !templateId) {
6950
6962
  return {
@@ -6961,7 +6973,7 @@ async function sendLightEffect(apiKey, segments, logger, repeatInput, reason) {
6961
6973
  const bizUniqueId = (0, import_node_crypto.randomUUID)();
6962
6974
  const requestBody = {
6963
6975
  appKey,
6964
- bizMap: { noticeType: "APP_NOTIFICATION_IMPORTANT", reason },
6976
+ bizMap: { noticeType: "APP_NOTIFICATION_IMPORTANT", title: resolvedTitle, reason },
6965
6977
  bizUniqueId,
6966
6978
  paramsMap: { bizContent },
6967
6979
  pushType: "SPECIFY_PUSH",
@@ -6988,6 +7000,14 @@ async function sendLightEffect(apiKey, segments, logger, repeatInput, reason) {
6988
7000
  logger?.info(`Light sender: OK bizUniqueId=${bizUniqueId}, resBody=${resBody.substring(0, 200)}`);
6989
7001
  return { ok: true, bizUniqueId, response: JSON.parse(resBody) };
6990
7002
  }
7003
+ function resolveLightTitle(title, reason, segments) {
7004
+ const trimmedTitle = title?.trim();
7005
+ if (trimmedTitle) return trimmedTitle;
7006
+ const trimmedReason = reason?.trim();
7007
+ if (trimmedReason) return trimmedReason;
7008
+ const modeDesc = segments.map((segment) => segment.mode).join("+");
7009
+ return `Effect: ${modeDesc || "custom"}`;
7010
+ }
6991
7011
 
6992
7012
  // src/light-rules/inline-evaluator.ts
6993
7013
  var InlineLightRuleEvaluator = class {
@@ -7050,7 +7070,8 @@ var InlineLightRuleEvaluator = class {
7050
7070
  rule.segments,
7051
7071
  this.logger,
7052
7072
  { repeat_times: rule.repeat_times },
7053
- rule.description
7073
+ rule.description,
7074
+ rule.title
7054
7075
  );
7055
7076
  if (!result.ok) {
7056
7077
  this.logger.warn(
@@ -7065,115 +7086,6 @@ var InlineLightRuleEvaluator = class {
7065
7086
  }
7066
7087
  };
7067
7088
 
7068
- // src/light-rules/migration.ts
7069
- var import_node_fs8 = require("fs");
7070
- var import_node_path7 = require("path");
7071
- var NO_MATCH_FETCH_PY = `#!/usr/bin/env python3
7072
- # \u6B64\u6587\u4EF6\u7531\u8FC1\u79FB\u5DE5\u5177\u751F\u6210\u3002
7073
- # \u706F\u6548\u89C4\u5219\u5DF2\u8FC1\u79FB\u81F3\u4E8B\u4EF6\u9A71\u52A8\u67B6\u6784\uFF0C\u6B64 cron job \u4E0D\u518D\u6267\u884C\u5B9E\u9645\u5DE5\u4F5C\u3002
7074
- print("NO_MATCH")
7075
- `;
7076
- function normalizeScriptText(text) {
7077
- return text.replace(/\r\n/g, "\n").trim();
7078
- }
7079
- function resolveTasksDir(ctx) {
7080
- if (ctx.workspaceDir) return (0, import_node_path7.join)(ctx.workspaceDir, "tasks");
7081
- if (ctx.stateDir) {
7082
- const inferredWorkspaceDir = (0, import_node_path7.join)(ctx.stateDir, "workspace");
7083
- if ((0, import_node_fs8.existsSync)(inferredWorkspaceDir)) return (0, import_node_path7.join)(inferredWorkspaceDir, "tasks");
7084
- return (0, import_node_path7.join)(ctx.stateDir, "tasks");
7085
- }
7086
- return null;
7087
- }
7088
- function migrateLegacyLightRuleTasks(ctx, logger) {
7089
- const tasksDir3 = resolveTasksDir(ctx);
7090
- if (!tasksDir3 || !(0, import_node_fs8.existsSync)(tasksDir3)) return;
7091
- try {
7092
- for (const entry of (0, import_node_fs8.readdirSync)(tasksDir3, { withFileTypes: true })) {
7093
- if (!entry.isDirectory()) continue;
7094
- migrateTaskDir((0, import_node_path7.join)(tasksDir3, String(entry.name)), logger);
7095
- }
7096
- } catch (err2) {
7097
- logger.warn(`migration: failed to read tasks dir: ${err2?.message}`);
7098
- }
7099
- }
7100
- function migrateTaskDir(taskDir, logger) {
7101
- const metaPath = (0, import_node_path7.join)(taskDir, "meta.json");
7102
- if (!(0, import_node_fs8.existsSync)(metaPath)) return;
7103
- let meta;
7104
- try {
7105
- meta = JSON.parse((0, import_node_fs8.readFileSync)(metaPath, "utf-8"));
7106
- } catch {
7107
- return;
7108
- }
7109
- if (meta.type !== "light-rule") return;
7110
- const name = typeof meta.name === "string" ? meta.name : taskDir;
7111
- cleanupLegacyMetaFields(meta, name, metaPath, logger);
7112
- replaceFetchPy(taskDir, name, logger);
7113
- for (const filename of ["README.md", "checkpoint.json"]) {
7114
- removeFile((0, import_node_path7.join)(taskDir, filename), name, filename, logger);
7115
- }
7116
- }
7117
- function cleanupLegacyMetaFields(meta, name, metaPath, logger) {
7118
- const cleaned = [];
7119
- if (mergeMatchRulesIntoDescription(meta)) cleaned.push("matchRules");
7120
- if (meta.cronSchedule !== void 0) {
7121
- delete meta.cronSchedule;
7122
- cleaned.push("cronSchedule");
7123
- }
7124
- if (cleaned.length === 0) return;
7125
- try {
7126
- (0, import_node_fs8.writeFileSync)(metaPath, JSON.stringify(meta, null, 2), "utf-8");
7127
- logger.info(
7128
- `migration: cleaned deprecated field(s) [${cleaned.join(", ")}] in meta.json for light rule: ${name}`
7129
- );
7130
- } catch (err2) {
7131
- logger.warn(`migration: failed to update meta.json for ${name}: ${err2?.message}`);
7132
- }
7133
- }
7134
- function mergeMatchRulesIntoDescription(meta) {
7135
- const matchRules = meta.matchRules;
7136
- if (!matchRules || typeof matchRules !== "object") return false;
7137
- const rules = matchRules;
7138
- const parts = [];
7139
- if (rules.appName) parts.push(`app=${rules.appName}`);
7140
- if (rules.senderKeywords?.length) {
7141
- parts.push(`\u53D1\u4EF6\u4EBA\u5173\u952E\u8BCD=${rules.senderKeywords.join("\u3001")}`);
7142
- }
7143
- if (rules.contentKeywords?.length) {
7144
- parts.push(`\u5185\u5BB9\u5173\u952E\u8BCD=${rules.contentKeywords.join("\u3001")}`);
7145
- }
7146
- if (parts.length > 0) {
7147
- const existing = typeof meta.description === "string" ? meta.description.trim() : "";
7148
- meta.description = existing ? `${existing}\u3002\u5339\u914D\u89C4\u5219\uFF1A${parts.join("\uFF0C")}` : `\u5339\u914D\u89C4\u5219\uFF1A${parts.join("\uFF0C")}`;
7149
- }
7150
- delete meta.matchRules;
7151
- return true;
7152
- }
7153
- function replaceFetchPy(taskDir, name, logger) {
7154
- const fetchPyPath = (0, import_node_path7.join)(taskDir, "fetch.py");
7155
- if (!(0, import_node_fs8.existsSync)(fetchPyPath)) return;
7156
- try {
7157
- const existing = (0, import_node_fs8.readFileSync)(fetchPyPath, "utf-8");
7158
- if (normalizeScriptText(existing) === normalizeScriptText(NO_MATCH_FETCH_PY)) {
7159
- return;
7160
- }
7161
- (0, import_node_fs8.writeFileSync)(fetchPyPath, NO_MATCH_FETCH_PY, "utf-8");
7162
- logger.info(`migration: replaced fetch.py with NO_MATCH placeholder for ${name}`);
7163
- } catch (err2) {
7164
- logger.warn(`migration: failed to replace fetch.py for ${name}: ${err2?.message}`);
7165
- }
7166
- }
7167
- function removeFile(filePath, ruleName, filename, logger) {
7168
- if (!(0, import_node_fs8.existsSync)(filePath)) return;
7169
- try {
7170
- (0, import_node_fs8.rmSync)(filePath);
7171
- logger.info(`migration: removed ${filename} for light rule: ${ruleName}`);
7172
- } catch (err2) {
7173
- logger.warn(`migration: failed to remove ${filename} for ${ruleName}: ${err2?.message}`);
7174
- }
7175
- }
7176
-
7177
7089
  // src/light-rules/pi-invoker.ts
7178
7090
  var import_agent_runtime = require("openclaw/plugin-sdk/agent-runtime");
7179
7091
  var DEFAULT_PROVIDER = "anthropic";
@@ -7355,7 +7267,7 @@ function resolveUpdateChannel(params) {
7355
7267
  }
7356
7268
 
7357
7269
  // src/update/index.ts
7358
- var import_node_path9 = require("path");
7270
+ var import_node_path8 = require("path");
7359
7271
 
7360
7272
  // src/update/checker.ts
7361
7273
  function parseSemver(v) {
@@ -7457,8 +7369,8 @@ var UpdateChecker = class {
7457
7369
  };
7458
7370
 
7459
7371
  // src/update/executor.ts
7460
- var import_node_fs9 = require("fs");
7461
- var import_node_path8 = require("path");
7372
+ var import_node_fs8 = require("fs");
7373
+ var import_node_path7 = require("path");
7462
7374
  var import_node_os = require("os");
7463
7375
  var VERSION_PATTERN = /^\d+\.\d+\.\d+(-[\w.]+)?$/;
7464
7376
  var BASE_URL = "https://artifact.yoooclaw.com/plugin";
@@ -7468,9 +7380,9 @@ async function executeUpdate(version, runCommand, logger, targetDir, updateConfi
7468
7380
  }
7469
7381
  const tgzUrl = `${BASE_URL}/v${version}/yoooclaw-phone-notifications-${version}.tgz`;
7470
7382
  logger.info(`\u6267\u884C\u66F4\u65B0: ${tgzUrl} \u2192 ${targetDir}`);
7471
- const workDir = (0, import_node_fs9.mkdtempSync)((0, import_node_path8.join)((0, import_node_os.tmpdir)(), ".openclaw-plugin-update-"));
7472
- const tgzPath = (0, import_node_path8.join)(workDir, "plugin.tgz");
7473
- const stagingDir = (0, import_node_path8.join)(workDir, "staged");
7383
+ const workDir = (0, import_node_fs8.mkdtempSync)((0, import_node_path7.join)((0, import_node_os.tmpdir)(), ".openclaw-plugin-update-"));
7384
+ const tgzPath = (0, import_node_path7.join)(workDir, "plugin.tgz");
7385
+ const stagingDir = (0, import_node_path7.join)(workDir, "staged");
7474
7386
  let backupDir = null;
7475
7387
  try {
7476
7388
  logger.info("\u4E0B\u8F7D\u63D2\u4EF6\u5305...");
@@ -7479,9 +7391,9 @@ async function executeUpdate(version, runCommand, logger, targetDir, updateConfi
7479
7391
  return { success: false, message: `\u4E0B\u8F7D\u5931\u8D25 (HTTP ${response.status}): ${tgzUrl}` };
7480
7392
  }
7481
7393
  const buffer = Buffer.from(await response.arrayBuffer());
7482
- (0, import_node_fs9.writeFileSync)(tgzPath, buffer);
7394
+ (0, import_node_fs8.writeFileSync)(tgzPath, buffer);
7483
7395
  logger.info(`\u4E0B\u8F7D\u5B8C\u6210 (${buffer.length} bytes)`);
7484
- (0, import_node_fs9.mkdirSync)(stagingDir, { recursive: true });
7396
+ (0, import_node_fs8.mkdirSync)(stagingDir, { recursive: true });
7485
7397
  const tarResult = await runCommand(
7486
7398
  ["tar", "-xzf", tgzPath, "-C", stagingDir, "--strip-components=1"],
7487
7399
  { timeoutMs: 3e4 }
@@ -7490,14 +7402,14 @@ async function executeUpdate(version, runCommand, logger, targetDir, updateConfi
7490
7402
  const err2 = tarResult.stderr || tarResult.stdout || "unknown error";
7491
7403
  return { success: false, message: `\u89E3\u538B\u5931\u8D25: ${err2}` };
7492
7404
  }
7493
- (0, import_node_fs9.mkdirSync)((0, import_node_path8.dirname)(targetDir), { recursive: true });
7405
+ (0, import_node_fs8.mkdirSync)((0, import_node_path7.dirname)(targetDir), { recursive: true });
7494
7406
  try {
7495
7407
  backupDir = `${targetDir}.bak.${Date.now()}`;
7496
- (0, import_node_fs9.renameSync)(targetDir, backupDir);
7408
+ (0, import_node_fs8.renameSync)(targetDir, backupDir);
7497
7409
  } catch {
7498
7410
  backupDir = null;
7499
7411
  }
7500
- (0, import_node_fs9.renameSync)(stagingDir, targetDir);
7412
+ (0, import_node_fs8.renameSync)(stagingDir, targetDir);
7501
7413
  try {
7502
7414
  await updateConfigRecord2(version, tgzUrl);
7503
7415
  } catch (err2) {
@@ -7505,18 +7417,18 @@ async function executeUpdate(version, runCommand, logger, targetDir, updateConfi
7505
7417
  }
7506
7418
  if (backupDir) {
7507
7419
  try {
7508
- (0, import_node_fs9.rmSync)(backupDir, { force: true, recursive: true });
7420
+ (0, import_node_fs8.rmSync)(backupDir, { force: true, recursive: true });
7509
7421
  } catch {
7510
7422
  }
7511
7423
  }
7512
- const msg = `\u5DF2\u66F4\u65B0\u5230 ${version}\uFF0C\u8BF7\u91CD\u542F gateway \u751F\u6548`;
7424
+ const msg = `\u5DF2\u66F4\u65B0\u5230 ${version}`;
7513
7425
  logger.info(msg);
7514
- return { success: true, message: msg };
7426
+ return { success: true, message: msg, version };
7515
7427
  } catch (err2) {
7516
7428
  if (backupDir) {
7517
7429
  try {
7518
- (0, import_node_fs9.rmSync)(targetDir, { force: true, recursive: true });
7519
- (0, import_node_fs9.renameSync)(backupDir, targetDir);
7430
+ (0, import_node_fs8.rmSync)(targetDir, { force: true, recursive: true });
7431
+ (0, import_node_fs8.renameSync)(backupDir, targetDir);
7520
7432
  logger.info("\u5DF2\u56DE\u6EDA\u5230\u4E4B\u524D\u7248\u672C");
7521
7433
  } catch (rollbackErr) {
7522
7434
  logger.error(`\u56DE\u6EDA\u5931\u8D25: ${String(rollbackErr)}`);
@@ -7527,12 +7439,47 @@ async function executeUpdate(version, runCommand, logger, targetDir, updateConfi
7527
7439
  return { success: false, message: errMsg };
7528
7440
  } finally {
7529
7441
  try {
7530
- (0, import_node_fs9.rmSync)(workDir, { force: true, recursive: true });
7442
+ (0, import_node_fs8.rmSync)(workDir, { force: true, recursive: true });
7531
7443
  } catch {
7532
7444
  }
7533
7445
  }
7534
7446
  }
7535
7447
 
7448
+ // src/update/restart.ts
7449
+ function scheduleGatewayRestart(logger, deps = {}) {
7450
+ try {
7451
+ const cfg = deps.loadConfig?.();
7452
+ if (cfg?.commands?.restart === false) {
7453
+ logger.warn("\u66F4\u65B0\u540E\u8DF3\u8FC7 gateway \u91CD\u542F: commands.restart=false");
7454
+ return { scheduled: false };
7455
+ }
7456
+ } catch (err2) {
7457
+ logger.warn(`\u66F4\u65B0\u540E\u68C0\u67E5 gateway \u91CD\u542F\u914D\u7F6E\u5931\u8D25: ${String(err2)}`);
7458
+ }
7459
+ const getListenerCount = deps.getListenerCount ?? ((signal) => process.listenerCount(signal));
7460
+ if (getListenerCount("SIGUSR1") < 1) {
7461
+ logger.warn("\u66F4\u65B0\u540E\u8DF3\u8FC7 gateway \u91CD\u542F: \u5F53\u524D\u8FDB\u7A0B\u672A\u6CE8\u518C SIGUSR1 \u76D1\u542C\u5668");
7462
+ return { scheduled: false };
7463
+ }
7464
+ const emitSignal = deps.emitSignal ?? ((signal) => process.emit(signal));
7465
+ const schedule = deps.schedule ?? ((task) => {
7466
+ setTimeout(task, 0);
7467
+ });
7468
+ schedule(() => {
7469
+ try {
7470
+ const accepted = emitSignal("SIGUSR1");
7471
+ if (accepted) {
7472
+ logger.info("\u66F4\u65B0\u5B8C\u6210\uFF0C\u5DF2\u89E6\u53D1 gateway SIGUSR1 \u91CD\u542F");
7473
+ } else {
7474
+ logger.warn("\u66F4\u65B0\u5B8C\u6210\uFF0C\u4F46 gateway SIGUSR1 \u4FE1\u53F7\u672A\u88AB\u5904\u7406");
7475
+ }
7476
+ } catch (err2) {
7477
+ logger.warn(`\u66F4\u65B0\u540E\u89E6\u53D1 gateway \u91CD\u542F\u5931\u8D25: ${String(err2)}`);
7478
+ }
7479
+ });
7480
+ return { scheduled: true };
7481
+ }
7482
+
7536
7483
  // src/update/index.ts
7537
7484
  var PLUGIN_ID = "phone-notifications";
7538
7485
  function resolveTargetDir(api) {
@@ -7542,7 +7489,7 @@ function resolveTargetDir(api) {
7542
7489
  if (installPath) return installPath;
7543
7490
  } catch {
7544
7491
  }
7545
- return (0, import_node_path9.join)(api.runtime.state.resolveStateDir(), "extensions", PLUGIN_ID);
7492
+ return (0, import_node_path8.join)(api.runtime.state.resolveStateDir(), "extensions", PLUGIN_ID);
7546
7493
  }
7547
7494
  async function updateConfigRecord(api, version, targetDir, tgzUrl) {
7548
7495
  const configApi = api.runtime.config;
@@ -7560,6 +7507,23 @@ async function updateConfigRecord(api, version, targetDir, tgzUrl) {
7560
7507
  };
7561
7508
  await configApi.writeConfigFile(cfg);
7562
7509
  }
7510
+ function finalizeUpdateResult(api, logger, result) {
7511
+ if (!result.success) {
7512
+ return result;
7513
+ }
7514
+ const restart = scheduleGatewayRestart(logger, {
7515
+ loadConfig: () => {
7516
+ const configApi = api.runtime.config;
7517
+ return configApi?.loadConfig?.();
7518
+ }
7519
+ });
7520
+ const version = result.version ?? "\u76EE\u6807\u7248\u672C";
7521
+ return {
7522
+ ...result,
7523
+ restartScheduled: restart.scheduled,
7524
+ message: restart.scheduled ? `\u5DF2\u66F4\u65B0\u5230 ${version}\uFF0C\u5DF2\u89E6\u53D1 gateway \u91CD\u542F` : `\u5DF2\u66F4\u65B0\u5230 ${version}\uFF0C\u8BF7\u624B\u52A8\u91CD\u542F gateway \u751F\u6548`
7525
+ };
7526
+ }
7563
7527
  function registerAutoUpdate(api, logger, config, getBroadcast, rememberBroadcast, externalUpdateNotifier) {
7564
7528
  if (config.enabled === false) {
7565
7529
  logger.info("\u81EA\u52A8\u66F4\u65B0\u5DF2\u7981\u7528 (autoUpdate.enabled = false)");
@@ -7591,7 +7555,7 @@ function registerAutoUpdate(api, logger, config, getBroadcast, rememberBroadcast
7591
7555
  api.registerTool({
7592
7556
  name: "plugin-update",
7593
7557
  label: "Plugin Update",
7594
- description: "\u5C06 phone-notifications \u63D2\u4EF6\u66F4\u65B0\u5230\u6307\u5B9A\u7248\u672C\u3002\u4EC5\u5728\u7528\u6237\u660E\u786E\u540C\u610F\u66F4\u65B0\u540E\u8C03\u7528\u3002\u66F4\u65B0\u5B8C\u6210\u540E\u9700\u91CD\u542F gateway \u751F\u6548\u3002",
7558
+ description: "\u5C06 phone-notifications \u63D2\u4EF6\u66F4\u65B0\u5230\u6307\u5B9A\u7248\u672C\u3002\u4EC5\u5728\u7528\u6237\u660E\u786E\u540C\u610F\u66F4\u65B0\u540E\u8C03\u7528\u3002\u66F4\u65B0\u5B8C\u6210\u540E\u4F1A\u81EA\u52A8\u5C1D\u8BD5\u91CD\u542F gateway\uFF1B\u5982\u5BBF\u4E3B\u4E0D\u652F\u6301\uFF0C\u5219\u9700\u624B\u52A8\u91CD\u542F\u3002",
7595
7559
  parameters: {
7596
7560
  type: "object",
7597
7561
  required: ["version"],
@@ -7613,13 +7577,14 @@ function registerAutoUpdate(api, logger, config, getBroadcast, rememberBroadcast
7613
7577
  targetDir,
7614
7578
  (v, url) => updateConfigRecord(api, v, targetDir, url)
7615
7579
  );
7616
- if (result.success) {
7580
+ const finalResult = finalizeUpdateResult(api, logger, result);
7581
+ if (finalResult.success) {
7617
7582
  pendingUpdate = null;
7618
7583
  externalUpdateNotifier?.clearPendingUpdate();
7619
7584
  }
7620
7585
  return {
7621
- content: [{ type: "text", text: result.message }],
7622
- details: result
7586
+ content: [{ type: "text", text: finalResult.message }],
7587
+ details: finalResult
7623
7588
  };
7624
7589
  }
7625
7590
  });
@@ -7641,16 +7606,20 @@ function registerAutoUpdate(api, logger, config, getBroadcast, rememberBroadcast
7641
7606
  targetDir,
7642
7607
  (v, url) => updateConfigRecord(api, v, targetDir, url)
7643
7608
  );
7644
- if (result.success) {
7609
+ const finalResult = finalizeUpdateResult(api, logger, result);
7610
+ if (finalResult.success) {
7645
7611
  pendingUpdate = null;
7646
7612
  externalUpdateNotifier?.clearPendingUpdate();
7647
7613
  }
7648
- if (result.success) {
7649
- respond(true, { message: result.message });
7614
+ if (finalResult.success) {
7615
+ respond(true, {
7616
+ message: finalResult.message,
7617
+ restartScheduled: finalResult.restartScheduled === true
7618
+ });
7650
7619
  } else {
7651
7620
  respond(false, void 0, {
7652
7621
  code: "UPDATE_FAILED",
7653
- message: result.message
7622
+ message: finalResult.message
7654
7623
  });
7655
7624
  }
7656
7625
  });
@@ -7730,10 +7699,10 @@ function registerAutoUpdateLifecycle(deps) {
7730
7699
  }
7731
7700
 
7732
7701
  // src/plugin/cli.ts
7733
- var import_node_path17 = require("path");
7702
+ var import_node_path16 = require("path");
7734
7703
 
7735
7704
  // src/cli/auth.ts
7736
- var import_node_fs10 = require("fs");
7705
+ var import_node_fs9 = require("fs");
7737
7706
  init_credentials();
7738
7707
  function registerAuthCli(program) {
7739
7708
  const auth = program.command("auth").description("\u7528\u6237\u8BA4\u8BC1\u7BA1\u7406");
@@ -7769,12 +7738,12 @@ function registerAuthCli(program) {
7769
7738
  });
7770
7739
  auth.command("clear").description("\u6E05\u9664\u5DF2\u4FDD\u5B58\u7684\u8BA4\u8BC1\u4FE1\u606F").action(() => {
7771
7740
  const path2 = credentialsPath();
7772
- if ((0, import_node_fs10.existsSync)(path2)) {
7741
+ if ((0, import_node_fs9.existsSync)(path2)) {
7773
7742
  const creds = readCredentials();
7774
7743
  delete creds.apiKey;
7775
7744
  delete creds.token;
7776
7745
  if (Object.keys(creds).length === 0) {
7777
- (0, import_node_fs10.rmSync)(path2, { force: true });
7746
+ (0, import_node_fs9.rmSync)(path2, { force: true });
7778
7747
  } else {
7779
7748
  writeCredentials(creds);
7780
7749
  }
@@ -7906,23 +7875,23 @@ function registerNtfStats(ntf, ctx) {
7906
7875
  }
7907
7876
 
7908
7877
  // src/cli/ntf-sync.ts
7909
- var import_node_fs11 = require("fs");
7910
- var import_node_path10 = require("path");
7878
+ var import_node_fs10 = require("fs");
7879
+ var import_node_path9 = require("path");
7911
7880
  var SYNC_FETCH_LIMIT = 300;
7912
7881
  function checkpointPath(dir) {
7913
- return (0, import_node_path10.join)(dir, ".checkpoint.json");
7882
+ return (0, import_node_path9.join)(dir, ".checkpoint.json");
7914
7883
  }
7915
7884
  function readCheckpoint(dir) {
7916
7885
  const p = checkpointPath(dir);
7917
- if (!(0, import_node_fs11.existsSync)(p)) return {};
7886
+ if (!(0, import_node_fs10.existsSync)(p)) return {};
7918
7887
  try {
7919
- return JSON.parse((0, import_node_fs11.readFileSync)(p, "utf-8"));
7888
+ return JSON.parse((0, import_node_fs10.readFileSync)(p, "utf-8"));
7920
7889
  } catch {
7921
7890
  return {};
7922
7891
  }
7923
7892
  }
7924
7893
  function writeCheckpoint(dir, data) {
7925
- (0, import_node_fs11.writeFileSync)(checkpointPath(dir), JSON.stringify(data, null, 2), "utf-8");
7894
+ (0, import_node_fs10.writeFileSync)(checkpointPath(dir), JSON.stringify(data, null, 2), "utf-8");
7926
7895
  }
7927
7896
  function registerNtfSync(ntf, ctx) {
7928
7897
  const sync = ntf.command("sync").description("\u540C\u6B65\u901A\u77E5\u5230\u8BB0\u5FC6\u7CFB\u7EDF");
@@ -8015,8 +7984,8 @@ function registerNtfSync(ntf, ctx) {
8015
7984
  }
8016
7985
 
8017
7986
  // src/cli/ntf-monitor.ts
8018
- var import_node_fs12 = require("fs");
8019
- var import_node_path11 = require("path");
7987
+ var import_node_fs11 = require("fs");
7988
+ var import_node_path10 = require("path");
8020
7989
 
8021
7990
  // src/monitor/fetch-gen.ts
8022
7991
  function generateFetchPy(name, matchRules) {
@@ -8101,19 +8070,19 @@ function pyLiteral(value) {
8101
8070
  function tasksDir2(ctx) {
8102
8071
  const base = ctx.workspaceDir || ctx.stateDir;
8103
8072
  if (!base) throw new Error("workspaceDir and stateDir both unavailable");
8104
- return (0, import_node_path11.join)(base, "tasks");
8073
+ return (0, import_node_path10.join)(base, "tasks");
8105
8074
  }
8106
8075
  function readMeta2(taskDir) {
8107
- const metaPath = (0, import_node_path11.join)(taskDir, "meta.json");
8108
- if (!(0, import_node_fs12.existsSync)(metaPath)) return null;
8076
+ const metaPath = (0, import_node_path10.join)(taskDir, "meta.json");
8077
+ if (!(0, import_node_fs11.existsSync)(metaPath)) return null;
8109
8078
  try {
8110
- return JSON.parse((0, import_node_fs12.readFileSync)(metaPath, "utf-8"));
8079
+ return JSON.parse((0, import_node_fs11.readFileSync)(metaPath, "utf-8"));
8111
8080
  } catch {
8112
8081
  return null;
8113
8082
  }
8114
8083
  }
8115
8084
  function writeMeta2(taskDir, meta) {
8116
- (0, import_node_fs12.writeFileSync)((0, import_node_path11.join)(taskDir, "meta.json"), JSON.stringify(meta, null, 2), "utf-8");
8085
+ (0, import_node_fs11.writeFileSync)((0, import_node_path10.join)(taskDir, "meta.json"), JSON.stringify(meta, null, 2), "utf-8");
8117
8086
  }
8118
8087
  function generateReadme(name, description) {
8119
8088
  return `# Monitor Task: ${name}
@@ -8132,27 +8101,27 @@ function registerNtfMonitor(ntf, ctx) {
8132
8101
  const monitor = ntf.command("monitor").description("\u901A\u77E5\u76D1\u63A7\u4EFB\u52A1\u7BA1\u7406");
8133
8102
  monitor.command("list").description("\u5217\u51FA\u6240\u6709\u76D1\u63A7\u4EFB\u52A1").action(() => {
8134
8103
  const dir = tasksDir2(ctx);
8135
- if (!(0, import_node_fs12.existsSync)(dir)) {
8104
+ if (!(0, import_node_fs11.existsSync)(dir)) {
8136
8105
  output({ ok: true, tasks: [] });
8137
8106
  return;
8138
8107
  }
8139
8108
  const tasks = [];
8140
- for (const entry of (0, import_node_fs12.readdirSync)(dir, { withFileTypes: true })) {
8109
+ for (const entry of (0, import_node_fs11.readdirSync)(dir, { withFileTypes: true })) {
8141
8110
  if (!entry.isDirectory()) continue;
8142
- const meta = readMeta2((0, import_node_path11.join)(dir, entry.name));
8111
+ const meta = readMeta2((0, import_node_path10.join)(dir, entry.name));
8143
8112
  if (meta) tasks.push(meta);
8144
8113
  }
8145
8114
  output({ ok: true, tasks });
8146
8115
  });
8147
8116
  monitor.command("show <name>").description("\u67E5\u770B\u76D1\u63A7\u4EFB\u52A1\u8BE6\u60C5").action((name) => {
8148
- const taskDir = (0, import_node_path11.join)(tasksDir2(ctx), name);
8117
+ const taskDir = (0, import_node_path10.join)(tasksDir2(ctx), name);
8149
8118
  const meta = readMeta2(taskDir);
8150
8119
  if (!meta) exitError("NOT_FOUND", `\u76D1\u63A7\u4EFB\u52A1 '${name}' \u4E0D\u5B58\u5728`);
8151
- const checkpointPath2 = (0, import_node_path11.join)(taskDir, "checkpoint.json");
8120
+ const checkpointPath2 = (0, import_node_path10.join)(taskDir, "checkpoint.json");
8152
8121
  let checkpoint = {};
8153
- if ((0, import_node_fs12.existsSync)(checkpointPath2)) {
8122
+ if ((0, import_node_fs11.existsSync)(checkpointPath2)) {
8154
8123
  try {
8155
- checkpoint = JSON.parse((0, import_node_fs12.readFileSync)(checkpointPath2, "utf-8"));
8124
+ checkpoint = JSON.parse((0, import_node_fs11.readFileSync)(checkpointPath2, "utf-8"));
8156
8125
  } catch {
8157
8126
  }
8158
8127
  }
@@ -8167,8 +8136,8 @@ function registerNtfMonitor(ntf, ctx) {
8167
8136
  monitor.command("create <name>").description("\u521B\u5EFA\u76D1\u63A7\u4EFB\u52A1").requiredOption("--description <text>", "\u4EFB\u52A1\u63CF\u8FF0").requiredOption("--match-rules <json>", "\u5339\u914D\u89C4\u5219 JSON").requiredOption("--schedule <cron>", "cron \u8868\u8FBE\u5F0F").action(
8168
8137
  (name, opts) => {
8169
8138
  const dir = tasksDir2(ctx);
8170
- const taskDir = (0, import_node_path11.join)(dir, name);
8171
- if ((0, import_node_fs12.existsSync)(taskDir)) {
8139
+ const taskDir = (0, import_node_path10.join)(dir, name);
8140
+ if ((0, import_node_fs11.existsSync)(taskDir)) {
8172
8141
  exitError("ALREADY_EXISTS", `\u76D1\u63A7\u4EFB\u52A1 '${name}' \u5DF2\u5B58\u5728`);
8173
8142
  }
8174
8143
  let matchRules;
@@ -8180,7 +8149,7 @@ function registerNtfMonitor(ntf, ctx) {
8180
8149
  "match-rules \u5FC5\u987B\u662F\u5408\u6CD5\u7684 JSON"
8181
8150
  );
8182
8151
  }
8183
- (0, import_node_fs12.mkdirSync)(taskDir, { recursive: true });
8152
+ (0, import_node_fs11.mkdirSync)(taskDir, { recursive: true });
8184
8153
  const meta = {
8185
8154
  name,
8186
8155
  description: opts.description,
@@ -8190,13 +8159,13 @@ function registerNtfMonitor(ntf, ctx) {
8190
8159
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
8191
8160
  };
8192
8161
  writeMeta2(taskDir, meta);
8193
- (0, import_node_fs12.writeFileSync)(
8194
- (0, import_node_path11.join)(taskDir, "fetch.py"),
8162
+ (0, import_node_fs11.writeFileSync)(
8163
+ (0, import_node_path10.join)(taskDir, "fetch.py"),
8195
8164
  generateFetchPy(name, matchRules),
8196
8165
  "utf-8"
8197
8166
  );
8198
- (0, import_node_fs12.writeFileSync)(
8199
- (0, import_node_path11.join)(taskDir, "README.md"),
8167
+ (0, import_node_fs11.writeFileSync)(
8168
+ (0, import_node_path10.join)(taskDir, "README.md"),
8200
8169
  generateReadme(name, opts.description),
8201
8170
  "utf-8"
8202
8171
  );
@@ -8224,8 +8193,8 @@ function registerNtfMonitor(ntf, ctx) {
8224
8193
  }
8225
8194
  );
8226
8195
  monitor.command("delete <name>").description("\u5220\u9664\u76D1\u63A7\u4EFB\u52A1").option("--yes", "\u8DF3\u8FC7\u786E\u8BA4").action((name, opts) => {
8227
- const taskDir = (0, import_node_path11.join)(tasksDir2(ctx), name);
8228
- if (!(0, import_node_fs12.existsSync)(taskDir)) {
8196
+ const taskDir = (0, import_node_path10.join)(tasksDir2(ctx), name);
8197
+ if (!(0, import_node_fs11.existsSync)(taskDir)) {
8229
8198
  exitError("NOT_FOUND", `\u76D1\u63A7\u4EFB\u52A1 '${name}' \u4E0D\u5B58\u5728`);
8230
8199
  }
8231
8200
  if (!opts.yes) {
@@ -8238,7 +8207,7 @@ function registerNtfMonitor(ntf, ctx) {
8238
8207
  });
8239
8208
  process.exit(1);
8240
8209
  }
8241
- (0, import_node_fs12.rmSync)(taskDir, { recursive: true, force: true });
8210
+ (0, import_node_fs11.rmSync)(taskDir, { recursive: true, force: true });
8242
8211
  output({
8243
8212
  ok: true,
8244
8213
  name,
@@ -8250,7 +8219,7 @@ function registerNtfMonitor(ntf, ctx) {
8250
8219
  });
8251
8220
  });
8252
8221
  monitor.command("enable <name>").description("\u542F\u7528\u76D1\u63A7\u4EFB\u52A1").action((name) => {
8253
- const taskDir = (0, import_node_path11.join)(tasksDir2(ctx), name);
8222
+ const taskDir = (0, import_node_path10.join)(tasksDir2(ctx), name);
8254
8223
  const meta = readMeta2(taskDir);
8255
8224
  if (!meta) exitError("NOT_FOUND", `\u76D1\u63A7\u4EFB\u52A1 '${name}' \u4E0D\u5B58\u5728`);
8256
8225
  meta.enabled = true;
@@ -8258,7 +8227,7 @@ function registerNtfMonitor(ntf, ctx) {
8258
8227
  output({ ok: true, name, enabled: true });
8259
8228
  });
8260
8229
  monitor.command("disable <name>").description("\u6682\u505C\u76D1\u63A7\u4EFB\u52A1").action((name) => {
8261
- const taskDir = (0, import_node_path11.join)(tasksDir2(ctx), name);
8230
+ const taskDir = (0, import_node_path10.join)(tasksDir2(ctx), name);
8262
8231
  const meta = readMeta2(taskDir);
8263
8232
  if (!meta) exitError("NOT_FOUND", `\u76D1\u63A7\u4EFB\u52A1 '${name}' \u4E0D\u5B58\u5728`);
8264
8233
  meta.enabled = false;
@@ -8302,9 +8271,9 @@ function registerLightSend(light) {
8302
8271
  }
8303
8272
 
8304
8273
  // src/cli/light-setup-tools.ts
8305
- var import_node_fs13 = require("fs");
8274
+ var import_node_fs12 = require("fs");
8306
8275
  var import_node_os2 = require("os");
8307
- var import_node_path12 = require("path");
8276
+ var import_node_path11 = require("path");
8308
8277
  function isObject(value) {
8309
8278
  return !!value && typeof value === "object" && !Array.isArray(value);
8310
8279
  }
@@ -8318,7 +8287,7 @@ function ensureArray(obj, key) {
8318
8287
  function resolveConfigPath2() {
8319
8288
  const fromEnv = process.env.OPENCLAW_CONFIG_PATH?.trim();
8320
8289
  if (fromEnv) return fromEnv;
8321
- return (0, import_node_path12.join)((0, import_node_os2.homedir)(), ".openclaw", "openclaw.json");
8290
+ return (0, import_node_path11.join)((0, import_node_os2.homedir)(), ".openclaw", "openclaw.json");
8322
8291
  }
8323
8292
  var LIGHT_TOOLS = [
8324
8293
  "light_control",
@@ -8365,12 +8334,12 @@ function upsertLightControlAlsoAllow(cfg) {
8365
8334
  function registerLightSetupTools(light) {
8366
8335
  light.command("setup").description("\u81EA\u52A8\u653E\u884C light_control\uFF08\u5199\u5165 tools.alsoAllow \u4E0E agents.main.tools.alsoAllow\uFF09").action(() => {
8367
8336
  const configPath = resolveConfigPath2();
8368
- if (!(0, import_node_fs13.existsSync)(configPath)) {
8337
+ if (!(0, import_node_fs12.existsSync)(configPath)) {
8369
8338
  exitError("CONFIG_NOT_FOUND", `\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6: ${configPath}`);
8370
8339
  }
8371
8340
  let cfg = {};
8372
8341
  try {
8373
- const raw = (0, import_node_fs13.readFileSync)(configPath, "utf-8");
8342
+ const raw = (0, import_node_fs12.readFileSync)(configPath, "utf-8");
8374
8343
  const parsed = JSON.parse(raw);
8375
8344
  if (isObject(parsed)) cfg = parsed;
8376
8345
  } catch (err2) {
@@ -8378,8 +8347,8 @@ function registerLightSetupTools(light) {
8378
8347
  }
8379
8348
  const result = upsertLightControlAlsoAllow(cfg);
8380
8349
  try {
8381
- (0, import_node_fs13.mkdirSync)((0, import_node_path12.dirname)(configPath), { recursive: true });
8382
- (0, import_node_fs13.writeFileSync)(configPath, JSON.stringify(cfg, null, 2) + "\n", "utf-8");
8350
+ (0, import_node_fs12.mkdirSync)((0, import_node_path11.dirname)(configPath), { recursive: true });
8351
+ (0, import_node_fs12.writeFileSync)(configPath, JSON.stringify(cfg, null, 2) + "\n", "utf-8");
8383
8352
  } catch (err2) {
8384
8353
  exitError("WRITE_FAILED", `\u5199\u5165\u914D\u7F6E\u5931\u8D25: ${err2?.message ?? String(err2)}`);
8385
8354
  }
@@ -8397,17 +8366,17 @@ function registerLightSetupTools(light) {
8397
8366
  }
8398
8367
 
8399
8368
  // src/cli/tunnel-status.ts
8400
- var import_node_fs14 = require("fs");
8401
- var import_node_path13 = require("path");
8369
+ var import_node_fs13 = require("fs");
8370
+ var import_node_path12 = require("path");
8402
8371
  init_credentials();
8403
8372
  init_env();
8404
- var STATUS_REL_PATH = (0, import_node_path13.join)("plugins", "phone-notifications", "tunnel-status.json");
8373
+ var STATUS_REL_PATH = (0, import_node_path12.join)("plugins", "phone-notifications", "tunnel-status.json");
8405
8374
  function readTunnelStatus(ctx) {
8406
8375
  if (!ctx.stateDir) return null;
8407
- const filePath = (0, import_node_path13.join)(ctx.stateDir, STATUS_REL_PATH);
8408
- if (!(0, import_node_fs14.existsSync)(filePath)) return null;
8376
+ const filePath = (0, import_node_path12.join)(ctx.stateDir, STATUS_REL_PATH);
8377
+ if (!(0, import_node_fs13.existsSync)(filePath)) return null;
8409
8378
  try {
8410
- return JSON.parse((0, import_node_fs14.readFileSync)(filePath, "utf-8"));
8379
+ return JSON.parse((0, import_node_fs13.readFileSync)(filePath, "utf-8"));
8411
8380
  } catch {
8412
8381
  return null;
8413
8382
  }
@@ -8475,24 +8444,24 @@ function registerNtfStoragePath(ntf, ctx) {
8475
8444
  }
8476
8445
 
8477
8446
  // src/cli/log-search.ts
8478
- var import_node_fs15 = require("fs");
8479
- var import_node_path14 = require("path");
8447
+ var import_node_fs14 = require("fs");
8448
+ var import_node_path13 = require("path");
8480
8449
  function resolveLogsDir(ctx) {
8481
8450
  if (ctx.stateDir) {
8482
- const dir = (0, import_node_path14.join)(
8451
+ const dir = (0, import_node_path13.join)(
8483
8452
  ctx.stateDir,
8484
8453
  "plugins",
8485
8454
  "phone-notifications",
8486
8455
  "logs"
8487
8456
  );
8488
- if ((0, import_node_fs15.existsSync)(dir)) return dir;
8457
+ if ((0, import_node_fs14.existsSync)(dir)) return dir;
8489
8458
  }
8490
8459
  return null;
8491
8460
  }
8492
8461
  function listLogDateKeys(dir) {
8493
8462
  const pattern = /^(\d{4}-\d{2}-\d{2})\.log$/;
8494
8463
  const keys = [];
8495
- for (const entry of (0, import_node_fs15.readdirSync)(dir, { withFileTypes: true })) {
8464
+ for (const entry of (0, import_node_fs14.readdirSync)(dir, { withFileTypes: true })) {
8496
8465
  if (!entry.isFile()) continue;
8497
8466
  const m = pattern.exec(entry.name);
8498
8467
  if (m) keys.push(m[1]);
@@ -8500,9 +8469,9 @@ function listLogDateKeys(dir) {
8500
8469
  return keys.sort().reverse();
8501
8470
  }
8502
8471
  function collectLogLines(dir, dateKey, keyword, limit, collected) {
8503
- const filePath = (0, import_node_path14.join)(dir, `${dateKey}.log`);
8504
- if (!(0, import_node_fs15.existsSync)(filePath)) return;
8505
- const content = (0, import_node_fs15.readFileSync)(filePath, "utf-8");
8472
+ const filePath = (0, import_node_path13.join)(dir, `${dateKey}.log`);
8473
+ if (!(0, import_node_fs14.existsSync)(filePath)) return;
8474
+ const content = (0, import_node_fs14.readFileSync)(filePath, "utf-8");
8506
8475
  const lowerKeyword = keyword?.toLowerCase();
8507
8476
  for (const line of content.split("\n")) {
8508
8477
  if (collected.length >= limit) return;
@@ -8569,12 +8538,12 @@ function registerEnvCli(ntf) {
8569
8538
  }
8570
8539
 
8571
8540
  // src/cli/doctor.ts
8572
- var import_node_fs19 = require("fs");
8541
+ var import_node_fs18 = require("fs");
8573
8542
  var import_node_readline = require("readline");
8574
8543
  init_host();
8575
8544
 
8576
8545
  // src/cli/doctor/check-dangerous-flags.ts
8577
- var import_node_fs16 = require("fs");
8546
+ var import_node_fs15 = require("fs");
8578
8547
  function isObject2(v) {
8579
8548
  return !!v && typeof v === "object" && !Array.isArray(v);
8580
8549
  }
@@ -8591,13 +8560,13 @@ var checkDangerousFlags = ({ cfg, configPath }) => {
8591
8560
  detail: "\u8FD9\u4F1A\u5173\u95ED Control UI \u7684\u8BBE\u5907\u8EAB\u4EFD\u9A8C\u8BC1\uFF0C\u4EFB\u4F55\u4EBA\u90FD\u53EF\u4EE5\u8BBF\u95EE\u63A7\u5236\u9762\u677F\u3002",
8592
8561
  fixDescription: "\u8BBE\u4E3A false",
8593
8562
  fix: () => {
8594
- const raw = (0, import_node_fs16.readFileSync)(configPath, "utf-8");
8563
+ const raw = (0, import_node_fs15.readFileSync)(configPath, "utf-8");
8595
8564
  const config = JSON.parse(raw);
8596
8565
  const gw = config.gateway;
8597
8566
  const cui = gw.controlUi;
8598
8567
  cui.dangerouslyDisableDeviceAuth = false;
8599
- (0, import_node_fs16.copyFileSync)(configPath, configPath + ".bak");
8600
- (0, import_node_fs16.writeFileSync)(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
8568
+ (0, import_node_fs15.copyFileSync)(configPath, configPath + ".bak");
8569
+ (0, import_node_fs15.writeFileSync)(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
8601
8570
  }
8602
8571
  };
8603
8572
  };
@@ -8645,11 +8614,11 @@ function warnEmpty() {
8645
8614
  }
8646
8615
 
8647
8616
  // src/cli/doctor/check-state-dir-perms.ts
8648
- var import_node_fs17 = require("fs");
8617
+ var import_node_fs16 = require("fs");
8649
8618
  var checkStateDirPerms = ({ stateDir }) => {
8650
8619
  let mode;
8651
8620
  try {
8652
- mode = (0, import_node_fs17.statSync)(stateDir).mode;
8621
+ mode = (0, import_node_fs16.statSync)(stateDir).mode;
8653
8622
  } catch {
8654
8623
  return null;
8655
8624
  }
@@ -8663,7 +8632,7 @@ var checkStateDirPerms = ({ stateDir }) => {
8663
8632
  detail: "\u5176\u4ED6\u7528\u6237\u53EF\u4EE5\u8BFB\u53D6\u8BE5\u76EE\u5F55\u4E0B\u7684\u51ED\u8BC1\u548C\u914D\u7F6E\u6587\u4EF6\u3002",
8664
8633
  fixDescription: "chmod 700 " + stateDir,
8665
8634
  fix: () => {
8666
- (0, import_node_fs17.chmodSync)(stateDir, 448);
8635
+ (0, import_node_fs16.chmodSync)(stateDir, 448);
8667
8636
  }
8668
8637
  };
8669
8638
  };
@@ -8718,16 +8687,16 @@ var checkCredentials = () => {
8718
8687
  };
8719
8688
 
8720
8689
  // src/cli/doctor/check-tunnel.ts
8721
- var import_node_fs18 = require("fs");
8722
- var import_node_path15 = require("path");
8723
- var STATUS_REL_PATH2 = (0, import_node_path15.join)(
8690
+ var import_node_fs17 = require("fs");
8691
+ var import_node_path14 = require("path");
8692
+ var STATUS_REL_PATH2 = (0, import_node_path14.join)(
8724
8693
  "plugins",
8725
8694
  "phone-notifications",
8726
8695
  "tunnel-status.json"
8727
8696
  );
8728
8697
  var checkTunnel = ({ stateDir }) => {
8729
- const filePath = (0, import_node_path15.join)(stateDir, STATUS_REL_PATH2);
8730
- if (!(0, import_node_fs18.existsSync)(filePath)) {
8698
+ const filePath = (0, import_node_path14.join)(stateDir, STATUS_REL_PATH2);
8699
+ if (!(0, import_node_fs17.existsSync)(filePath)) {
8731
8700
  return {
8732
8701
  id: "tunnel",
8733
8702
  severity: "warn",
@@ -8739,7 +8708,7 @@ var checkTunnel = ({ stateDir }) => {
8739
8708
  }
8740
8709
  let status;
8741
8710
  try {
8742
- status = JSON.parse((0, import_node_fs18.readFileSync)(filePath, "utf-8"));
8711
+ status = JSON.parse((0, import_node_fs17.readFileSync)(filePath, "utf-8"));
8743
8712
  } catch {
8744
8713
  return {
8745
8714
  id: "tunnel",
@@ -8832,9 +8801,9 @@ function isObject5(v) {
8832
8801
  return !!v && typeof v === "object" && !Array.isArray(v);
8833
8802
  }
8834
8803
  function readConfig(configPath) {
8835
- if (!(0, import_node_fs19.existsSync)(configPath)) return {};
8804
+ if (!(0, import_node_fs18.existsSync)(configPath)) return {};
8836
8805
  try {
8837
- const parsed = JSON.parse((0, import_node_fs19.readFileSync)(configPath, "utf-8"));
8806
+ const parsed = JSON.parse((0, import_node_fs18.readFileSync)(configPath, "utf-8"));
8838
8807
  return isObject5(parsed) ? parsed : {};
8839
8808
  } catch {
8840
8809
  return {};
@@ -9035,7 +9004,7 @@ function registerRecStoragePath(rec, ctx) {
9035
9004
 
9036
9005
  // src/cli/rec-setup.ts
9037
9006
  var import_node_readline2 = require("readline");
9038
- var import_node_fs20 = require("fs");
9007
+ var import_node_fs19 = require("fs");
9039
9008
  function ask(rl, question) {
9040
9009
  return new Promise((resolve) => rl.question(question, resolve));
9041
9010
  }
@@ -9121,9 +9090,9 @@ async function setupLocal(rl) {
9121
9090
  function registerRecSetup(rec, ctx) {
9122
9091
  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 () => {
9123
9092
  const configPath = resolveAsrConfigPath(ctx);
9124
- if ((0, import_node_fs20.existsSync)(configPath)) {
9093
+ if ((0, import_node_fs19.existsSync)(configPath)) {
9125
9094
  try {
9126
- const existing = JSON.parse((0, import_node_fs20.readFileSync)(configPath, "utf-8"));
9095
+ const existing = JSON.parse((0, import_node_fs19.readFileSync)(configPath, "utf-8"));
9127
9096
  process.stderr.write(`\u5F53\u524D\u5DF2\u6709\u914D\u7F6E\uFF1Amode = ${existing.mode}`);
9128
9097
  if (existing.updatedAt) process.stderr.write(`\uFF0C\u66F4\u65B0\u4E8E ${existing.updatedAt}`);
9129
9098
  process.stderr.write("\n");
@@ -9136,7 +9105,7 @@ function registerRecSetup(rec, ctx) {
9136
9105
  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"]);
9137
9106
  const config = modeIdx === 0 ? await setupApi(rl) : await setupLocal(rl);
9138
9107
  const stored = { ...config, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
9139
- (0, import_node_fs20.writeFileSync)(configPath, JSON.stringify(stored, null, 2), "utf-8");
9108
+ (0, import_node_fs19.writeFileSync)(configPath, JSON.stringify(stored, null, 2), "utf-8");
9140
9109
  process.stderr.write(`
9141
9110
  \u2713 \u914D\u7F6E\u5DF2\u4FDD\u5B58\u5230 ${configPath}
9142
9111
 
@@ -9150,8 +9119,8 @@ function registerRecSetup(rec, ctx) {
9150
9119
 
9151
9120
  // src/cli/update.ts
9152
9121
  var import_node_child_process = require("child_process");
9153
- var import_node_fs21 = require("fs");
9154
- var import_node_path16 = require("path");
9122
+ var import_node_fs20 = require("fs");
9123
+ var import_node_path15 = require("path");
9155
9124
  var import_node_os3 = __toESM(require("os"), 1);
9156
9125
  init_host();
9157
9126
  var BASE_URL2 = "https://artifact.yoooclaw.com/plugin";
@@ -9210,9 +9179,9 @@ async function runUpdate(ctx, opts) {
9210
9179
  `);
9211
9180
  process.exit(1);
9212
9181
  }
9213
- const tmpScript = (0, import_node_path16.join)(import_node_os3.default.tmpdir(), `openclaw-install-${Date.now()}.mjs`);
9182
+ const tmpScript = (0, import_node_path15.join)(import_node_os3.default.tmpdir(), `openclaw-install-${Date.now()}.mjs`);
9214
9183
  try {
9215
- (0, import_node_fs21.writeFileSync)(tmpScript, installScript, "utf-8");
9184
+ (0, import_node_fs20.writeFileSync)(tmpScript, installScript, "utf-8");
9216
9185
  } catch (err2) {
9217
9186
  const msg = `\u5199\u5165\u4E34\u65F6\u6587\u4EF6\u5931\u8D25: ${err2?.message ?? String(err2)}`;
9218
9187
  if (json) {
@@ -9230,7 +9199,7 @@ async function runUpdate(ctx, opts) {
9230
9199
  { stdio: "inherit" }
9231
9200
  );
9232
9201
  try {
9233
- (0, import_node_fs21.unlinkSync)(tmpScript);
9202
+ (0, import_node_fs20.unlinkSync)(tmpScript);
9234
9203
  } catch {
9235
9204
  }
9236
9205
  if (result.error) {
@@ -9294,10 +9263,10 @@ function inferOpenClawRootDir(workspaceDir) {
9294
9263
  if (!workspaceDir) {
9295
9264
  return void 0;
9296
9265
  }
9297
- if ((0, import_node_path17.basename)(workspaceDir) !== "workspace") {
9266
+ if ((0, import_node_path16.basename)(workspaceDir) !== "workspace") {
9298
9267
  return void 0;
9299
9268
  }
9300
- return (0, import_node_path17.dirname)(workspaceDir);
9269
+ return (0, import_node_path16.dirname)(workspaceDir);
9301
9270
  }
9302
9271
  function registerPluginCli(api, params) {
9303
9272
  const { logger, openclawDir } = params;
@@ -9468,6 +9437,10 @@ var lightControlParameters = {
9468
9437
  type: "string",
9469
9438
  description: "\u7ED3\u5408\u5BF9\u8BDD\u4E0A\u4E0B\u6587\uFF0C\u7B80\u8981\u8BF4\u660E\u4E3A\u4EC0\u4E48\u8981\u4EAE\u706F\uFF08\u4F8B\u5982\uFF1A\u7528\u6237\u5FC3\u60C5\u4F4E\u843D\u60F3\u8981\u6E29\u6696\u6C1B\u56F4\u3001\u5E86\u795D\u751F\u65E5\u3001\u63D0\u9192\u559D\u6C34\u7B49\uFF09\u3002\u8FD9\u4E2A\u5B57\u6BB5\u5E2E\u52A9\u8BB0\u5F55\u6BCF\u6B21\u4EAE\u706F\u7684\u610F\u56FE\u548C\u52A8\u673A\u3002"
9470
9439
  },
9440
+ title: {
9441
+ type: "string",
9442
+ description: "\u5BF9\u672C\u6B21\u4EAE\u706F\u7684\u7B80\u77ED\u6807\u9898/\u6458\u8981\uFF08\u4F8B\u5982\uFF1A\u6E29\u6696\u6C1B\u56F4\u3001\u751F\u65E5\u5E86\u795D\u3001\u559D\u6C34\u63D0\u9192\uFF09\u3002\u4E0D\u586B\u65F6\u9ED8\u8BA4\u56DE\u9000\u5230 reason\u3002"
9443
+ },
9471
9444
  repeat: {
9472
9445
  type: "boolean",
9473
9446
  description: "\u517C\u5BB9\u65E7\u53C2\u6570\u3002true = \u65E0\u9650\u5FAA\u73AF\uFF0Cfalse/\u4E0D\u586B = \u4E00\u8F6E\uFF1B\u82E5\u540C\u65F6\u63D0\u4F9B repeat_times\uFF0C\u5219\u4EE5\u540E\u8005\u4E3A\u51C6"
@@ -9495,7 +9468,7 @@ function registerLightControlTool(api, logger) {
9495
9468
  details: { ok: false, error: { code: "AUTH_REQUIRED", message: e.message } }
9496
9469
  };
9497
9470
  }
9498
- const { segments, reason, repeat, repeat_times } = params;
9471
+ const { segments, title, reason, repeat, repeat_times } = params;
9499
9472
  const validation = validateSegments(segments);
9500
9473
  if (!validation.valid) {
9501
9474
  return {
@@ -9519,13 +9492,14 @@ function registerLightControlTool(api, logger) {
9519
9492
  }
9520
9493
  };
9521
9494
  }
9522
- logger.info(`Light control reason: ${reason}`);
9495
+ logger.info(`Light control title: ${title ?? ""}, reason: ${reason}`);
9523
9496
  const result = await sendLightEffect(
9524
9497
  apiKey,
9525
9498
  validation.segments,
9526
9499
  logger,
9527
9500
  { repeat_times: repeatTimes },
9528
- reason
9501
+ reason,
9502
+ title
9529
9503
  );
9530
9504
  if (!result.ok) {
9531
9505
  logger.warn(
@@ -9543,12 +9517,12 @@ function registerLightControlTool(api, logger) {
9543
9517
  }
9544
9518
 
9545
9519
  // src/plugin/lifecycle.ts
9546
- var import_node_fs31 = require("fs");
9520
+ var import_node_fs30 = require("fs");
9547
9521
  init_host();
9548
9522
 
9549
9523
  // src/notification/app-name-map.ts
9550
- var import_node_fs22 = require("fs");
9551
- var import_node_path18 = require("path");
9524
+ var import_node_fs21 = require("fs");
9525
+ var import_node_path17 = require("path");
9552
9526
  init_credentials();
9553
9527
  init_env();
9554
9528
  var PLUGIN_STATE_DIR = "phone-notifications";
@@ -9569,7 +9543,7 @@ function isAppNameMapApiResponse(v) {
9569
9543
  );
9570
9544
  }
9571
9545
  function getCachePath(stateDir) {
9572
- return (0, import_node_path18.join)(stateDir, "plugins", PLUGIN_STATE_DIR, CACHE_FILE);
9546
+ return (0, import_node_path17.join)(stateDir, "plugins", PLUGIN_STATE_DIR, CACHE_FILE);
9573
9547
  }
9574
9548
  function createAppNameMapProvider(opts) {
9575
9549
  const { stateDir, logger } = opts;
@@ -9581,9 +9555,9 @@ function createAppNameMapProvider(opts) {
9581
9555
  let inFlightFetch = null;
9582
9556
  function loadFromDisk() {
9583
9557
  const path2 = getCachePath(stateDir);
9584
- if (!(0, import_node_fs22.existsSync)(path2)) return;
9558
+ if (!(0, import_node_fs21.existsSync)(path2)) return;
9585
9559
  try {
9586
- const raw = JSON.parse((0, import_node_fs22.readFileSync)(path2, "utf-8"));
9560
+ const raw = JSON.parse((0, import_node_fs21.readFileSync)(path2, "utf-8"));
9587
9561
  if (!isRecordOfStrings(raw)) return;
9588
9562
  map.clear();
9589
9563
  for (const [k, v] of Object.entries(raw)) map.set(k, v);
@@ -9627,10 +9601,10 @@ function createAppNameMapProvider(opts) {
9627
9601
  logger.warn("[app-name-map] refresh succeeded but got 0 entries");
9628
9602
  return;
9629
9603
  }
9630
- const dir = (0, import_node_path18.join)(stateDir, "plugins", PLUGIN_STATE_DIR);
9631
- (0, import_node_fs22.mkdirSync)(dir, { recursive: true });
9604
+ const dir = (0, import_node_path17.join)(stateDir, "plugins", PLUGIN_STATE_DIR);
9605
+ (0, import_node_fs21.mkdirSync)(dir, { recursive: true });
9632
9606
  const cachePath = getCachePath(stateDir);
9633
- (0, import_node_fs22.writeFileSync)(cachePath, JSON.stringify(Object.fromEntries(map), null, 2), "utf-8");
9607
+ (0, import_node_fs21.writeFileSync)(cachePath, JSON.stringify(Object.fromEntries(map), null, 2), "utf-8");
9634
9608
  logger.info(`[app-name-map] refreshed ${map.size} entries from server and saved: ${cachePath}`);
9635
9609
  } catch (e) {
9636
9610
  const message = e instanceof Error ? e.message : String(e);
@@ -9674,19 +9648,19 @@ function createAppNameMapProvider(opts) {
9674
9648
  }
9675
9649
 
9676
9650
  // src/notification/storage.ts
9677
- var import_node_fs23 = require("fs");
9651
+ var import_node_fs22 = require("fs");
9678
9652
  var import_node_crypto2 = require("crypto");
9679
- var import_node_path19 = require("path");
9653
+ var import_node_path18 = require("path");
9680
9654
  var NOTIFICATION_DIR_NAME = "notifications";
9681
9655
  var ID_INDEX_DIR_NAME = ".ids";
9682
9656
  var CONTENT_KEY_INDEX_DIR_NAME = ".keys";
9683
9657
  function getStateFallbackNotificationDir(stateDir) {
9684
- return (0, import_node_path19.join)(stateDir, "plugins", "phone-notifications", NOTIFICATION_DIR_NAME);
9658
+ return (0, import_node_path18.join)(stateDir, "plugins", "phone-notifications", NOTIFICATION_DIR_NAME);
9685
9659
  }
9686
9660
  function ensureWritableDirectory(dir) {
9687
9661
  try {
9688
- (0, import_node_fs23.mkdirSync)(dir, { recursive: true });
9689
- (0, import_node_fs23.accessSync)(dir, import_node_fs23.constants.R_OK | import_node_fs23.constants.W_OK);
9662
+ (0, import_node_fs22.mkdirSync)(dir, { recursive: true });
9663
+ (0, import_node_fs22.accessSync)(dir, import_node_fs22.constants.R_OK | import_node_fs22.constants.W_OK);
9690
9664
  return true;
9691
9665
  } catch {
9692
9666
  return false;
@@ -9699,7 +9673,7 @@ function resolveNotificationStorageDir(ctx, logger) {
9699
9673
  return stateNotifDir;
9700
9674
  }
9701
9675
  if (ctx.workspaceDir) {
9702
- const workspaceDir = (0, import_node_path19.join)(ctx.workspaceDir, NOTIFICATION_DIR_NAME);
9676
+ const workspaceDir = (0, import_node_path18.join)(ctx.workspaceDir, NOTIFICATION_DIR_NAME);
9703
9677
  if (ensureWritableDirectory(workspaceDir)) {
9704
9678
  logger.warn(
9705
9679
  `stateDir \u4E0D\u53EF\u7528\uFF0C\u901A\u77E5\u5DF2\u56DE\u9000\u5230 workspace \u8DEF\u5F84: ${workspaceDir}`
@@ -9714,8 +9688,8 @@ var NotificationStorage = class {
9714
9688
  this.config = config;
9715
9689
  this.logger = logger;
9716
9690
  this.dir = dir;
9717
- this.idIndexDir = (0, import_node_path19.join)(dir, ID_INDEX_DIR_NAME);
9718
- this.contentKeyIndexDir = (0, import_node_path19.join)(dir, CONTENT_KEY_INDEX_DIR_NAME);
9691
+ this.idIndexDir = (0, import_node_path18.join)(dir, ID_INDEX_DIR_NAME);
9692
+ this.contentKeyIndexDir = (0, import_node_path18.join)(dir, CONTENT_KEY_INDEX_DIR_NAME);
9719
9693
  this.resolveDisplayName = resolveDisplayName;
9720
9694
  }
9721
9695
  dir;
@@ -9726,10 +9700,10 @@ var NotificationStorage = class {
9726
9700
  dateWriteChains = /* @__PURE__ */ new Map();
9727
9701
  resolveDisplayName;
9728
9702
  async init() {
9729
- (0, import_node_fs23.mkdirSync)(this.dir, { recursive: true });
9730
- (0, import_node_fs23.mkdirSync)(this.idIndexDir, { recursive: true });
9731
- (0, import_node_fs23.rmSync)(this.contentKeyIndexDir, { recursive: true, force: true });
9732
- (0, import_node_fs23.mkdirSync)(this.contentKeyIndexDir, { recursive: true });
9703
+ (0, import_node_fs22.mkdirSync)(this.dir, { recursive: true });
9704
+ (0, import_node_fs22.mkdirSync)(this.idIndexDir, { recursive: true });
9705
+ (0, import_node_fs22.rmSync)(this.contentKeyIndexDir, { recursive: true, force: true });
9706
+ (0, import_node_fs22.mkdirSync)(this.contentKeyIndexDir, { recursive: true });
9733
9707
  }
9734
9708
  async ingest(items) {
9735
9709
  const result = {
@@ -9768,7 +9742,7 @@ var NotificationStorage = class {
9768
9742
  return { kind: "invalid" };
9769
9743
  }
9770
9744
  const dateKey = this.formatDate(ts);
9771
- const filePath = (0, import_node_path19.join)(this.dir, `${dateKey}.json`);
9745
+ const filePath = (0, import_node_path18.join)(this.dir, `${dateKey}.json`);
9772
9746
  const normalizedId = typeof n.id === "string" ? n.id.trim() : "";
9773
9747
  const entry = this.buildStoredNotification(n);
9774
9748
  return this.withDateWriteLock(dateKey, async () => {
@@ -9785,7 +9759,7 @@ var NotificationStorage = class {
9785
9759
  };
9786
9760
  const arr = this.readStoredNotifications(filePath);
9787
9761
  arr.push(storedEntry);
9788
- (0, import_node_fs23.writeFileSync)(filePath, JSON.stringify(arr, null, 2), "utf-8");
9762
+ (0, import_node_fs22.writeFileSync)(filePath, JSON.stringify(arr, null, 2), "utf-8");
9789
9763
  if (normalizedId) {
9790
9764
  this.recordNotificationId(dateKey, normalizedId);
9791
9765
  }
@@ -9822,7 +9796,7 @@ var NotificationStorage = class {
9822
9796
  return `${year}-${month}-${day}`;
9823
9797
  }
9824
9798
  getIdIndexPath(dateKey) {
9825
- return (0, import_node_path19.join)(this.idIndexDir, `${dateKey}.ids`);
9799
+ return (0, import_node_path18.join)(this.idIndexDir, `${dateKey}.ids`);
9826
9800
  }
9827
9801
  getIdSet(dateKey) {
9828
9802
  const cached = this.idCache.get(dateKey);
@@ -9831,8 +9805,8 @@ var NotificationStorage = class {
9831
9805
  }
9832
9806
  const idPath = this.getIdIndexPath(dateKey);
9833
9807
  const ids = /* @__PURE__ */ new Set();
9834
- if ((0, import_node_fs23.existsSync)(idPath)) {
9835
- const lines = (0, import_node_fs23.readFileSync)(idPath, "utf-8").split(/\r?\n/);
9808
+ if ((0, import_node_fs22.existsSync)(idPath)) {
9809
+ const lines = (0, import_node_fs22.readFileSync)(idPath, "utf-8").split(/\r?\n/);
9836
9810
  for (const line of lines) {
9837
9811
  const id = line.trim();
9838
9812
  if (id) {
@@ -9847,7 +9821,7 @@ var NotificationStorage = class {
9847
9821
  return this.getIdSet(dateKey).has(id);
9848
9822
  }
9849
9823
  getContentKeyIndexPath(dateKey) {
9850
- return (0, import_node_path19.join)(this.contentKeyIndexDir, `${dateKey}.keys`);
9824
+ return (0, import_node_path18.join)(this.contentKeyIndexDir, `${dateKey}.keys`);
9851
9825
  }
9852
9826
  getContentKeySet(dateKey, filePath) {
9853
9827
  const cached = this.contentKeyCache.get(dateKey);
@@ -9856,16 +9830,16 @@ var NotificationStorage = class {
9856
9830
  }
9857
9831
  const keyPath = this.getContentKeyIndexPath(dateKey);
9858
9832
  const keys = /* @__PURE__ */ new Set();
9859
- if ((0, import_node_fs23.existsSync)(filePath)) {
9833
+ if ((0, import_node_fs22.existsSync)(filePath)) {
9860
9834
  for (const item of this.readStoredNotifications(filePath)) {
9861
9835
  keys.add(this.buildNotificationContentKey(item));
9862
9836
  }
9863
9837
  }
9864
9838
  if (keys.size > 0) {
9865
- (0, import_node_fs23.writeFileSync)(keyPath, `${Array.from(keys).join("\n")}
9839
+ (0, import_node_fs22.writeFileSync)(keyPath, `${Array.from(keys).join("\n")}
9866
9840
  `, "utf-8");
9867
- } else if ((0, import_node_fs23.existsSync)(keyPath)) {
9868
- (0, import_node_fs23.rmSync)(keyPath, { force: true });
9841
+ } else if ((0, import_node_fs22.existsSync)(keyPath)) {
9842
+ (0, import_node_fs22.rmSync)(keyPath, { force: true });
9869
9843
  }
9870
9844
  this.contentKeyCache.set(dateKey, keys);
9871
9845
  return keys;
@@ -9880,7 +9854,7 @@ var NotificationStorage = class {
9880
9854
  if (ids.has(id)) {
9881
9855
  return;
9882
9856
  }
9883
- (0, import_node_fs23.appendFileSync)(this.getIdIndexPath(dateKey), `${id}
9857
+ (0, import_node_fs22.appendFileSync)(this.getIdIndexPath(dateKey), `${id}
9884
9858
  `, "utf-8");
9885
9859
  ids.add(id);
9886
9860
  }
@@ -9890,7 +9864,7 @@ var NotificationStorage = class {
9890
9864
  if (keys.has(key)) {
9891
9865
  return;
9892
9866
  }
9893
- (0, import_node_fs23.appendFileSync)(this.getContentKeyIndexPath(dateKey), `${key}
9867
+ (0, import_node_fs22.appendFileSync)(this.getContentKeyIndexPath(dateKey), `${key}
9894
9868
  `, "utf-8");
9895
9869
  keys.add(key);
9896
9870
  }
@@ -9898,11 +9872,11 @@ var NotificationStorage = class {
9898
9872
  return (0, import_node_crypto2.createHash)("sha256").update(entry.appName).update("").update(entry.title).update("").update(entry.content).update("").update(entry.timestamp).digest("hex");
9899
9873
  }
9900
9874
  readStoredNotifications(filePath) {
9901
- if (!(0, import_node_fs23.existsSync)(filePath)) {
9875
+ if (!(0, import_node_fs22.existsSync)(filePath)) {
9902
9876
  return [];
9903
9877
  }
9904
9878
  try {
9905
- const parsed = JSON.parse((0, import_node_fs23.readFileSync)(filePath, "utf-8"));
9879
+ const parsed = JSON.parse((0, import_node_fs22.readFileSync)(filePath, "utf-8"));
9906
9880
  return Array.isArray(parsed) ? parsed : [];
9907
9881
  } catch {
9908
9882
  return [];
@@ -9942,14 +9916,14 @@ var NotificationStorage = class {
9942
9916
  const dateFilePattern = /^(\d{4}-\d{2}-\d{2})\.(json|md)$/;
9943
9917
  const dateDirPattern = /^\d{4}-\d{2}-\d{2}$/;
9944
9918
  try {
9945
- for (const entry of (0, import_node_fs23.readdirSync)(this.dir, { withFileTypes: true })) {
9919
+ for (const entry of (0, import_node_fs22.readdirSync)(this.dir, { withFileTypes: true })) {
9946
9920
  if (entry.isFile()) {
9947
9921
  const match = dateFilePattern.exec(entry.name);
9948
9922
  if (match && match[1] < cutoffDate) {
9949
- (0, import_node_fs23.rmSync)((0, import_node_path19.join)(this.dir, entry.name), { force: true });
9923
+ (0, import_node_fs22.rmSync)((0, import_node_path18.join)(this.dir, entry.name), { force: true });
9950
9924
  }
9951
9925
  } else if (entry.isDirectory() && dateDirPattern.test(entry.name) && entry.name < cutoffDate) {
9952
- (0, import_node_fs23.rmSync)((0, import_node_path19.join)(this.dir, entry.name), { recursive: true, force: true });
9926
+ (0, import_node_fs22.rmSync)((0, import_node_path18.join)(this.dir, entry.name), { recursive: true, force: true });
9953
9927
  }
9954
9928
  }
9955
9929
  } catch {
@@ -9958,11 +9932,11 @@ var NotificationStorage = class {
9958
9932
  /** Remove expired .ids index files */
9959
9933
  pruneIdIndex(cutoffDate) {
9960
9934
  try {
9961
- for (const entry of (0, import_node_fs23.readdirSync)(this.idIndexDir, { withFileTypes: true })) {
9935
+ for (const entry of (0, import_node_fs22.readdirSync)(this.idIndexDir, { withFileTypes: true })) {
9962
9936
  if (!entry.isFile()) continue;
9963
9937
  const match = /^(\d{4}-\d{2}-\d{2})\.ids$/.exec(entry.name);
9964
9938
  if (match && match[1] < cutoffDate) {
9965
- (0, import_node_fs23.rmSync)((0, import_node_path19.join)(this.idIndexDir, entry.name), { force: true });
9939
+ (0, import_node_fs22.rmSync)((0, import_node_path18.join)(this.idIndexDir, entry.name), { force: true });
9966
9940
  this.idCache.delete(match[1]);
9967
9941
  }
9968
9942
  }
@@ -9971,11 +9945,11 @@ var NotificationStorage = class {
9971
9945
  }
9972
9946
  pruneContentKeyIndex(cutoffDate) {
9973
9947
  try {
9974
- for (const entry of (0, import_node_fs23.readdirSync)(this.contentKeyIndexDir, { withFileTypes: true })) {
9948
+ for (const entry of (0, import_node_fs22.readdirSync)(this.contentKeyIndexDir, { withFileTypes: true })) {
9975
9949
  if (!entry.isFile()) continue;
9976
9950
  const match = /^(\d{4}-\d{2}-\d{2})\.keys$/.exec(entry.name);
9977
9951
  if (match && match[1] < cutoffDate) {
9978
- (0, import_node_fs23.rmSync)((0, import_node_path19.join)(this.contentKeyIndexDir, entry.name), { force: true });
9952
+ (0, import_node_fs22.rmSync)((0, import_node_path18.join)(this.contentKeyIndexDir, entry.name), { force: true });
9979
9953
  this.contentKeyCache.delete(match[1]);
9980
9954
  }
9981
9955
  }
@@ -9990,8 +9964,8 @@ var NotificationStorage = class {
9990
9964
  };
9991
9965
 
9992
9966
  // src/recording/storage.ts
9993
- var import_node_fs24 = require("fs");
9994
- var import_node_path20 = require("path");
9967
+ var import_node_fs23 = require("fs");
9968
+ var import_node_path19 = require("path");
9995
9969
 
9996
9970
  // src/recording/state-machine.ts
9997
9971
  var VALID_TRANSITIONS = /* @__PURE__ */ new Map([
@@ -10052,7 +10026,7 @@ function stripMarkdownFence(markdown) {
10052
10026
  }
10053
10027
  function deriveTitleFromTranscriptPath(transcriptFile, recordingId) {
10054
10028
  if (!transcriptFile) return void 0;
10055
- const name = (0, import_node_path20.basename)(transcriptFile, ".md");
10029
+ const name = (0, import_node_path19.basename)(transcriptFile, ".md");
10056
10030
  const prefix = `${recordingId}_`;
10057
10031
  if (name.startsWith(prefix)) {
10058
10032
  const derived = name.slice(prefix.length).trim();
@@ -10080,22 +10054,22 @@ function extractTranscriptContent(markdown) {
10080
10054
  return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
10081
10055
  }
10082
10056
  function resolveRecordingStorageDir(ctx, logger) {
10083
- const stateRecDir = (0, import_node_path20.join)(
10057
+ const stateRecDir = (0, import_node_path19.join)(
10084
10058
  ctx.stateDir,
10085
10059
  "plugins",
10086
10060
  "phone-notifications",
10087
10061
  RECORDINGS_DIR
10088
10062
  );
10089
10063
  try {
10090
- (0, import_node_fs24.mkdirSync)(stateRecDir, { recursive: true });
10064
+ (0, import_node_fs23.mkdirSync)(stateRecDir, { recursive: true });
10091
10065
  logger.info(`\u5F55\u97F3\u5C06\u5199\u5165 stateDir \u8DEF\u5F84: ${stateRecDir}`);
10092
10066
  return stateRecDir;
10093
10067
  } catch {
10094
10068
  }
10095
10069
  if (ctx.workspaceDir) {
10096
- const wsRecDir = (0, import_node_path20.join)(ctx.workspaceDir, RECORDINGS_DIR);
10070
+ const wsRecDir = (0, import_node_path19.join)(ctx.workspaceDir, RECORDINGS_DIR);
10097
10071
  try {
10098
- (0, import_node_fs24.mkdirSync)(wsRecDir, { recursive: true });
10072
+ (0, import_node_fs23.mkdirSync)(wsRecDir, { recursive: true });
10099
10073
  logger.warn(`stateDir \u4E0D\u53EF\u7528\uFF0C\u5F55\u97F3\u5DF2\u56DE\u9000\u5230 workspace \u8DEF\u5F84: ${wsRecDir}`);
10100
10074
  return wsRecDir;
10101
10075
  } catch {
@@ -10107,11 +10081,11 @@ var RecordingStorage = class {
10107
10081
  constructor(dir, logger) {
10108
10082
  this.logger = logger;
10109
10083
  this.dir = dir;
10110
- this.audioDir = (0, import_node_path20.join)(dir, AUDIO_DIR);
10111
- this.transcriptDataDir = (0, import_node_path20.join)(dir, TRANSCRIPT_DATA_DIR);
10112
- this.transcriptsDir = (0, import_node_path20.join)(dir, TRANSCRIPTS_DIR);
10113
- this.summariesDir = (0, import_node_path20.join)(dir, SUMMARIES_DIR);
10114
- this.indexPath = (0, import_node_path20.join)(dir, INDEX_FILE);
10084
+ this.audioDir = (0, import_node_path19.join)(dir, AUDIO_DIR);
10085
+ this.transcriptDataDir = (0, import_node_path19.join)(dir, TRANSCRIPT_DATA_DIR);
10086
+ this.transcriptsDir = (0, import_node_path19.join)(dir, TRANSCRIPTS_DIR);
10087
+ this.summariesDir = (0, import_node_path19.join)(dir, SUMMARIES_DIR);
10088
+ this.indexPath = (0, import_node_path19.join)(dir, INDEX_FILE);
10115
10089
  }
10116
10090
  dir;
10117
10091
  audioDir;
@@ -10122,10 +10096,10 @@ var RecordingStorage = class {
10122
10096
  index = { recordings: [] };
10123
10097
  /** 初始化目录结构并加载索引 */
10124
10098
  async init() {
10125
- (0, import_node_fs24.mkdirSync)(this.audioDir, { recursive: true });
10126
- (0, import_node_fs24.mkdirSync)(this.transcriptDataDir, { recursive: true });
10127
- (0, import_node_fs24.mkdirSync)(this.transcriptsDir, { recursive: true });
10128
- (0, import_node_fs24.mkdirSync)(this.summariesDir, { recursive: true });
10099
+ (0, import_node_fs23.mkdirSync)(this.audioDir, { recursive: true });
10100
+ (0, import_node_fs23.mkdirSync)(this.transcriptDataDir, { recursive: true });
10101
+ (0, import_node_fs23.mkdirSync)(this.transcriptsDir, { recursive: true });
10102
+ (0, import_node_fs23.mkdirSync)(this.summariesDir, { recursive: true });
10129
10103
  this.loadIndex();
10130
10104
  this.logger.info(
10131
10105
  `\u5F55\u97F3\u5B58\u50A8\u5DF2\u521D\u59CB\u5316: ${this.dir}\uFF08\u5171 ${this.index.recordings.length} \u6761\u8BB0\u5F55\uFF09`
@@ -10169,13 +10143,13 @@ var RecordingStorage = class {
10169
10143
  return id;
10170
10144
  }
10171
10145
  if (existing.transcriptDataFile) {
10172
- (0, import_node_fs24.rmSync)((0, import_node_path20.join)(this.dir, existing.transcriptDataFile), { force: true });
10146
+ (0, import_node_fs23.rmSync)((0, import_node_path19.join)(this.dir, existing.transcriptDataFile), { force: true });
10173
10147
  }
10174
10148
  if (existing.transcriptFile) {
10175
- (0, import_node_fs24.rmSync)((0, import_node_path20.join)(this.dir, existing.transcriptFile), { force: true });
10149
+ (0, import_node_fs23.rmSync)((0, import_node_path19.join)(this.dir, existing.transcriptFile), { force: true });
10176
10150
  }
10177
10151
  if (existing.summaryFile) {
10178
- (0, import_node_fs24.rmSync)((0, import_node_path20.join)(this.dir, existing.summaryFile), { force: true });
10152
+ (0, import_node_fs23.rmSync)((0, import_node_path19.join)(this.dir, existing.summaryFile), { force: true });
10179
10153
  }
10180
10154
  existing.metadata = metadata;
10181
10155
  existing.status = "syncing_openclaw";
@@ -10243,7 +10217,7 @@ var RecordingStorage = class {
10243
10217
  if (!entry) return;
10244
10218
  const nextTranscriptDataFile = `${TRANSCRIPT_DATA_DIR}/${filename}`;
10245
10219
  if (entry.transcriptDataFile && entry.transcriptDataFile !== nextTranscriptDataFile) {
10246
- (0, import_node_fs24.rmSync)((0, import_node_path20.join)(this.dir, entry.transcriptDataFile), { force: true });
10220
+ (0, import_node_fs23.rmSync)((0, import_node_path19.join)(this.dir, entry.transcriptDataFile), { force: true });
10247
10221
  }
10248
10222
  entry.transcriptDataFile = nextTranscriptDataFile;
10249
10223
  entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -10257,7 +10231,7 @@ var RecordingStorage = class {
10257
10231
  if (!entry) return;
10258
10232
  const nextTranscriptFile = `${TRANSCRIPTS_DIR}/${filename}`;
10259
10233
  if (entry.transcriptFile && entry.transcriptFile !== nextTranscriptFile) {
10260
- (0, import_node_fs24.rmSync)((0, import_node_path20.join)(this.dir, entry.transcriptFile), { force: true });
10234
+ (0, import_node_fs23.rmSync)((0, import_node_path19.join)(this.dir, entry.transcriptFile), { force: true });
10261
10235
  }
10262
10236
  entry.transcriptFile = nextTranscriptFile;
10263
10237
  entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -10271,7 +10245,7 @@ var RecordingStorage = class {
10271
10245
  if (!entry) return;
10272
10246
  const nextSummaryFile = `${SUMMARIES_DIR}/${filename}`;
10273
10247
  if (entry.summaryFile && entry.summaryFile !== nextSummaryFile) {
10274
- (0, import_node_fs24.rmSync)((0, import_node_path20.join)(this.dir, entry.summaryFile), { force: true });
10248
+ (0, import_node_fs23.rmSync)((0, import_node_path19.join)(this.dir, entry.summaryFile), { force: true });
10275
10249
  }
10276
10250
  entry.summaryFile = nextSummaryFile;
10277
10251
  entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -10369,24 +10343,24 @@ var RecordingStorage = class {
10369
10343
  const entry = this.findById(recordingId);
10370
10344
  if (!entry) return false;
10371
10345
  if (entry.audioFile) {
10372
- const audioPath = (0, import_node_path20.join)(this.dir, entry.audioFile);
10373
- (0, import_node_fs24.rmSync)(audioPath, { force: true });
10346
+ const audioPath = (0, import_node_path19.join)(this.dir, entry.audioFile);
10347
+ (0, import_node_fs23.rmSync)(audioPath, { force: true });
10374
10348
  }
10375
10349
  if (entry.srtFile) {
10376
- const srtPath = (0, import_node_path20.join)(this.dir, entry.srtFile);
10377
- (0, import_node_fs24.rmSync)(srtPath, { force: true });
10350
+ const srtPath = (0, import_node_path19.join)(this.dir, entry.srtFile);
10351
+ (0, import_node_fs23.rmSync)(srtPath, { force: true });
10378
10352
  }
10379
10353
  if (entry.transcriptDataFile) {
10380
- const transcriptDataPath = (0, import_node_path20.join)(this.dir, entry.transcriptDataFile);
10381
- (0, import_node_fs24.rmSync)(transcriptDataPath, { force: true });
10354
+ const transcriptDataPath = (0, import_node_path19.join)(this.dir, entry.transcriptDataFile);
10355
+ (0, import_node_fs23.rmSync)(transcriptDataPath, { force: true });
10382
10356
  }
10383
10357
  if (entry.transcriptFile) {
10384
- const transcriptPath = (0, import_node_path20.join)(this.dir, entry.transcriptFile);
10385
- (0, import_node_fs24.rmSync)(transcriptPath, { force: true });
10358
+ const transcriptPath = (0, import_node_path19.join)(this.dir, entry.transcriptFile);
10359
+ (0, import_node_fs23.rmSync)(transcriptPath, { force: true });
10386
10360
  }
10387
10361
  if (entry.summaryFile) {
10388
- const summaryPath = (0, import_node_path20.join)(this.dir, entry.summaryFile);
10389
- (0, import_node_fs24.rmSync)(summaryPath, { force: true });
10362
+ const summaryPath = (0, import_node_path19.join)(this.dir, entry.summaryFile);
10363
+ (0, import_node_fs23.rmSync)(summaryPath, { force: true });
10390
10364
  }
10391
10365
  if (opts?.localOnly) {
10392
10366
  entry.audioFile = void 0;
@@ -10444,34 +10418,34 @@ var RecordingStorage = class {
10444
10418
  * 获取音频文件的绝对路径。ossUrl 用于推断文件扩展名
10445
10419
  */
10446
10420
  getAudioFilePath(recordingId, ossUrl) {
10447
- return (0, import_node_path20.join)(this.audioDir, this.buildAudioFilename(recordingId, ossUrl));
10421
+ return (0, import_node_path19.join)(this.audioDir, this.buildAudioFilename(recordingId, ossUrl));
10448
10422
  }
10449
10423
  /**
10450
10424
  * 获取打点文件的绝对路径
10451
10425
  */
10452
10426
  getSrtFilePath(recordingId) {
10453
- return (0, import_node_path20.join)(this.audioDir, this.buildSrtFilename(recordingId));
10427
+ return (0, import_node_path19.join)(this.audioDir, this.buildSrtFilename(recordingId));
10454
10428
  }
10455
10429
  /**
10456
10430
  * 获取转写 JSON 文件的绝对路径
10457
10431
  */
10458
10432
  getTranscriptDataFilePath(recordingId) {
10459
- return (0, import_node_path20.join)(this.transcriptDataDir, this.buildTranscriptDataFilename(recordingId));
10433
+ return (0, import_node_path19.join)(this.transcriptDataDir, this.buildTranscriptDataFilename(recordingId));
10460
10434
  }
10461
10435
  /**
10462
10436
  * 获取摘要文件的绝对路径
10463
10437
  */
10464
10438
  getSummaryFilePath(recordingId) {
10465
- return (0, import_node_path20.join)(this.summariesDir, this.buildSummaryFilename(recordingId));
10439
+ return (0, import_node_path19.join)(this.summariesDir, this.buildSummaryFilename(recordingId));
10466
10440
  }
10467
10441
  // ─── Persistence ───
10468
10442
  loadIndex() {
10469
- if (!(0, import_node_fs24.existsSync)(this.indexPath)) {
10443
+ if (!(0, import_node_fs23.existsSync)(this.indexPath)) {
10470
10444
  this.index = { recordings: [] };
10471
10445
  return;
10472
10446
  }
10473
10447
  try {
10474
- const raw = JSON.parse((0, import_node_fs24.readFileSync)(this.indexPath, "utf-8"));
10448
+ const raw = JSON.parse((0, import_node_fs23.readFileSync)(this.indexPath, "utf-8"));
10475
10449
  if (raw && Array.isArray(raw.recordings)) {
10476
10450
  let needsRewrite = false;
10477
10451
  const normalized = raw.recordings.filter((entry) => entry && typeof entry === "object").map((entry) => {
@@ -10509,8 +10483,8 @@ var RecordingStorage = class {
10509
10483
  segments: []
10510
10484
  });
10511
10485
  const transcriptDataFilename = this.buildTranscriptDataFilename(compacted.id);
10512
- (0, import_node_fs24.writeFileSync)(
10513
- (0, import_node_path20.join)(this.transcriptDataDir, transcriptDataFilename),
10486
+ (0, import_node_fs23.writeFileSync)(
10487
+ (0, import_node_path19.join)(this.transcriptDataDir, transcriptDataFilename),
10514
10488
  JSON.stringify(transcriptDoc, null, 2),
10515
10489
  "utf-8"
10516
10490
  );
@@ -10522,8 +10496,8 @@ var RecordingStorage = class {
10522
10496
  compacted.summaryFile = entry.summaryFile;
10523
10497
  } else if (typeof entry.summary === "string" && entry.summary.trim()) {
10524
10498
  const summaryFilename = this.buildSummaryFilename(entry.id);
10525
- (0, import_node_fs24.writeFileSync)(
10526
- (0, import_node_path20.join)(this.summariesDir, summaryFilename),
10499
+ (0, import_node_fs23.writeFileSync)(
10500
+ (0, import_node_path19.join)(this.summariesDir, summaryFilename),
10527
10501
  entry.summary.trim(),
10528
10502
  "utf-8"
10529
10503
  );
@@ -10533,8 +10507,8 @@ var RecordingStorage = class {
10533
10507
  const summaryFromDocument = extractTranscriptSummaryFromDocument(transcriptDoc);
10534
10508
  if (summaryFromDocument) {
10535
10509
  const summaryFilename = this.buildSummaryFilename(entry.id);
10536
- (0, import_node_fs24.writeFileSync)(
10537
- (0, import_node_path20.join)(this.summariesDir, summaryFilename),
10510
+ (0, import_node_fs23.writeFileSync)(
10511
+ (0, import_node_path19.join)(this.summariesDir, summaryFilename),
10538
10512
  summaryFromDocument,
10539
10513
  "utf-8"
10540
10514
  );
@@ -10574,7 +10548,7 @@ var RecordingStorage = class {
10574
10548
  }
10575
10549
  readRelativeTextFile(relativePath) {
10576
10550
  try {
10577
- return (0, import_node_fs24.readFileSync)((0, import_node_path20.join)(this.dir, relativePath), "utf-8");
10551
+ return (0, import_node_fs23.readFileSync)((0, import_node_path19.join)(this.dir, relativePath), "utf-8");
10578
10552
  } catch {
10579
10553
  return void 0;
10580
10554
  }
@@ -10587,7 +10561,7 @@ var RecordingStorage = class {
10587
10561
  return parseTranscriptDocument(raw);
10588
10562
  }
10589
10563
  saveIndex() {
10590
- (0, import_node_fs24.writeFileSync)(
10564
+ (0, import_node_fs23.writeFileSync)(
10591
10565
  this.indexPath,
10592
10566
  JSON.stringify(this.index, null, 2),
10593
10567
  "utf-8"
@@ -10601,8 +10575,8 @@ var RecordingStorage = class {
10601
10575
  init_transcript_document();
10602
10576
 
10603
10577
  // src/recording/downloader.ts
10604
- var import_node_fs25 = require("fs");
10605
- var import_node_path21 = require("path");
10578
+ var import_node_fs24 = require("fs");
10579
+ var import_node_path20 = require("path");
10606
10580
  var import_promises2 = require("stream/promises");
10607
10581
  var import_node_stream = require("stream");
10608
10582
  var DEFAULT_TIMEOUT_MS2 = 5 * 60 * 1e3;
@@ -10612,7 +10586,7 @@ async function downloadFile(url, destPath, logger, options) {
10612
10586
  const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
10613
10587
  const maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;
10614
10588
  const retryBackoffMs = options?.retryBackoffMs ?? DEFAULT_RETRY_BACKOFF_MS;
10615
- (0, import_node_fs25.mkdirSync)((0, import_node_path21.dirname)(destPath), { recursive: true });
10589
+ (0, import_node_fs24.mkdirSync)((0, import_node_path20.dirname)(destPath), { recursive: true });
10616
10590
  let lastError;
10617
10591
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
10618
10592
  const startMs = Date.now();
@@ -10630,11 +10604,11 @@ async function downloadFile(url, destPath, logger, options) {
10630
10604
  if (!res.body) {
10631
10605
  throw new Error("\u54CD\u5E94\u4F53\u4E3A\u7A7A");
10632
10606
  }
10633
- const writeStream = (0, import_node_fs25.createWriteStream)(destPath);
10607
+ const writeStream = (0, import_node_fs24.createWriteStream)(destPath);
10634
10608
  const readable = import_node_stream.Readable.fromWeb(res.body);
10635
10609
  await (0, import_promises2.pipeline)(readable, writeStream);
10636
10610
  const elapsed = Date.now() - startMs;
10637
- const fileSize = (0, import_node_fs25.existsSync)(destPath) ? (0, import_node_fs25.statSync)(destPath).size : 0;
10611
+ const fileSize = (0, import_node_fs24.existsSync)(destPath) ? (0, import_node_fs24.statSync)(destPath).size : 0;
10638
10612
  logger.info(
10639
10613
  `[downloader] \u4E0B\u8F7D\u5B8C\u6210: ${destPath} (${formatBytes(fileSize)}, ${elapsed}ms)`
10640
10614
  );
@@ -10645,7 +10619,7 @@ async function downloadFile(url, destPath, logger, options) {
10645
10619
  } catch (err2) {
10646
10620
  lastError = err2?.message ?? String(err2);
10647
10621
  try {
10648
- if ((0, import_node_fs25.existsSync)(destPath)) (0, import_node_fs25.unlinkSync)(destPath);
10622
+ if ((0, import_node_fs24.existsSync)(destPath)) (0, import_node_fs24.unlinkSync)(destPath);
10649
10623
  } catch {
10650
10624
  }
10651
10625
  const isAbort = err2?.name === "AbortError";
@@ -10883,13 +10857,13 @@ async function triggerTranscription(recordingId, storage, asrConfig, logger, opt
10883
10857
  }
10884
10858
 
10885
10859
  // src/tunnel/service.ts
10886
- var import_node_fs30 = require("fs");
10887
- var import_node_path26 = require("path");
10860
+ var import_node_fs29 = require("fs");
10861
+ var import_node_path25 = require("path");
10888
10862
  init_credentials();
10889
10863
 
10890
10864
  // src/tunnel/relay-client.ts
10891
- var import_node_fs28 = require("fs");
10892
- var import_node_path24 = require("path");
10865
+ var import_node_fs27 = require("fs");
10866
+ var import_node_path23 = require("path");
10893
10867
 
10894
10868
  // node_modules/.pnpm/ws@8.19.0/node_modules/ws/wrapper.mjs
10895
10869
  var import_stream = __toESM(require_stream(), 1);
@@ -10933,8 +10907,8 @@ var RelayClient = class {
10933
10907
  lastDisconnectReason
10934
10908
  };
10935
10909
  try {
10936
- (0, import_node_fs28.mkdirSync)((0, import_node_path24.dirname)(this.opts.statusFilePath), { recursive: true });
10937
- (0, import_node_fs28.writeFileSync)(this.opts.statusFilePath, JSON.stringify(info, null, 2));
10910
+ (0, import_node_fs27.mkdirSync)((0, import_node_path23.dirname)(this.opts.statusFilePath), { recursive: true });
10911
+ (0, import_node_fs27.writeFileSync)(this.opts.statusFilePath, JSON.stringify(info, null, 2));
10938
10912
  } catch {
10939
10913
  }
10940
10914
  }
@@ -11316,8 +11290,8 @@ init_host();
11316
11290
 
11317
11291
  // src/tunnel/device-identity.ts
11318
11292
  var import_node_crypto3 = __toESM(require("crypto"), 1);
11319
- var import_node_fs29 = __toESM(require("fs"), 1);
11320
- var import_node_path25 = __toESM(require("path"), 1);
11293
+ var import_node_fs28 = __toESM(require("fs"), 1);
11294
+ var import_node_path24 = __toESM(require("path"), 1);
11321
11295
  init_host();
11322
11296
  var ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
11323
11297
  function base64UrlEncode(buf) {
@@ -11362,10 +11336,10 @@ function resolveClientStateDir(stateDir) {
11362
11336
  return stateDir ?? resolveStateDir();
11363
11337
  }
11364
11338
  function ensureDir(filePath) {
11365
- import_node_fs29.default.mkdirSync(import_node_path25.default.dirname(filePath), { recursive: true });
11339
+ import_node_fs28.default.mkdirSync(import_node_path24.default.dirname(filePath), { recursive: true });
11366
11340
  }
11367
11341
  function resolveIdentityPath(stateDir) {
11368
- return import_node_path25.default.join(stateDir, "identity", "device.json");
11342
+ return import_node_path24.default.join(stateDir, "identity", "device.json");
11369
11343
  }
11370
11344
  function normalizeDeviceAuthRole(role) {
11371
11345
  return role.trim();
@@ -11381,12 +11355,12 @@ function normalizeDeviceAuthScopes(scopes) {
11381
11355
  return [...out].sort();
11382
11356
  }
11383
11357
  function resolveDeviceAuthPath(stateDir) {
11384
- return import_node_path25.default.join(stateDir, "identity", "device-auth.json");
11358
+ return import_node_path24.default.join(stateDir, "identity", "device-auth.json");
11385
11359
  }
11386
11360
  function readDeviceAuthStore(filePath) {
11387
11361
  try {
11388
- if (!import_node_fs29.default.existsSync(filePath)) return null;
11389
- const raw = import_node_fs29.default.readFileSync(filePath, "utf8");
11362
+ if (!import_node_fs28.default.existsSync(filePath)) return null;
11363
+ const raw = import_node_fs28.default.readFileSync(filePath, "utf8");
11390
11364
  const parsed = JSON.parse(raw);
11391
11365
  if (parsed?.version !== 1 || typeof parsed.deviceId !== "string") return null;
11392
11366
  if (!parsed.tokens || typeof parsed.tokens !== "object") return null;
@@ -11397,12 +11371,12 @@ function readDeviceAuthStore(filePath) {
11397
11371
  }
11398
11372
  function writeDeviceAuthStore(filePath, store) {
11399
11373
  ensureDir(filePath);
11400
- import_node_fs29.default.writeFileSync(filePath, `${JSON.stringify(store, null, 2)}
11374
+ import_node_fs28.default.writeFileSync(filePath, `${JSON.stringify(store, null, 2)}
11401
11375
  `, {
11402
11376
  mode: 384
11403
11377
  });
11404
11378
  try {
11405
- import_node_fs29.default.chmodSync(filePath, 384);
11379
+ import_node_fs28.default.chmodSync(filePath, 384);
11406
11380
  } catch {
11407
11381
  }
11408
11382
  }
@@ -11449,8 +11423,8 @@ function clearDeviceAuthToken(params) {
11449
11423
  function loadOrCreateDeviceIdentity(stateDir) {
11450
11424
  const filePath = resolveIdentityPath(stateDir);
11451
11425
  try {
11452
- if (import_node_fs29.default.existsSync(filePath)) {
11453
- const raw = import_node_fs29.default.readFileSync(filePath, "utf8");
11426
+ if (import_node_fs28.default.existsSync(filePath)) {
11427
+ const raw = import_node_fs28.default.readFileSync(filePath, "utf8");
11454
11428
  const parsed = JSON.parse(raw);
11455
11429
  if (parsed?.version === 1 && typeof parsed.deviceId === "string" && typeof parsed.publicKeyPem === "string" && typeof parsed.privateKeyPem === "string") {
11456
11430
  const derivedId = fingerprintPublicKey(parsed.publicKeyPem);
@@ -11471,14 +11445,14 @@ function loadOrCreateDeviceIdentity(stateDir) {
11471
11445
  publicKeyPem,
11472
11446
  privateKeyPem
11473
11447
  };
11474
- import_node_fs29.default.mkdirSync(import_node_path25.default.dirname(filePath), { recursive: true });
11448
+ import_node_fs28.default.mkdirSync(import_node_path24.default.dirname(filePath), { recursive: true });
11475
11449
  const stored = {
11476
11450
  version: 1,
11477
11451
  ...identity,
11478
11452
  createdAtMs: Date.now()
11479
11453
  };
11480
11454
  ensureDir(filePath);
11481
- import_node_fs29.default.writeFileSync(filePath, `${JSON.stringify(stored, null, 2)}
11455
+ import_node_fs28.default.writeFileSync(filePath, `${JSON.stringify(stored, null, 2)}
11482
11456
  `, {
11483
11457
  mode: 384
11484
11458
  });
@@ -12250,7 +12224,7 @@ function createTunnelService(opts) {
12250
12224
  }
12251
12225
  function readLockOwner(filePath) {
12252
12226
  try {
12253
- const parsed = JSON.parse((0, import_node_fs30.readFileSync)(filePath, "utf-8"));
12227
+ const parsed = JSON.parse((0, import_node_fs29.readFileSync)(filePath, "utf-8"));
12254
12228
  return typeof parsed.pid === "number" ? parsed.pid : null;
12255
12229
  } catch {
12256
12230
  return null;
@@ -12263,23 +12237,23 @@ function createTunnelService(opts) {
12263
12237
  lockFd = null;
12264
12238
  if (fd !== null) {
12265
12239
  try {
12266
- (0, import_node_fs30.closeSync)(fd);
12240
+ (0, import_node_fs29.closeSync)(fd);
12267
12241
  } catch {
12268
12242
  }
12269
12243
  }
12270
12244
  if (filePath) {
12271
12245
  try {
12272
- (0, import_node_fs30.unlinkSync)(filePath);
12246
+ (0, import_node_fs29.unlinkSync)(filePath);
12273
12247
  } catch {
12274
12248
  }
12275
12249
  }
12276
12250
  }
12277
12251
  function acquireLock(filePath) {
12278
- (0, import_node_fs30.mkdirSync)((0, import_node_path26.dirname)(filePath), { recursive: true });
12252
+ (0, import_node_fs29.mkdirSync)((0, import_node_path25.dirname)(filePath), { recursive: true });
12279
12253
  for (let attempt = 0; attempt < 2; attempt++) {
12280
12254
  try {
12281
- const fd = (0, import_node_fs30.openSync)(filePath, "wx", 384);
12282
- (0, import_node_fs30.writeFileSync)(
12255
+ const fd = (0, import_node_fs29.openSync)(filePath, "wx", 384);
12256
+ (0, import_node_fs29.writeFileSync)(
12283
12257
  fd,
12284
12258
  JSON.stringify({
12285
12259
  pid: process.pid,
@@ -12303,7 +12277,7 @@ function createTunnelService(opts) {
12303
12277
  `Relay tunnel: removing stale local lock owned by dead pid=${ownerPid}`
12304
12278
  );
12305
12279
  try {
12306
- (0, import_node_fs30.unlinkSync)(filePath);
12280
+ (0, import_node_fs29.unlinkSync)(filePath);
12307
12281
  } catch {
12308
12282
  }
12309
12283
  continue;
@@ -12348,12 +12322,12 @@ function createTunnelService(opts) {
12348
12322
  return;
12349
12323
  }
12350
12324
  const { logger } = opts;
12351
- const baseStateDir = (0, import_node_path26.join)(ctx.stateDir, "plugins", "phone-notifications");
12325
+ const baseStateDir = (0, import_node_path25.join)(ctx.stateDir, "plugins", "phone-notifications");
12352
12326
  logger.info(
12353
12327
  `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})`
12354
12328
  );
12355
- const statusFilePath = (0, import_node_path26.join)(baseStateDir, "tunnel-status.json");
12356
- const lockPath = (0, import_node_path26.join)(baseStateDir, "relay-tunnel.lock");
12329
+ const statusFilePath = (0, import_node_path25.join)(baseStateDir, "tunnel-status.json");
12330
+ const lockPath = (0, import_node_path25.join)(baseStateDir, "relay-tunnel.lock");
12357
12331
  if (!acquireLock(lockPath)) {
12358
12332
  return;
12359
12333
  }
@@ -12493,7 +12467,7 @@ function readHostGatewayConfig(params) {
12493
12467
  let configData;
12494
12468
  if (configPath) {
12495
12469
  try {
12496
- configData = JSON.parse((0, import_node_fs31.readFileSync)(configPath, "utf-8"));
12470
+ configData = JSON.parse((0, import_node_fs30.readFileSync)(configPath, "utf-8"));
12497
12471
  } catch (err2) {
12498
12472
  if (err2?.code !== "ENOENT") {
12499
12473
  params.logger.warn(
@@ -13255,7 +13229,6 @@ var index_default = {
13255
13229
  recordingStorage = nextRecordingStorage;
13256
13230
  },
13257
13231
  onStorageReady() {
13258
- migrateLegacyLightRuleTasks(lightRuleCtx, logger);
13259
13232
  lightRuleRegistry.reload();
13260
13233
  }
13261
13234
  });