@yoooclaw/phone-notifications 1.10.0-beta.21 → 1.10.0-beta.22
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.js +172 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1133,6 +1133,7 @@ async function runTranscriptionWorkflow(params) {
|
|
|
1133
1133
|
durationSec,
|
|
1134
1134
|
createdAt,
|
|
1135
1135
|
transcriptsDir,
|
|
1136
|
+
summariesDir,
|
|
1136
1137
|
recordingId,
|
|
1137
1138
|
logger
|
|
1138
1139
|
} = params;
|
|
@@ -1157,9 +1158,14 @@ async function runTranscriptionWorkflow(params) {
|
|
|
1157
1158
|
const filePath = join15(transcriptsDir, filename);
|
|
1158
1159
|
writeFileSync12(filePath, markdown, "utf-8");
|
|
1159
1160
|
logger.info(`[asr] \u8F6C\u5199\u6587\u672C\u5DF2\u5199\u5165: ${filePath}`);
|
|
1161
|
+
const summaryFilename = `${recordingId}.md`;
|
|
1162
|
+
const summaryFilePath = join15(summariesDir, summaryFilename);
|
|
1163
|
+
writeFileSync12(summaryFilePath, summary, "utf-8");
|
|
1164
|
+
logger.info(`[asr] \u6458\u8981\u6587\u672C\u5DF2\u5199\u5165: ${summaryFilePath}`);
|
|
1160
1165
|
return {
|
|
1161
1166
|
ok: true,
|
|
1162
1167
|
transcriptFilename: filename,
|
|
1168
|
+
summaryFilename,
|
|
1163
1169
|
transcript: result.text ?? "",
|
|
1164
1170
|
summary,
|
|
1165
1171
|
title
|
|
@@ -7762,6 +7768,8 @@ function registerRecStatus(rec, ctx) {
|
|
|
7762
7768
|
audioFile: entry.audioFile ?? null,
|
|
7763
7769
|
srtFile: entry.srtFile ?? null,
|
|
7764
7770
|
transcriptFile: entry.transcriptFile ?? null,
|
|
7771
|
+
summaryFile: entry.summaryFile ?? null,
|
|
7772
|
+
title: entry.title ?? null,
|
|
7765
7773
|
ingestedAt: entry.ingestedAt,
|
|
7766
7774
|
updatedAt: entry.updatedAt
|
|
7767
7775
|
}
|
|
@@ -8616,7 +8624,7 @@ import {
|
|
|
8616
8624
|
writeFileSync as writeFileSync11,
|
|
8617
8625
|
rmSync as rmSync5
|
|
8618
8626
|
} from "fs";
|
|
8619
|
-
import { join as join13 } from "path";
|
|
8627
|
+
import { join as join13, basename as basename3 } from "path";
|
|
8620
8628
|
|
|
8621
8629
|
// src/recording/state-machine.ts
|
|
8622
8630
|
var VALID_TRANSITIONS = /* @__PURE__ */ new Map([
|
|
@@ -8668,7 +8676,40 @@ function extractAudioExt(ossUrl) {
|
|
|
8668
8676
|
var RECORDINGS_DIR = "recordings";
|
|
8669
8677
|
var AUDIO_DIR = "audio";
|
|
8670
8678
|
var TRANSCRIPTS_DIR = "transcripts";
|
|
8679
|
+
var SUMMARIES_DIR = "summaries";
|
|
8671
8680
|
var INDEX_FILE = "index.json";
|
|
8681
|
+
function stripMarkdownFence(markdown) {
|
|
8682
|
+
return markdown.replace(/\r\n/g, "\n");
|
|
8683
|
+
}
|
|
8684
|
+
function deriveTitleFromTranscriptPath(transcriptFile, recordingId) {
|
|
8685
|
+
if (!transcriptFile) return void 0;
|
|
8686
|
+
const name = basename3(transcriptFile, ".md");
|
|
8687
|
+
const prefix = `${recordingId}_`;
|
|
8688
|
+
if (name.startsWith(prefix)) {
|
|
8689
|
+
const derived = name.slice(prefix.length).trim();
|
|
8690
|
+
return derived || void 0;
|
|
8691
|
+
}
|
|
8692
|
+
return void 0;
|
|
8693
|
+
}
|
|
8694
|
+
function extractTranscriptContent(markdown) {
|
|
8695
|
+
const normalized = stripMarkdownFence(markdown);
|
|
8696
|
+
const firstDivider = normalized.indexOf("\n---\n");
|
|
8697
|
+
let body = firstDivider >= 0 ? normalized.slice(firstDivider + "\n---\n".length) : normalized;
|
|
8698
|
+
if (body.startsWith("\n")) {
|
|
8699
|
+
body = body.slice(1);
|
|
8700
|
+
}
|
|
8701
|
+
if (body.startsWith("### \u5173\u952E\u70B9")) {
|
|
8702
|
+
const secondDivider = body.indexOf("\n---\n");
|
|
8703
|
+
if (secondDivider >= 0) {
|
|
8704
|
+
body = body.slice(secondDivider + "\n---\n".length);
|
|
8705
|
+
if (body.startsWith("\n")) {
|
|
8706
|
+
body = body.slice(1);
|
|
8707
|
+
}
|
|
8708
|
+
}
|
|
8709
|
+
}
|
|
8710
|
+
const lines = body.split("\n").filter((line) => !/^\*\*\[关键点 .+\]\*\*$/.test(line.trim())).filter((line) => !/^- \*\*\[关键点 .+\]\*\*$/.test(line.trim()));
|
|
8711
|
+
return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
8712
|
+
}
|
|
8672
8713
|
function resolveRecordingStorageDir(ctx, logger) {
|
|
8673
8714
|
const stateRecDir = join13(
|
|
8674
8715
|
ctx.stateDir,
|
|
@@ -8699,17 +8740,20 @@ var RecordingStorage = class {
|
|
|
8699
8740
|
this.dir = dir;
|
|
8700
8741
|
this.audioDir = join13(dir, AUDIO_DIR);
|
|
8701
8742
|
this.transcriptsDir = join13(dir, TRANSCRIPTS_DIR);
|
|
8743
|
+
this.summariesDir = join13(dir, SUMMARIES_DIR);
|
|
8702
8744
|
this.indexPath = join13(dir, INDEX_FILE);
|
|
8703
8745
|
}
|
|
8704
8746
|
dir;
|
|
8705
8747
|
audioDir;
|
|
8706
8748
|
transcriptsDir;
|
|
8749
|
+
summariesDir;
|
|
8707
8750
|
indexPath;
|
|
8708
8751
|
index = { recordings: [] };
|
|
8709
8752
|
/** 初始化目录结构并加载索引 */
|
|
8710
8753
|
async init() {
|
|
8711
8754
|
mkdirSync10(this.audioDir, { recursive: true });
|
|
8712
8755
|
mkdirSync10(this.transcriptsDir, { recursive: true });
|
|
8756
|
+
mkdirSync10(this.summariesDir, { recursive: true });
|
|
8713
8757
|
this.loadIndex();
|
|
8714
8758
|
this.logger.info(
|
|
8715
8759
|
`\u5F55\u97F3\u5B58\u50A8\u5DF2\u521D\u59CB\u5316: ${this.dir}\uFF08\u5171 ${this.index.recordings.length} \u6761\u8BB0\u5F55\uFF09`
|
|
@@ -8723,6 +8767,10 @@ var RecordingStorage = class {
|
|
|
8723
8767
|
getTranscriptsDir() {
|
|
8724
8768
|
return this.transcriptsDir;
|
|
8725
8769
|
}
|
|
8770
|
+
/** 获取摘要目录路径 */
|
|
8771
|
+
getSummariesDir() {
|
|
8772
|
+
return this.summariesDir;
|
|
8773
|
+
}
|
|
8726
8774
|
// ─── 录音入库 ───
|
|
8727
8775
|
/**
|
|
8728
8776
|
* 收到 recordings.sync 后,将元数据写入索引。
|
|
@@ -8733,9 +8781,17 @@ var RecordingStorage = class {
|
|
|
8733
8781
|
const id = recordingId;
|
|
8734
8782
|
const existing = this.findById(id);
|
|
8735
8783
|
if (existing) {
|
|
8784
|
+
if (existing.transcriptFile) {
|
|
8785
|
+
rmSync5(join13(this.dir, existing.transcriptFile), { force: true });
|
|
8786
|
+
}
|
|
8787
|
+
if (existing.summaryFile) {
|
|
8788
|
+
rmSync5(join13(this.dir, existing.summaryFile), { force: true });
|
|
8789
|
+
}
|
|
8736
8790
|
existing.metadata = metadata;
|
|
8737
8791
|
existing.status = "syncing_openclaw";
|
|
8738
8792
|
existing.transcriptFile = void 0;
|
|
8793
|
+
existing.summaryFile = void 0;
|
|
8794
|
+
existing.title = void 0;
|
|
8739
8795
|
existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8740
8796
|
this.logger.info(`\u5F55\u97F3\u5143\u6570\u636E\u5DF2\u66F4\u65B0: ${id}`);
|
|
8741
8797
|
} else {
|
|
@@ -8802,6 +8858,50 @@ var RecordingStorage = class {
|
|
|
8802
8858
|
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8803
8859
|
this.saveIndex();
|
|
8804
8860
|
}
|
|
8861
|
+
/**
|
|
8862
|
+
* 记录摘要文件路径
|
|
8863
|
+
*/
|
|
8864
|
+
setSummaryFile(recordingId, filename) {
|
|
8865
|
+
const entry = this.findById(recordingId);
|
|
8866
|
+
if (!entry) return;
|
|
8867
|
+
const nextSummaryFile = `${SUMMARIES_DIR}/${filename}`;
|
|
8868
|
+
if (entry.summaryFile && entry.summaryFile !== nextSummaryFile) {
|
|
8869
|
+
rmSync5(join13(this.dir, entry.summaryFile), { force: true });
|
|
8870
|
+
}
|
|
8871
|
+
entry.summaryFile = nextSummaryFile;
|
|
8872
|
+
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8873
|
+
this.saveIndex();
|
|
8874
|
+
}
|
|
8875
|
+
/**
|
|
8876
|
+
* 记录转写标题
|
|
8877
|
+
*/
|
|
8878
|
+
setTitle(recordingId, title) {
|
|
8879
|
+
const entry = this.findById(recordingId);
|
|
8880
|
+
if (!entry) return;
|
|
8881
|
+
entry.title = title?.trim() || void 0;
|
|
8882
|
+
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8883
|
+
this.saveIndex();
|
|
8884
|
+
}
|
|
8885
|
+
/**
|
|
8886
|
+
* 读取摘要文本
|
|
8887
|
+
*/
|
|
8888
|
+
readSummary(recordingId) {
|
|
8889
|
+
const entry = this.findById(recordingId);
|
|
8890
|
+
if (!entry?.summaryFile) return void 0;
|
|
8891
|
+
const summary = this.readRelativeTextFile(entry.summaryFile);
|
|
8892
|
+
return summary?.trim() || void 0;
|
|
8893
|
+
}
|
|
8894
|
+
/**
|
|
8895
|
+
* 从 transcript Markdown 中提取正文。
|
|
8896
|
+
*/
|
|
8897
|
+
readTranscript(recordingId) {
|
|
8898
|
+
const entry = this.findById(recordingId);
|
|
8899
|
+
if (!entry?.transcriptFile) return void 0;
|
|
8900
|
+
const markdown = this.readRelativeTextFile(entry.transcriptFile);
|
|
8901
|
+
if (!markdown) return void 0;
|
|
8902
|
+
const transcript = extractTranscriptContent(markdown);
|
|
8903
|
+
return transcript || void 0;
|
|
8904
|
+
}
|
|
8805
8905
|
// ─── 查询 ───
|
|
8806
8906
|
findById(id) {
|
|
8807
8907
|
return this.index.recordings.find((r) => r.id === id);
|
|
@@ -8851,10 +8951,15 @@ var RecordingStorage = class {
|
|
|
8851
8951
|
const transcriptPath = join13(this.dir, entry.transcriptFile);
|
|
8852
8952
|
rmSync5(transcriptPath, { force: true });
|
|
8853
8953
|
}
|
|
8954
|
+
if (entry.summaryFile) {
|
|
8955
|
+
const summaryPath = join13(this.dir, entry.summaryFile);
|
|
8956
|
+
rmSync5(summaryPath, { force: true });
|
|
8957
|
+
}
|
|
8854
8958
|
if (opts?.localOnly) {
|
|
8855
8959
|
entry.audioFile = void 0;
|
|
8856
8960
|
entry.srtFile = void 0;
|
|
8857
8961
|
entry.transcriptFile = void 0;
|
|
8962
|
+
entry.summaryFile = void 0;
|
|
8858
8963
|
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8859
8964
|
this.saveIndex();
|
|
8860
8965
|
this.logger.info(`\u5F55\u97F3\u672C\u5730\u6587\u4EF6\u5DF2\u5220\u9664\uFF08\u4FDD\u7559\u7D22\u5F15\uFF09: ${recordingId}`);
|
|
@@ -8889,6 +8994,12 @@ var RecordingStorage = class {
|
|
|
8889
8994
|
const safeSummary = summary.replace(/[/\\:*?"<>|]/g, "").trim().slice(0, 20);
|
|
8890
8995
|
return safeSummary ? `${recordingId}_${safeSummary}.md` : `${recordingId}.md`;
|
|
8891
8996
|
}
|
|
8997
|
+
/**
|
|
8998
|
+
* 生成摘要文件名
|
|
8999
|
+
*/
|
|
9000
|
+
buildSummaryFilename(recordingId) {
|
|
9001
|
+
return `${recordingId}.md`;
|
|
9002
|
+
}
|
|
8892
9003
|
/**
|
|
8893
9004
|
* 获取音频文件的绝对路径。ossUrl 用于推断文件扩展名
|
|
8894
9005
|
*/
|
|
@@ -8901,6 +9012,12 @@ var RecordingStorage = class {
|
|
|
8901
9012
|
getSrtFilePath(recordingId) {
|
|
8902
9013
|
return join13(this.audioDir, this.buildSrtFilename(recordingId));
|
|
8903
9014
|
}
|
|
9015
|
+
/**
|
|
9016
|
+
* 获取摘要文件的绝对路径
|
|
9017
|
+
*/
|
|
9018
|
+
getSummaryFilePath(recordingId) {
|
|
9019
|
+
return join13(this.summariesDir, this.buildSummaryFilename(recordingId));
|
|
9020
|
+
}
|
|
8904
9021
|
// ─── Persistence ───
|
|
8905
9022
|
loadIndex() {
|
|
8906
9023
|
if (!existsSync17(this.indexPath)) {
|
|
@@ -8910,6 +9027,7 @@ var RecordingStorage = class {
|
|
|
8910
9027
|
try {
|
|
8911
9028
|
const raw = JSON.parse(readFileSync18(this.indexPath, "utf-8"));
|
|
8912
9029
|
if (raw && Array.isArray(raw.recordings)) {
|
|
9030
|
+
let needsRewrite = false;
|
|
8913
9031
|
const normalized = raw.recordings.filter((entry) => entry && typeof entry === "object").map((entry) => {
|
|
8914
9032
|
const compacted = {
|
|
8915
9033
|
id: entry.id,
|
|
@@ -8923,13 +9041,37 @@ var RecordingStorage = class {
|
|
|
8923
9041
|
if (typeof entry.transcriptFile === "string") {
|
|
8924
9042
|
compacted.transcriptFile = entry.transcriptFile;
|
|
8925
9043
|
}
|
|
9044
|
+
if (typeof entry.summaryFile === "string") {
|
|
9045
|
+
compacted.summaryFile = entry.summaryFile;
|
|
9046
|
+
} else if (typeof entry.summary === "string" && entry.summary.trim()) {
|
|
9047
|
+
const summaryFilename = this.buildSummaryFilename(entry.id);
|
|
9048
|
+
writeFileSync11(
|
|
9049
|
+
join13(this.summariesDir, summaryFilename),
|
|
9050
|
+
entry.summary.trim(),
|
|
9051
|
+
"utf-8"
|
|
9052
|
+
);
|
|
9053
|
+
compacted.summaryFile = `${SUMMARIES_DIR}/${summaryFilename}`;
|
|
9054
|
+
needsRewrite = true;
|
|
9055
|
+
}
|
|
9056
|
+
if (typeof entry.title === "string" && entry.title.trim()) {
|
|
9057
|
+
compacted.title = entry.title.trim();
|
|
9058
|
+
} else {
|
|
9059
|
+
const derivedTitle = deriveTitleFromTranscriptPath(
|
|
9060
|
+
compacted.transcriptFile,
|
|
9061
|
+
compacted.id
|
|
9062
|
+
);
|
|
9063
|
+
if (derivedTitle) {
|
|
9064
|
+
compacted.title = derivedTitle;
|
|
9065
|
+
needsRewrite = true;
|
|
9066
|
+
}
|
|
9067
|
+
}
|
|
8926
9068
|
return compacted;
|
|
8927
9069
|
});
|
|
8928
9070
|
const hadLargeFields = raw.recordings.some(
|
|
8929
9071
|
(entry) => entry && typeof entry === "object" && ("transcript" in entry || "summary" in entry)
|
|
8930
9072
|
);
|
|
8931
9073
|
this.index = { recordings: normalized };
|
|
8932
|
-
if (hadLargeFields) {
|
|
9074
|
+
if (hadLargeFields || needsRewrite) {
|
|
8933
9075
|
this.saveIndex();
|
|
8934
9076
|
}
|
|
8935
9077
|
} else {
|
|
@@ -8940,6 +9082,13 @@ var RecordingStorage = class {
|
|
|
8940
9082
|
this.index = { recordings: [] };
|
|
8941
9083
|
}
|
|
8942
9084
|
}
|
|
9085
|
+
readRelativeTextFile(relativePath) {
|
|
9086
|
+
try {
|
|
9087
|
+
return readFileSync18(join13(this.dir, relativePath), "utf-8");
|
|
9088
|
+
} catch {
|
|
9089
|
+
return void 0;
|
|
9090
|
+
}
|
|
9091
|
+
}
|
|
8943
9092
|
saveIndex() {
|
|
8944
9093
|
writeFileSync11(
|
|
8945
9094
|
this.indexPath,
|
|
@@ -9151,11 +9300,16 @@ async function triggerTranscription(recordingId, storage, asrConfig, logger, opt
|
|
|
9151
9300
|
durationSec: entry.metadata.duration_sec,
|
|
9152
9301
|
createdAt: entry.metadata.created_at,
|
|
9153
9302
|
transcriptsDir: storage.getTranscriptsDir(),
|
|
9303
|
+
summariesDir: storage.getSummariesDir(),
|
|
9154
9304
|
recordingId,
|
|
9155
9305
|
logger
|
|
9156
9306
|
});
|
|
9157
9307
|
if (result.ok && result.transcriptFilename) {
|
|
9158
9308
|
storage.setTranscriptFile(recordingId, result.transcriptFilename);
|
|
9309
|
+
if (result.summaryFilename) {
|
|
9310
|
+
storage.setSummaryFile(recordingId, result.summaryFilename);
|
|
9311
|
+
}
|
|
9312
|
+
storage.setTitle(recordingId, result.title);
|
|
9159
9313
|
storage.updateStatus(recordingId, "transcribed");
|
|
9160
9314
|
emitRecordingStatus(
|
|
9161
9315
|
recordingId,
|
|
@@ -10959,7 +11113,7 @@ function buildRecordingListItem(entry) {
|
|
|
10959
11113
|
updatedAt: entry.updatedAt
|
|
10960
11114
|
};
|
|
10961
11115
|
}
|
|
10962
|
-
function buildRecordingDetail(entry) {
|
|
11116
|
+
function buildRecordingDetail(entry, extras) {
|
|
10963
11117
|
return {
|
|
10964
11118
|
recordingId: entry.id,
|
|
10965
11119
|
name: entry.metadata.name,
|
|
@@ -10974,6 +11128,10 @@ function buildRecordingDetail(entry) {
|
|
|
10974
11128
|
audioFile: entry.audioFile,
|
|
10975
11129
|
srtFile: entry.srtFile,
|
|
10976
11130
|
transcriptFile: entry.transcriptFile,
|
|
11131
|
+
summaryFile: entry.summaryFile,
|
|
11132
|
+
title: entry.title,
|
|
11133
|
+
summary: extras?.summary,
|
|
11134
|
+
transcript: extras?.transcript,
|
|
10977
11135
|
ingestedAt: entry.ingestedAt,
|
|
10978
11136
|
updatedAt: entry.updatedAt
|
|
10979
11137
|
};
|
|
@@ -11081,7 +11239,10 @@ function registerRecordingInterfaces(deps) {
|
|
|
11081
11239
|
});
|
|
11082
11240
|
return;
|
|
11083
11241
|
}
|
|
11084
|
-
const {
|
|
11242
|
+
const {
|
|
11243
|
+
recordingId,
|
|
11244
|
+
includeTranscriptContent
|
|
11245
|
+
} = params;
|
|
11085
11246
|
if (!recordingId) {
|
|
11086
11247
|
respond(false, null, {
|
|
11087
11248
|
code: "INVALID_PARAMS",
|
|
@@ -11097,7 +11258,13 @@ function registerRecordingInterfaces(deps) {
|
|
|
11097
11258
|
});
|
|
11098
11259
|
return;
|
|
11099
11260
|
}
|
|
11100
|
-
respond(
|
|
11261
|
+
respond(
|
|
11262
|
+
true,
|
|
11263
|
+
buildRecordingDetail(entry, {
|
|
11264
|
+
summary: recordingStorage.readSummary(recordingId),
|
|
11265
|
+
transcript: includeTranscriptContent ? recordingStorage.readTranscript(recordingId) : void 0
|
|
11266
|
+
})
|
|
11267
|
+
);
|
|
11101
11268
|
}
|
|
11102
11269
|
);
|
|
11103
11270
|
registerGatewayMethod(
|