@yoooclaw/phone-notifications 1.11.2-beta.2 → 1.11.2-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -56,6 +56,19 @@ function expandUserPath(value) {
56
56
  }
57
57
  return value;
58
58
  }
59
+ function detectHostKindFromPath(value) {
60
+ if (!value) {
61
+ return void 0;
62
+ }
63
+ const normalized = value.replace(/\\/g, "/").toLowerCase();
64
+ if (normalized.endsWith("/.qclaw") || normalized.includes("/.qclaw/") || normalized.endsWith("/.qclow") || normalized.includes("/.qclow/")) {
65
+ return "qclaw";
66
+ }
67
+ if (normalized.endsWith("/.openclaw") || normalized.includes("/.openclaw/")) {
68
+ return "openclaw";
69
+ }
70
+ return void 0;
71
+ }
59
72
  function candidateMetaPaths() {
60
73
  const home = homeDir();
61
74
  return [
@@ -97,6 +110,22 @@ function hasOpenClawMarkers() {
97
110
  (0, import_node_path4.join)(baseDir, "extensions")
98
111
  ].some((candidate) => (0, import_node_fs5.existsSync)(candidate));
99
112
  }
113
+ function detectHostKind() {
114
+ if (trimToUndefined(process.env.QCLAW_STATE_DIR) || trimToUndefined(process.env.QCLAW_CONFIG_PATH) || trimToUndefined(process.env.QCLAW_HOME)) {
115
+ return "qclaw";
116
+ }
117
+ const pathHintKind = detectHostKindFromPath(resolveStateDirFromEnv()) ?? detectHostKindFromPath(resolveConfigPathFromEnv());
118
+ if (pathHintKind) {
119
+ return pathHintKind;
120
+ }
121
+ if (hasOpenClawMarkers()) {
122
+ return "openclaw";
123
+ }
124
+ if (loadQClawMeta()) {
125
+ return "qclaw";
126
+ }
127
+ return "openclaw";
128
+ }
100
129
  function resolveStateDir() {
101
130
  const envDir = resolveStateDirFromEnv();
102
131
  if (envDir) {
@@ -233,11 +262,16 @@ function readDotEnv() {
233
262
  })
234
263
  );
235
264
  }
265
+ function readPersistedEnvName() {
266
+ const fromDotEnv = readDotEnv()["PHONE_NOTIFICATIONS_ENV"]?.trim();
267
+ if (fromDotEnv && VALID_ENVS.has(fromDotEnv)) return fromDotEnv;
268
+ return void 0;
269
+ }
236
270
  function loadEnvName() {
237
271
  const fromEnvVar = process.env.PHONE_NOTIFICATIONS_ENV?.trim();
238
272
  if (fromEnvVar && VALID_ENVS.has(fromEnvVar)) return fromEnvVar;
239
- const fromDotEnv = readDotEnv()["PHONE_NOTIFICATIONS_ENV"]?.trim();
240
- if (fromDotEnv && VALID_ENVS.has(fromDotEnv)) return fromDotEnv;
273
+ const fromDotEnv = readPersistedEnvName();
274
+ if (fromDotEnv) return fromDotEnv;
241
275
  const { env } = readCredentials();
242
276
  if (env && VALID_ENVS.has(env)) return env;
243
277
  return "production";
@@ -265,13 +299,13 @@ var init_env = __esm({
265
299
  init_credentials();
266
300
  init_host();
267
301
  ENV_CONFIG = {
268
- development: {
269
- lightApiUrl: "https://openclaw-service-dev.yoooclaw.com/api/message/tob/sendMessage",
270
- relayTunnelUrl: "wss://openclaw-service-dev.yoooclaw.com/message/messages/ws/plugin",
271
- appNameMapUrl: "https://openclaw-service-dev.yoooclaw.com/api/application-config/app-package/config-all",
272
- modelProxyLongRecordingSubmitTaskUrl: "https://openclaw-service-dev.yoooclaw.com/api/model-proxy/long-recording/submit-task",
273
- modelProxyLongRecordingQueryTaskResultBaseUrl: "https://openclaw-service-dev.yoooclaw.com/api/model-proxy/long-recording/query-task-result",
274
- accountFileDeleteUrl: "https://openclaw-service-dev.yoooclaw.com/api/account/file/delete"
302
+ test: {
303
+ lightApiUrl: "https://openclaw-service-test.yoooclaw.com/api/message/tob/sendMessage",
304
+ relayTunnelUrl: "wss://openclaw-service-test.yoooclaw.com/message/messages/ws/plugin",
305
+ appNameMapUrl: "https://openclaw-service-test.yoooclaw.com/api/application-config/app-package/config-all",
306
+ modelProxyLongRecordingSubmitTaskUrl: "https://openclaw-service-test.yoooclaw.com/api/model-proxy/long-recording/submit-task",
307
+ modelProxyLongRecordingQueryTaskResultBaseUrl: "https://openclaw-service-test.yoooclaw.com/api/model-proxy/long-recording/query-task-result",
308
+ accountFileDeleteUrl: "https://openclaw-service-test.yoooclaw.com/api/account/file/delete"
275
309
  },
276
310
  production: {
277
311
  lightApiUrl: "https://openclaw-service.yoootek.com/api/message/tob/sendMessage",
@@ -692,7 +726,7 @@ function findWhisperBinary(dataDir, logger) {
692
726
  for (const name of pathNames) {
693
727
  try {
694
728
  const cmd = (0, import_node_os4.platform)() === "win32" ? "where" : "which";
695
- const result = (0, import_node_child_process2.execSync)(`${cmd} ${name}`, { encoding: "utf-8", stdio: "pipe" }).trim();
729
+ const result = (0, import_node_child_process3.execSync)(`${cmd} ${name}`, { encoding: "utf-8", stdio: "pipe" }).trim();
696
730
  if (result) {
697
731
  logger.info(`[whisper-local] \u627E\u5230\u7CFB\u7EDF PATH \u4E8C\u8FDB\u5236: ${result}`);
698
732
  return result.split("\n")[0].trim();
@@ -762,7 +796,7 @@ async function transcribeWithWhisperLocal(audioFilePath, localConfig, dataDir, l
762
796
  const startMs = Date.now();
763
797
  let result;
764
798
  try {
765
- result = (0, import_node_child_process2.spawnSync)(whisperBin, args, {
799
+ result = (0, import_node_child_process3.spawnSync)(whisperBin, args, {
766
800
  encoding: "utf-8",
767
801
  timeout: 60 * 60 * 1e3,
768
802
  // 1 小时超时(5 小时录音)
@@ -817,14 +851,14 @@ function getWhisperLocalStatus(dataDir, logger) {
817
851
  }
818
852
  function detectCuda(logger) {
819
853
  try {
820
- (0, import_node_child_process2.execSync)("nvidia-smi", { encoding: "utf-8", stdio: "pipe", timeout: 5e3 });
854
+ (0, import_node_child_process3.execSync)("nvidia-smi", { encoding: "utf-8", stdio: "pipe", timeout: 5e3 });
821
855
  logger.info("[whisper-local] \u68C0\u6D4B\u5230 NVIDIA GPU (nvidia-smi)");
822
856
  return true;
823
857
  } catch {
824
858
  }
825
859
  if ((0, import_node_os4.platform)() === "linux") {
826
860
  try {
827
- const ldconfig = (0, import_node_child_process2.execSync)("ldconfig -p 2>/dev/null | grep libcudart", {
861
+ const ldconfig = (0, import_node_child_process3.execSync)("ldconfig -p 2>/dev/null | grep libcudart", {
828
862
  encoding: "utf-8",
829
863
  stdio: "pipe",
830
864
  timeout: 5e3
@@ -841,7 +875,7 @@ function detectCuda(logger) {
841
875
  function getAvailableMemoryGB(backend, logger) {
842
876
  if (backend === "cuda") {
843
877
  try {
844
- const output2 = (0, import_node_child_process2.execSync)(
878
+ const output2 = (0, import_node_child_process3.execSync)(
845
879
  "nvidia-smi --query-gpu=memory.free --format=csv,noheader,nounits",
846
880
  { encoding: "utf-8", stdio: "pipe", timeout: 5e3 }
847
881
  );
@@ -889,7 +923,7 @@ function detectAudioFormat(filePath) {
889
923
  }
890
924
  function convertToWav(inputPath, outputPath, actualFmt, logger) {
891
925
  try {
892
- const ffmpegResult = (0, import_node_child_process2.spawnSync)("ffmpeg", [
926
+ const ffmpegResult = (0, import_node_child_process3.spawnSync)("ffmpeg", [
893
927
  "-y",
894
928
  "-i",
895
929
  inputPath,
@@ -913,7 +947,7 @@ function convertToWav(inputPath, outputPath, actualFmt, logger) {
913
947
  }
914
948
  if (actualFmt === ".ogg") {
915
949
  try {
916
- const opusResult = (0, import_node_child_process2.spawnSync)(
950
+ const opusResult = (0, import_node_child_process3.spawnSync)(
917
951
  "opusdec",
918
952
  ["--rate", "16000", "--mono", inputPath, outputPath],
919
953
  { encoding: "utf-8", timeout: 12e4, stdio: ["pipe", "pipe", "pipe"] }
@@ -942,7 +976,7 @@ function convertToWav(inputPath, outputPath, actualFmt, logger) {
942
976
  }
943
977
  }
944
978
  try {
945
- const afResult = (0, import_node_child_process2.spawnSync)("afconvert", [
979
+ const afResult = (0, import_node_child_process3.spawnSync)("afconvert", [
946
980
  "-f",
947
981
  "WAVE",
948
982
  "-d",
@@ -1070,11 +1104,11 @@ function formatBytes2(bytes) {
1070
1104
  if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
1071
1105
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)}GB`;
1072
1106
  }
1073
- 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;
1107
+ var import_node_child_process3, import_node_fs25, import_node_path21, import_promises3, import_node_stream2, import_node_os4, WHISPER_MODELS_DIR, WHISPER_BIN_DIR, HF_MODEL_URL_TEMPLATE, MODELSCOPE_MODEL_URL_TEMPLATE, PROBE_TIMEOUT_MS, MODEL_FILENAMES, MODEL_DISK_SIZES;
1074
1108
  var init_whisper_local = __esm({
1075
1109
  "src/recording/whisper-local.ts"() {
1076
1110
  "use strict";
1077
- import_node_child_process2 = require("child_process");
1111
+ import_node_child_process3 = require("child_process");
1078
1112
  import_node_fs25 = require("fs");
1079
1113
  import_node_path21 = require("path");
1080
1114
  import_promises3 = require("stream/promises");
@@ -5438,7 +5472,7 @@ function readBuildInjectedVersion() {
5438
5472
  if (false) {
5439
5473
  return void 0;
5440
5474
  }
5441
- const version = "1.11.2-beta.2".trim();
5475
+ const version = "1.11.2-beta.4".trim();
5442
5476
  return version || void 0;
5443
5477
  }
5444
5478
  function readPluginVersionFromPackageJson() {
@@ -7309,7 +7343,7 @@ function resolveUpdateChannel(params) {
7309
7343
  if (params.currentVersion.includes("-")) {
7310
7344
  return "beta";
7311
7345
  }
7312
- return params.envName === "development" ? "beta" : "latest";
7346
+ return params.envName === "test" ? "beta" : "latest";
7313
7347
  }
7314
7348
 
7315
7349
  // src/update/index.ts
@@ -8539,21 +8573,98 @@ function registerLogSearch(ntf, ctx) {
8539
8573
  }
8540
8574
 
8541
8575
  // src/cli/env.ts
8576
+ var import_node_child_process = require("child_process");
8542
8577
  init_env();
8543
- function registerEnvCli(ntf) {
8544
- const env = ntf.command("env").description("\u73AF\u5883\u7BA1\u7406\uFF08\u5207\u6362 development / production\uFF09");
8545
- env.command("show").description("\u663E\u793A\u5F53\u524D\u73AF\u5883\u53CA\u5BF9\u5E94\u7684\u63A5\u53E3\u5730\u5740").action(() => {
8546
- const current = loadEnvName();
8547
- const urls = getEnvUrls(current);
8548
- output({
8578
+ init_host();
8579
+ var GATEWAY_RESTART_TIMEOUT_MS = 3e4;
8580
+ var NOOP_LOGGER = {
8581
+ info() {
8582
+ },
8583
+ warn() {
8584
+ },
8585
+ error() {
8586
+ }
8587
+ };
8588
+ function buildEnvShowPayload(deps = {}) {
8589
+ const persistedEnv = deps.readPersistedEnvName?.() ?? readPersistedEnvName();
8590
+ const env = persistedEnv ?? "production";
8591
+ const urls = getEnvUrls(env);
8592
+ return {
8593
+ ok: true,
8594
+ env,
8595
+ source: persistedEnv ? ".env" : "default",
8596
+ lightApiUrl: urls.lightApiUrl,
8597
+ relayTunnelUrl: urls.relayTunnelUrl,
8598
+ availableEnvs: getAvailableEnvs()
8599
+ };
8600
+ }
8601
+ function triggerGatewayReloadAfterEnvSwitch(deps = {}) {
8602
+ const scheduleRestart = deps.scheduleGatewayRestart ?? (() => scheduleGatewayRestart(NOOP_LOGGER));
8603
+ const scheduled = scheduleRestart();
8604
+ if (scheduled.scheduled) {
8605
+ return {
8606
+ attempted: true,
8549
8607
  ok: true,
8550
- env: current,
8551
- lightApiUrl: urls.lightApiUrl,
8552
- relayTunnelUrl: urls.relayTunnelUrl,
8553
- availableEnvs: getAvailableEnvs()
8554
- });
8608
+ method: "signal",
8609
+ message: "\u5DF2\u81EA\u52A8\u89E6\u53D1 gateway \u8FDB\u7A0B\u5185\u91CD\u8F7D"
8610
+ };
8611
+ }
8612
+ const hostKind = deps.detectHostKind?.() ?? detectHostKind();
8613
+ if (hostKind === "qclaw") {
8614
+ return {
8615
+ attempted: false,
8616
+ ok: false,
8617
+ method: "skipped",
8618
+ message: "\u5F53\u524D\u5BBF\u4E3B\u4E3A QClaw\uFF0C\u8BF7\u91CD\u542F QClaw \u684C\u9762\u5E94\u7528\u540E\u4F7F\u73AF\u5883\u5207\u6362\u751F\u6548"
8619
+ };
8620
+ }
8621
+ const hostCli = deps.hostCli?.trim() || process.env.HOST_CLI?.trim() || "openclaw";
8622
+ const command = `${hostCli} gateway restart`;
8623
+ const run = deps.spawnSync ?? import_node_child_process.spawnSync;
8624
+ const result = run(hostCli, ["gateway", "restart"], {
8625
+ encoding: "utf-8",
8626
+ stdio: "pipe",
8627
+ timeout: GATEWAY_RESTART_TIMEOUT_MS
8555
8628
  });
8556
- env.command("switch <env>").description("\u5207\u6362\u73AF\u5883\uFF08development / production\uFF09").action((envName) => {
8629
+ if (!result.error && result.status === 0) {
8630
+ return {
8631
+ attempted: true,
8632
+ ok: true,
8633
+ method: "cli",
8634
+ command,
8635
+ message: `\u5DF2\u81EA\u52A8\u89E6\u53D1 ${command}`
8636
+ };
8637
+ }
8638
+ const stderr = typeof result.stderr === "string" ? result.stderr.trim() : "";
8639
+ const errorMessage = result.error?.message?.trim();
8640
+ return {
8641
+ attempted: true,
8642
+ ok: false,
8643
+ method: "cli",
8644
+ command,
8645
+ message: `\u5DF2\u5207\u6362\u73AF\u5883\uFF0C\u4F46\u81EA\u52A8\u91CD\u8F7D\u672A\u5B8C\u6210\uFF0C\u8BF7\u624B\u52A8\u8FD0\u884C: ${command}`,
8646
+ detail: stderr || errorMessage || (typeof result.status === "number" ? `exit status=${result.status}` : void 0)
8647
+ };
8648
+ }
8649
+ function buildEnvSwitchPayload(envName, deps = {}) {
8650
+ saveEnvName(envName);
8651
+ const urls = getEnvUrls(envName);
8652
+ const reload = triggerGatewayReloadAfterEnvSwitch(deps);
8653
+ return {
8654
+ ok: true,
8655
+ message: reload.ok ? `\u5DF2\u5207\u6362\u5230 ${envName} \u73AF\u5883\uFF0C\u5E76\u5DF2\u89E6\u53D1\u91CD\u8F7D` : `\u5DF2\u5207\u6362\u5230 ${envName} \u73AF\u5883`,
8656
+ env: envName,
8657
+ lightApiUrl: urls.lightApiUrl,
8658
+ relayTunnelUrl: urls.relayTunnelUrl,
8659
+ reload
8660
+ };
8661
+ }
8662
+ function registerEnvCli(ntf, deps = {}) {
8663
+ const env = ntf.command("env").description("\u73AF\u5883\u7BA1\u7406\uFF08\u5207\u6362 test / production\uFF09");
8664
+ env.command("show").description("\u663E\u793A\u5F53\u524D\u73AF\u5883\u53CA\u5BF9\u5E94\u7684\u63A5\u53E3\u5730\u5740").action(() => {
8665
+ output(buildEnvShowPayload(deps));
8666
+ });
8667
+ env.command("switch <env>").description("\u5207\u6362\u73AF\u5883\uFF08test / production\uFF09").action((envName) => {
8557
8668
  const available = getAvailableEnvs();
8558
8669
  if (!available.includes(envName)) {
8559
8670
  exitError(
@@ -8561,15 +8672,7 @@ function registerEnvCli(ntf) {
8561
8672
  `\u65E0\u6548\u7684\u73AF\u5883\u540D\u79F0: ${envName}\uFF0C\u53EF\u9009\u503C: ${available.join(", ")}`
8562
8673
  );
8563
8674
  }
8564
- saveEnvName(envName);
8565
- const urls = getEnvUrls(envName);
8566
- output({
8567
- ok: true,
8568
- message: `\u5DF2\u5207\u6362\u5230 ${envName} \u73AF\u5883`,
8569
- env: envName,
8570
- lightApiUrl: urls.lightApiUrl,
8571
- relayTunnelUrl: urls.relayTunnelUrl
8572
- });
8675
+ output(buildEnvSwitchPayload(envName, deps));
8573
8676
  });
8574
8677
  }
8575
8678
 
@@ -8989,7 +9092,8 @@ function registerRecList(rec, ctx) {
8989
9092
  has_audio: !!r.audioFile,
8990
9093
  has_transcript: !!r.transcriptFile,
8991
9094
  created_at: r.metadata.created_at,
8992
- updated_at: r.updatedAt
9095
+ updated_at: r.updatedAt,
9096
+ error: r.lastError ?? null
8993
9097
  }));
8994
9098
  output({ ok: true, total: items.length, recordings: items });
8995
9099
  });
@@ -9022,6 +9126,7 @@ function registerRecStatus(rec, ctx) {
9022
9126
  transcriptFile: entry.transcriptFile ?? null,
9023
9127
  summaryFile: entry.summaryFile ?? null,
9024
9128
  title: entry.title ?? null,
9129
+ error: entry.lastError ?? null,
9025
9130
  ingestedAt: entry.ingestedAt,
9026
9131
  updatedAt: entry.updatedAt
9027
9132
  }
@@ -9154,7 +9259,7 @@ function registerRecSetup(rec, ctx) {
9154
9259
  }
9155
9260
 
9156
9261
  // src/cli/update.ts
9157
- var import_node_child_process = require("child_process");
9262
+ var import_node_child_process2 = require("child_process");
9158
9263
  var import_node_fs20 = require("fs");
9159
9264
  var import_node_path15 = require("path");
9160
9265
  var import_node_os3 = __toESM(require("os"), 1);
@@ -9229,7 +9334,7 @@ async function runUpdate(ctx, opts) {
9229
9334
  process.exit(1);
9230
9335
  }
9231
9336
  const stateDir = ctx.stateDir ?? resolveStateDir();
9232
- const result = (0, import_node_child_process.spawnSync)(
9337
+ const result = (0, import_node_child_process2.spawnSync)(
9233
9338
  process.execPath,
9234
9339
  [tmpScript, "--version", latest, "--state-dir", stateDir],
9235
9340
  { stdio: "inherit" }
@@ -10010,7 +10115,8 @@ var VALID_TRANSITIONS = /* @__PURE__ */ new Map([
10010
10115
  ["pending_oss_upload", /* @__PURE__ */ new Set(["uploading_oss"])],
10011
10116
  ["uploading_oss", /* @__PURE__ */ new Set(["oss_uploaded"])],
10012
10117
  ["oss_uploaded", /* @__PURE__ */ new Set(["syncing_openclaw"])],
10013
- ["syncing_openclaw", /* @__PURE__ */ new Set(["synced"])],
10118
+ ["syncing_openclaw", /* @__PURE__ */ new Set(["synced", "sync_failed"])],
10119
+ ["sync_failed", /* @__PURE__ */ new Set(["syncing_openclaw"])],
10014
10120
  ["synced", /* @__PURE__ */ new Set(["transcribing"])],
10015
10121
  ["transcribing", /* @__PURE__ */ new Set(["transcribed", "transcribe_failed"])],
10016
10122
  ["transcribe_failed", /* @__PURE__ */ new Set(["transcribing"])],
@@ -10168,7 +10274,7 @@ var RecordingStorage = class {
10168
10274
  const existing = this.findById(id);
10169
10275
  if (existing) {
10170
10276
  const sameAudioUrl = existing.metadata.oss_audio_url === metadata.oss_audio_url;
10171
- const canPreserveSyncState = sameAudioUrl && !!existing.audioFile && existing.status !== "syncing_openclaw";
10277
+ const canPreserveSyncState = sameAudioUrl && !!existing.audioFile && existing.status !== "syncing_openclaw" && existing.status !== "sync_failed";
10172
10278
  if (canPreserveSyncState) {
10173
10279
  existing.metadata = metadata;
10174
10280
  existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -10193,6 +10299,7 @@ var RecordingStorage = class {
10193
10299
  existing.transcriptFile = void 0;
10194
10300
  existing.summaryFile = void 0;
10195
10301
  existing.title = void 0;
10302
+ existing.lastError = void 0;
10196
10303
  existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
10197
10304
  this.logger.info(`\u5F55\u97F3\u5143\u6570\u636E\u5DF2\u66F4\u65B0: ${id}`);
10198
10305
  } else {
@@ -10297,6 +10404,16 @@ var RecordingStorage = class {
10297
10404
  entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
10298
10405
  this.saveIndex();
10299
10406
  }
10407
+ /**
10408
+ * 记录最近一次失败原因;传 undefined 表示清除错误
10409
+ */
10410
+ setLastError(recordingId, error) {
10411
+ const entry = this.findById(recordingId);
10412
+ if (!entry) return;
10413
+ entry.lastError = error?.trim() || void 0;
10414
+ entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
10415
+ this.saveIndex();
10416
+ }
10300
10417
  /**
10301
10418
  * 读取摘要文本
10302
10419
  */
@@ -10565,6 +10682,9 @@ var RecordingStorage = class {
10565
10682
  needsRewrite = true;
10566
10683
  }
10567
10684
  }
10685
+ if (typeof entry.lastError === "string" && entry.lastError.trim()) {
10686
+ compacted.lastError = entry.lastError.trim();
10687
+ }
10568
10688
  return compacted;
10569
10689
  });
10570
10690
  const hadLargeFields = raw.recordings.some(
@@ -10763,6 +10883,7 @@ function emitRecordingStatus(recordingId, storage, logger, notifyStatus, error,
10763
10883
  return;
10764
10884
  }
10765
10885
  const title = extras?.title?.trim() || entry.title?.trim() || entry.metadata.name?.trim() || entry.id;
10886
+ const persistedError = entry.lastError?.trim() || void 0;
10766
10887
  try {
10767
10888
  notifyStatus({
10768
10889
  recordingId: entry.id,
@@ -10775,7 +10896,7 @@ function emitRecordingStatus(recordingId, storage, logger, notifyStatus, error,
10775
10896
  summary: extras?.summary,
10776
10897
  title,
10777
10898
  updatedAt: entry.updatedAt,
10778
- error
10899
+ error: error ?? persistedError
10779
10900
  });
10780
10901
  } catch (err2) {
10781
10902
  logger.error(
@@ -10802,6 +10923,8 @@ async function runRecordingSyncInBackground(metadata, recordingId, storage, asrC
10802
10923
  if (!downloadResult.audio.ok) {
10803
10924
  const error = `\u97F3\u9891\u4E0B\u8F7D\u5931\u8D25: ${downloadResult.audio.error}`;
10804
10925
  logger.error(`[recording-sync] ${error}: ${recordingId}`);
10926
+ storage.updateStatus(recordingId, "sync_failed");
10927
+ storage.setLastError(recordingId, error);
10805
10928
  emitRecordingStatus(
10806
10929
  recordingId,
10807
10930
  storage,
@@ -10811,6 +10934,7 @@ async function runRecordingSyncInBackground(metadata, recordingId, storage, asrC
10811
10934
  );
10812
10935
  return;
10813
10936
  }
10937
+ storage.setLastError(recordingId, void 0);
10814
10938
  storage.setAudioFile(
10815
10939
  recordingId,
10816
10940
  storage.buildAudioFilename(recordingId, metadata.oss_audio_url)
@@ -10833,7 +10957,7 @@ async function runRecordingSyncInBackground(metadata, recordingId, storage, asrC
10833
10957
  }
10834
10958
  async function handleRecordingSync(recordingId, metadata, storage, asrConfig, logger, options = {}) {
10835
10959
  const existing = storage.findById(recordingId);
10836
- const shouldDownloadAndSync = !existing || existing.metadata.oss_audio_url !== metadata.oss_audio_url || !existing.audioFile || existing.status === "syncing_openclaw";
10960
+ const shouldDownloadAndSync = !existing || existing.metadata.oss_audio_url !== metadata.oss_audio_url || !existing.audioFile || existing.status === "syncing_openclaw" || existing.status === "sync_failed";
10837
10961
  storage.ingest(recordingId, metadata);
10838
10962
  if (shouldDownloadAndSync) {
10839
10963
  runRecordingSyncInBackground(
@@ -10846,6 +10970,11 @@ async function handleRecordingSync(recordingId, metadata, storage, asrConfig, lo
10846
10970
  ).catch((err2) => {
10847
10971
  const error = `\u5F55\u97F3\u540C\u6B65\u5931\u8D25: ${err2?.message ?? err2}`;
10848
10972
  logger.error(`[recording-sync] ${error}: ${recordingId}`);
10973
+ const current2 = storage.findById(recordingId);
10974
+ if (current2?.status === "syncing_openclaw") {
10975
+ storage.updateStatus(recordingId, "sync_failed");
10976
+ }
10977
+ storage.setLastError(recordingId, error);
10849
10978
  emitRecordingStatus(
10850
10979
  recordingId,
10851
10980
  storage,
@@ -10859,8 +10988,8 @@ async function handleRecordingSync(recordingId, metadata, storage, asrConfig, lo
10859
10988
  `[recording-sync] \u5F55\u97F3\u5DF2\u5B58\u5728\u4E14\u97F3\u9891\u672A\u53D8\u5316\uFF0C\u8DF3\u8FC7\u91CD\u590D\u4E0B\u8F7D: ${recordingId}`
10860
10989
  );
10861
10990
  emitRecordingStatus(recordingId, storage, logger, options.notifyStatus);
10862
- const current = storage.findById(recordingId);
10863
- if (current?.status === "synced" && isAsrConfigured(asrConfig)) {
10991
+ const current2 = storage.findById(recordingId);
10992
+ if (current2?.status === "synced" && isAsrConfigured(asrConfig)) {
10864
10993
  triggerTranscription(
10865
10994
  recordingId,
10866
10995
  storage,
@@ -10874,10 +11003,12 @@ async function handleRecordingSync(recordingId, metadata, storage, asrConfig, lo
10874
11003
  });
10875
11004
  }
10876
11005
  }
11006
+ const current = storage.findById(recordingId);
10877
11007
  return {
10878
11008
  ok: true,
10879
11009
  recordingId,
10880
- transfer_status: storage.findById(recordingId)?.status ?? "syncing_openclaw"
11010
+ transfer_status: current?.status ?? "syncing_openclaw",
11011
+ ...current?.lastError ? { error: current.lastError } : {}
10881
11012
  };
10882
11013
  }
10883
11014
  async function triggerTranscription(recordingId, storage, asrConfig, logger, options = {}) {
@@ -10893,6 +11024,7 @@ async function triggerTranscription(recordingId, storage, asrConfig, logger, opt
10893
11024
  return;
10894
11025
  }
10895
11026
  storage.updateStatus(recordingId, "transcribing");
11027
+ storage.setLastError(recordingId, void 0);
10896
11028
  emitRecordingStatus(recordingId, storage, logger, options.notifyStatus);
10897
11029
  const audioFilePath = storage.getAudioFilePath(recordingId);
10898
11030
  const result = await runTranscriptionWorkflow({
@@ -10944,6 +11076,7 @@ async function triggerTranscription(recordingId, storage, asrConfig, logger, opt
10944
11076
  }
10945
11077
  } else {
10946
11078
  storage.updateStatus(recordingId, "transcribe_failed");
11079
+ storage.setLastError(recordingId, result.error);
10947
11080
  emitRecordingStatus(
10948
11081
  recordingId,
10949
11082
  storage,
@@ -12778,6 +12911,7 @@ var RECORDING_TRANSFER_STATUSES = /* @__PURE__ */ new Set([
12778
12911
  "uploading_oss",
12779
12912
  "oss_uploaded",
12780
12913
  "syncing_openclaw",
12914
+ "sync_failed",
12781
12915
  "synced",
12782
12916
  "transcribing",
12783
12917
  "transcribe_failed",
@@ -12801,7 +12935,8 @@ function buildRecordingListItem(entry) {
12801
12935
  audioFile: entry.audioFile,
12802
12936
  transcriptDataFile: entry.transcriptDataFile,
12803
12937
  transcriptFile: entry.transcriptFile,
12804
- updatedAt: entry.updatedAt
12938
+ updatedAt: entry.updatedAt,
12939
+ ...entry.lastError ? { error: entry.lastError } : {}
12805
12940
  };
12806
12941
  }
12807
12942
  function isRelayInternalHttpRequest2(req) {
@@ -12834,7 +12969,8 @@ function buildRecordingDetail(entry, extras) {
12834
12969
  transcript: extras?.transcript,
12835
12970
  transcriptData: extras?.transcriptData,
12836
12971
  ingestedAt: entry.ingestedAt,
12837
- updatedAt: entry.updatedAt
12972
+ updatedAt: entry.updatedAt,
12973
+ ...entry.lastError ? { error: entry.lastError } : {}
12838
12974
  };
12839
12975
  }
12840
12976
  function registerRecordingInterfaces(deps) {