@yoooclaw/phone-notifications 1.10.0-beta.21 → 1.10.0-beta.23
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 +766 -52
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -272,6 +272,154 @@ var init_credentials = __esm({
|
|
|
272
272
|
}
|
|
273
273
|
});
|
|
274
274
|
|
|
275
|
+
// src/recording/transcript-document.ts
|
|
276
|
+
function buildTranscriptDataFilename(recordingId) {
|
|
277
|
+
return `${recordingId}.json`;
|
|
278
|
+
}
|
|
279
|
+
function buildTranscriptDocument(params) {
|
|
280
|
+
const segments = normalizeSegments(params.segments);
|
|
281
|
+
const text = normalizeOptionalText(params.text) ?? joinSegmentsText(segments) ?? "";
|
|
282
|
+
return {
|
|
283
|
+
schemaVersion: TRANSCRIPT_DOCUMENT_SCHEMA_VERSION,
|
|
284
|
+
recordingId: params.recordingId,
|
|
285
|
+
generatedAt: params.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
286
|
+
source: {
|
|
287
|
+
provider: params.source.provider,
|
|
288
|
+
taskId: normalizeOptionalText(params.source.taskId),
|
|
289
|
+
requestId: normalizeOptionalText(params.source.requestId),
|
|
290
|
+
status: normalizeOptionalText(params.source.status)
|
|
291
|
+
},
|
|
292
|
+
normalized: {
|
|
293
|
+
title: normalizeOptionalText(params.title),
|
|
294
|
+
category: normalizeOptionalText(params.category),
|
|
295
|
+
summary: normalizeOptionalText(params.summary),
|
|
296
|
+
text,
|
|
297
|
+
segments
|
|
298
|
+
},
|
|
299
|
+
raw: params.raw
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function parseTranscriptDocument(value) {
|
|
303
|
+
const parsed = typeof value === "string" ? parseJson(value) : value;
|
|
304
|
+
if (!parsed || typeof parsed !== "object") {
|
|
305
|
+
return void 0;
|
|
306
|
+
}
|
|
307
|
+
const doc = parsed;
|
|
308
|
+
const recordingId = normalizeOptionalText(doc.recordingId);
|
|
309
|
+
const generatedAt = normalizeOptionalText(doc.generatedAt);
|
|
310
|
+
const source = normalizeSource(doc.source);
|
|
311
|
+
const normalized = normalizeDocumentBody(doc.normalized);
|
|
312
|
+
if (!recordingId || !generatedAt || !source || !normalized) {
|
|
313
|
+
return void 0;
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
schemaVersion: typeof doc.schemaVersion === "number" ? doc.schemaVersion : TRANSCRIPT_DOCUMENT_SCHEMA_VERSION,
|
|
317
|
+
recordingId,
|
|
318
|
+
generatedAt,
|
|
319
|
+
source,
|
|
320
|
+
normalized,
|
|
321
|
+
raw: doc.raw
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function extractTranscriptTextFromDocument(doc) {
|
|
325
|
+
if (!doc) return void 0;
|
|
326
|
+
return normalizeOptionalText(doc.normalized.text) ?? joinSegmentsText(doc.normalized.segments);
|
|
327
|
+
}
|
|
328
|
+
function extractTranscriptSummaryFromDocument(doc) {
|
|
329
|
+
if (!doc) return void 0;
|
|
330
|
+
return normalizeOptionalText(doc.normalized.summary);
|
|
331
|
+
}
|
|
332
|
+
function extractTranscriptTitleFromDocument(doc) {
|
|
333
|
+
if (!doc) return void 0;
|
|
334
|
+
return normalizeOptionalText(doc.normalized.title);
|
|
335
|
+
}
|
|
336
|
+
function parseJson(value) {
|
|
337
|
+
try {
|
|
338
|
+
return JSON.parse(value);
|
|
339
|
+
} catch {
|
|
340
|
+
return void 0;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function normalizeSource(value) {
|
|
344
|
+
if (!value || typeof value !== "object") {
|
|
345
|
+
return void 0;
|
|
346
|
+
}
|
|
347
|
+
const source = value;
|
|
348
|
+
const provider = normalizeOptionalText(source.provider);
|
|
349
|
+
if (!provider) {
|
|
350
|
+
return void 0;
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
provider,
|
|
354
|
+
taskId: normalizeOptionalText(source.taskId),
|
|
355
|
+
requestId: normalizeOptionalText(source.requestId),
|
|
356
|
+
status: normalizeOptionalText(source.status)
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function normalizeDocumentBody(value) {
|
|
360
|
+
if (!value || typeof value !== "object") {
|
|
361
|
+
return void 0;
|
|
362
|
+
}
|
|
363
|
+
const normalized = value;
|
|
364
|
+
const segments = normalizeSegments(normalized.segments);
|
|
365
|
+
const text = normalizeOptionalText(normalized.text) ?? joinSegmentsText(segments);
|
|
366
|
+
if (!text) {
|
|
367
|
+
return void 0;
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
title: normalizeOptionalText(normalized.title),
|
|
371
|
+
category: normalizeOptionalText(normalized.category),
|
|
372
|
+
summary: normalizeOptionalText(normalized.summary),
|
|
373
|
+
text,
|
|
374
|
+
segments
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
function normalizeSegments(value) {
|
|
378
|
+
if (!Array.isArray(value)) {
|
|
379
|
+
return [];
|
|
380
|
+
}
|
|
381
|
+
return value.map((item) => normalizeSegment(item)).filter((item) => !!item);
|
|
382
|
+
}
|
|
383
|
+
function normalizeSegment(value) {
|
|
384
|
+
if (!value || typeof value !== "object") {
|
|
385
|
+
return void 0;
|
|
386
|
+
}
|
|
387
|
+
const segment = value;
|
|
388
|
+
const text = normalizeOptionalText(segment.text);
|
|
389
|
+
if (!text) {
|
|
390
|
+
return void 0;
|
|
391
|
+
}
|
|
392
|
+
return {
|
|
393
|
+
text,
|
|
394
|
+
startMs: normalizeOptionalNumber(segment.startMs),
|
|
395
|
+
endMs: normalizeOptionalNumber(segment.endMs),
|
|
396
|
+
speakerId: normalizeOptionalInteger(segment.speakerId)
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function joinSegmentsText(segments) {
|
|
400
|
+
const parts = segments.map((segment) => normalizeOptionalText(segment.text)).filter((segment) => !!segment);
|
|
401
|
+
if (parts.length === 0) {
|
|
402
|
+
return void 0;
|
|
403
|
+
}
|
|
404
|
+
return parts.join("\n\n");
|
|
405
|
+
}
|
|
406
|
+
function normalizeOptionalText(value) {
|
|
407
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
408
|
+
}
|
|
409
|
+
function normalizeOptionalNumber(value) {
|
|
410
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
411
|
+
}
|
|
412
|
+
function normalizeOptionalInteger(value) {
|
|
413
|
+
return Number.isInteger(value) ? Number(value) : void 0;
|
|
414
|
+
}
|
|
415
|
+
var TRANSCRIPT_DOCUMENT_SCHEMA_VERSION;
|
|
416
|
+
var init_transcript_document = __esm({
|
|
417
|
+
"src/recording/transcript-document.ts"() {
|
|
418
|
+
"use strict";
|
|
419
|
+
TRANSCRIPT_DOCUMENT_SCHEMA_VERSION = 1;
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
|
|
275
423
|
// src/recording/whisper-local.ts
|
|
276
424
|
var whisper_local_exports = {};
|
|
277
425
|
__export(whisper_local_exports, {
|
|
@@ -1090,8 +1238,11 @@ function buildTranscriptMarkdown(result, markers, recordingName, durationSec, cr
|
|
|
1090
1238
|
lines.push("");
|
|
1091
1239
|
markerIdx++;
|
|
1092
1240
|
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1241
|
+
const segmentText = formatTranscriptSegmentText(seg);
|
|
1242
|
+
if (segmentText) {
|
|
1243
|
+
lines.push(segmentText);
|
|
1244
|
+
lines.push("");
|
|
1245
|
+
}
|
|
1095
1246
|
}
|
|
1096
1247
|
while (markerIdx < sortedMarkers.length) {
|
|
1097
1248
|
const m = sortedMarkers[markerIdx];
|
|
@@ -1133,6 +1284,8 @@ async function runTranscriptionWorkflow(params) {
|
|
|
1133
1284
|
durationSec,
|
|
1134
1285
|
createdAt,
|
|
1135
1286
|
transcriptsDir,
|
|
1287
|
+
transcriptDataDir,
|
|
1288
|
+
summariesDir,
|
|
1136
1289
|
recordingId,
|
|
1137
1290
|
logger
|
|
1138
1291
|
} = params;
|
|
@@ -1142,9 +1295,27 @@ async function runTranscriptionWorkflow(params) {
|
|
|
1142
1295
|
if (!result.ok) {
|
|
1143
1296
|
return { ok: false, error: result.error };
|
|
1144
1297
|
}
|
|
1145
|
-
const title =
|
|
1146
|
-
const summary =
|
|
1298
|
+
const title = normalizeOptionalText2(result.summary) ? normalizeOptionalText2(result.summary) : extractSummary(result.text ?? "");
|
|
1299
|
+
const summary = normalizeOptionalText2(result.summaryText) ? normalizeOptionalText2(result.summaryText) : extractTranscriptSummary(result.text ?? "");
|
|
1147
1300
|
result.summary = title;
|
|
1301
|
+
const transcriptData = buildTranscriptDocument({
|
|
1302
|
+
recordingId,
|
|
1303
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1304
|
+
source: result.sourceInfo ?? {
|
|
1305
|
+
provider: config.mode === "api" ? "model-proxy" : config.mode
|
|
1306
|
+
},
|
|
1307
|
+
title,
|
|
1308
|
+
category: result.category,
|
|
1309
|
+
summary,
|
|
1310
|
+
text: result.text,
|
|
1311
|
+
segments: (result.segments ?? []).map((segment) => ({
|
|
1312
|
+
text: segment.text,
|
|
1313
|
+
startMs: segment.start_ms,
|
|
1314
|
+
endMs: segment.end_ms,
|
|
1315
|
+
speakerId: segment.speaker_id
|
|
1316
|
+
})),
|
|
1317
|
+
raw: result.rawResponse
|
|
1318
|
+
});
|
|
1148
1319
|
const markdown = buildTranscriptMarkdown(
|
|
1149
1320
|
result,
|
|
1150
1321
|
markers,
|
|
@@ -1152,21 +1323,35 @@ async function runTranscriptionWorkflow(params) {
|
|
|
1152
1323
|
durationSec,
|
|
1153
1324
|
createdAt
|
|
1154
1325
|
);
|
|
1326
|
+
const transcriptDataFilename = buildTranscriptDataFilename(recordingId);
|
|
1327
|
+
const transcriptDataPath = join15(transcriptDataDir, transcriptDataFilename);
|
|
1328
|
+
writeFileSync12(
|
|
1329
|
+
transcriptDataPath,
|
|
1330
|
+
JSON.stringify(transcriptData, null, 2),
|
|
1331
|
+
"utf-8"
|
|
1332
|
+
);
|
|
1333
|
+
logger.info(`[asr] \u8F6C\u5199 JSON \u5DF2\u5199\u5165: ${transcriptDataPath}`);
|
|
1155
1334
|
const safeSummary = title.replace(/[/\\:*?"<>|]/g, "").trim().slice(0, 20);
|
|
1156
1335
|
const filename = safeSummary ? `${recordingId}_${safeSummary}.md` : `${recordingId}.md`;
|
|
1157
1336
|
const filePath = join15(transcriptsDir, filename);
|
|
1158
1337
|
writeFileSync12(filePath, markdown, "utf-8");
|
|
1159
1338
|
logger.info(`[asr] \u8F6C\u5199\u6587\u672C\u5DF2\u5199\u5165: ${filePath}`);
|
|
1339
|
+
const summaryFilename = `${recordingId}.md`;
|
|
1340
|
+
const summaryFilePath = join15(summariesDir, summaryFilename);
|
|
1341
|
+
writeFileSync12(summaryFilePath, summary, "utf-8");
|
|
1342
|
+
logger.info(`[asr] \u6458\u8981\u6587\u672C\u5DF2\u5199\u5165: ${summaryFilePath}`);
|
|
1160
1343
|
return {
|
|
1161
1344
|
ok: true,
|
|
1162
1345
|
transcriptFilename: filename,
|
|
1163
|
-
|
|
1346
|
+
transcriptDataFilename,
|
|
1347
|
+
summaryFilename,
|
|
1348
|
+
transcript: transcriptData.normalized.text,
|
|
1164
1349
|
summary,
|
|
1165
1350
|
title
|
|
1166
1351
|
};
|
|
1167
1352
|
}
|
|
1168
1353
|
async function transcribeWithModelProxy(audioOssUrl, apiConfig, logger) {
|
|
1169
|
-
const normalizedAudioOssUrl =
|
|
1354
|
+
const normalizedAudioOssUrl = normalizeOptionalText2(audioOssUrl);
|
|
1170
1355
|
if (!normalizedAudioOssUrl) {
|
|
1171
1356
|
return { ok: false, error: "API \u6A21\u5F0F\u7F3A\u5C11 audioOssUrl\uFF0C\u65E0\u6CD5\u8C03\u7528 model-proxy" };
|
|
1172
1357
|
}
|
|
@@ -1199,8 +1384,8 @@ async function transcribeWithModelProxy(audioOssUrl, apiConfig, logger) {
|
|
|
1199
1384
|
`[asr-submit-response] \u63D0\u4EA4\u957F\u5F55\u97F3\u4EFB\u52A1\u54CD\u5E94: ${stringifyForLog(raw) ?? "{}"}`
|
|
1200
1385
|
);
|
|
1201
1386
|
const data = unwrapResponse(raw);
|
|
1202
|
-
const taskId =
|
|
1203
|
-
const requestId =
|
|
1387
|
+
const taskId = normalizeOptionalText2(data?.taskId);
|
|
1388
|
+
const requestId = normalizeOptionalText2(data?.requestId);
|
|
1204
1389
|
const status = normalizeLongRecordingStatus(data?.status);
|
|
1205
1390
|
if (!taskId) {
|
|
1206
1391
|
return {
|
|
@@ -1238,6 +1423,10 @@ async function transcribeWithWhisperLocal2(audioFilePath, config, logger) {
|
|
|
1238
1423
|
if (result.ok && result.text) {
|
|
1239
1424
|
const { extractSummary: extract } = await Promise.resolve().then(() => (init_asr(), asr_exports));
|
|
1240
1425
|
result.summary = extract(result.text);
|
|
1426
|
+
result.sourceInfo = {
|
|
1427
|
+
provider: "whisper-local",
|
|
1428
|
+
status: "SUCCEEDED"
|
|
1429
|
+
};
|
|
1241
1430
|
}
|
|
1242
1431
|
return result;
|
|
1243
1432
|
}
|
|
@@ -1301,7 +1490,7 @@ async function pollLongRecordingTaskResult(params) {
|
|
|
1301
1490
|
const raw = await res.json();
|
|
1302
1491
|
const data = unwrapResponse(raw);
|
|
1303
1492
|
const status = normalizeLongRecordingStatus(data?.status) ?? "UNKNOWN";
|
|
1304
|
-
const requestId =
|
|
1493
|
+
const requestId = normalizeOptionalText2(data?.requestId) ?? initialRequestId;
|
|
1305
1494
|
if (status !== lastStatus) {
|
|
1306
1495
|
logger.info(
|
|
1307
1496
|
`[asr-query-response] \u957F\u5F55\u97F3\u4EFB\u52A1\u67E5\u8BE2\u54CD\u5E94: taskId=${taskId}, attempt=${attempt}, body=${stringifyForLog(raw) ?? "{}"}`
|
|
@@ -1336,19 +1525,24 @@ async function pollLongRecordingTaskResult(params) {
|
|
|
1336
1525
|
};
|
|
1337
1526
|
}
|
|
1338
1527
|
function buildLongRecordingSuccessResult(taskId, requestId, data, logger) {
|
|
1339
|
-
const
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
const
|
|
1528
|
+
const sourceTextList = normalizeLongRecordingSourceTextList(
|
|
1529
|
+
data?.recordResult?.sourceTextList
|
|
1530
|
+
);
|
|
1531
|
+
const listResult = extractLongRecordingTextFromList(sourceTextList);
|
|
1532
|
+
const sourceText = normalizeOptionalText2(data?.recordResult?.sourceText);
|
|
1533
|
+
const summaryText = normalizeOptionalText2(data?.recordResult?.summaryResult);
|
|
1534
|
+
const title = normalizeOptionalText2(data?.recordResult?.title);
|
|
1535
|
+
const category = normalizeOptionalText2(data?.recordResult?.category);
|
|
1536
|
+
const text = listResult.text ?? sourceText ?? summaryText;
|
|
1343
1537
|
if (!text) {
|
|
1344
1538
|
return {
|
|
1345
1539
|
ok: false,
|
|
1346
|
-
error: "Model Proxy ASR \u4EFB\u52A1\u5DF2\u5B8C\u6210\uFF0C\u4F46\u672A\u8FD4\u56DE
|
|
1540
|
+
error: "Model Proxy ASR \u4EFB\u52A1\u5DF2\u5B8C\u6210\uFF0C\u4F46\u672A\u8FD4\u56DE sourceTextList\u3001sourceText \u6216 summaryResult"
|
|
1347
1541
|
};
|
|
1348
1542
|
}
|
|
1349
|
-
if (!sourceText && summaryText) {
|
|
1543
|
+
if (!listResult.text && !sourceText && summaryText) {
|
|
1350
1544
|
logger.warn(
|
|
1351
|
-
`[asr] Model Proxy \u957F\u5F55\u97F3\u7ED3\u679C\u7F3A\u5C11 sourceText\uFF0C\u5DF2\u56DE\u9000\u4F7F\u7528 summaryResult \u4F5C\u4E3A\u8F6C\u5199\u6587\u672C: taskId=${taskId}`
|
|
1545
|
+
`[asr] Model Proxy \u957F\u5F55\u97F3\u7ED3\u679C\u7F3A\u5C11 sourceTextList/sourceText\uFF0C\u5DF2\u56DE\u9000\u4F7F\u7528 summaryResult \u4F5C\u4E3A\u8F6C\u5199\u6587\u672C: taskId=${taskId}`
|
|
1352
1546
|
);
|
|
1353
1547
|
}
|
|
1354
1548
|
logger.info(
|
|
@@ -1357,13 +1551,21 @@ function buildLongRecordingSuccessResult(taskId, requestId, data, logger) {
|
|
|
1357
1551
|
return {
|
|
1358
1552
|
ok: true,
|
|
1359
1553
|
text,
|
|
1360
|
-
segments:
|
|
1554
|
+
segments: listResult.segments,
|
|
1361
1555
|
summary: title ?? extractSummary(text),
|
|
1362
|
-
summaryText: summaryText ?? extractTranscriptSummary(text)
|
|
1556
|
+
summaryText: summaryText ?? extractTranscriptSummary(text),
|
|
1557
|
+
category,
|
|
1558
|
+
sourceInfo: {
|
|
1559
|
+
provider: "model-proxy",
|
|
1560
|
+
taskId,
|
|
1561
|
+
requestId,
|
|
1562
|
+
status: "SUCCEEDED"
|
|
1563
|
+
},
|
|
1564
|
+
rawResponse: data
|
|
1363
1565
|
};
|
|
1364
1566
|
}
|
|
1365
1567
|
function buildLongRecordingStatusError(data, status) {
|
|
1366
|
-
const errorMessage =
|
|
1568
|
+
const errorMessage = normalizeOptionalText2(data?.errorMessage);
|
|
1367
1569
|
return errorMessage ? `Model Proxy ASR ${status}: ${errorMessage}` : `Model Proxy ASR ${status}`;
|
|
1368
1570
|
}
|
|
1369
1571
|
function unwrapResponse(payload) {
|
|
@@ -1378,9 +1580,56 @@ function normalizeApiKeyHeaderValue(apiKey) {
|
|
|
1378
1580
|
function normalizeLongRecordingStatus(status) {
|
|
1379
1581
|
return typeof status === "string" && status.trim() ? status.trim().toUpperCase() : void 0;
|
|
1380
1582
|
}
|
|
1381
|
-
function
|
|
1583
|
+
function normalizeOptionalText2(value) {
|
|
1382
1584
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
1383
1585
|
}
|
|
1586
|
+
function normalizeOptionalInteger2(value) {
|
|
1587
|
+
return Number.isInteger(value) ? Number(value) : void 0;
|
|
1588
|
+
}
|
|
1589
|
+
function normalizeOptionalNonNegativeNumber(value) {
|
|
1590
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
1591
|
+
}
|
|
1592
|
+
function normalizeLongRecordingSourceTextList(value) {
|
|
1593
|
+
if (!Array.isArray(value)) {
|
|
1594
|
+
return [];
|
|
1595
|
+
}
|
|
1596
|
+
return value.map((item) => {
|
|
1597
|
+
if (!item || typeof item !== "object") {
|
|
1598
|
+
return void 0;
|
|
1599
|
+
}
|
|
1600
|
+
const record = item;
|
|
1601
|
+
const content = normalizeOptionalText2(record.content);
|
|
1602
|
+
if (!content) {
|
|
1603
|
+
return void 0;
|
|
1604
|
+
}
|
|
1605
|
+
return {
|
|
1606
|
+
content,
|
|
1607
|
+
speakerId: normalizeOptionalInteger2(record.speakerId),
|
|
1608
|
+
startTime: normalizeOptionalNonNegativeNumber(record.startTime)
|
|
1609
|
+
};
|
|
1610
|
+
}).filter((item) => !!item);
|
|
1611
|
+
}
|
|
1612
|
+
function extractLongRecordingTextFromList(items) {
|
|
1613
|
+
if (items.length === 0) {
|
|
1614
|
+
return {
|
|
1615
|
+
segments: []
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
const segments = items.map((item, index) => {
|
|
1619
|
+
const startMs = item.startTime ?? 0;
|
|
1620
|
+
const nextStart = items[index + 1]?.startTime;
|
|
1621
|
+
return {
|
|
1622
|
+
start_ms: startMs,
|
|
1623
|
+
end_ms: nextStart ?? startMs,
|
|
1624
|
+
text: item.content,
|
|
1625
|
+
speaker_id: item.speakerId
|
|
1626
|
+
};
|
|
1627
|
+
});
|
|
1628
|
+
return {
|
|
1629
|
+
text: items.map((item) => item.content).join("\n\n"),
|
|
1630
|
+
segments
|
|
1631
|
+
};
|
|
1632
|
+
}
|
|
1384
1633
|
function stringifyForLog(value, maxLength = 500) {
|
|
1385
1634
|
if (value == null) {
|
|
1386
1635
|
return void 0;
|
|
@@ -1426,12 +1675,23 @@ function formatDuration(seconds) {
|
|
|
1426
1675
|
function hasText(value) {
|
|
1427
1676
|
return typeof value === "string" && value.trim().length > 0;
|
|
1428
1677
|
}
|
|
1678
|
+
function formatTranscriptSegmentText(segment) {
|
|
1679
|
+
const text = segment.text.trim();
|
|
1680
|
+
if (!text) {
|
|
1681
|
+
return text;
|
|
1682
|
+
}
|
|
1683
|
+
if (typeof segment.speaker_id === "number") {
|
|
1684
|
+
return `\u8BF4\u8BDD\u4EBA${segment.speaker_id}\uFF1A${text}`;
|
|
1685
|
+
}
|
|
1686
|
+
return text;
|
|
1687
|
+
}
|
|
1429
1688
|
var DEFAULT_LONG_RECORDING_POLL_INTERVAL_MS, DEFAULT_LONG_RECORDING_MAX_POLL_ATTEMPTS, LONG_RECORDING_RUNNING_STATUSES, LONG_RECORDING_TERMINAL_FAILURE_STATUSES;
|
|
1430
1689
|
var init_asr = __esm({
|
|
1431
1690
|
"src/recording/asr.ts"() {
|
|
1432
1691
|
"use strict";
|
|
1433
1692
|
init_credentials();
|
|
1434
1693
|
init_env();
|
|
1694
|
+
init_transcript_document();
|
|
1435
1695
|
DEFAULT_LONG_RECORDING_POLL_INTERVAL_MS = 2e3;
|
|
1436
1696
|
DEFAULT_LONG_RECORDING_MAX_POLL_ATTEMPTS = 150;
|
|
1437
1697
|
LONG_RECORDING_RUNNING_STATUSES = /* @__PURE__ */ new Set(["PENDING", "RUNNING", "SUSPENDED"]);
|
|
@@ -5623,6 +5883,31 @@ if __name__ == "__main__":
|
|
|
5623
5883
|
}
|
|
5624
5884
|
|
|
5625
5885
|
// src/light-rules/storage.ts
|
|
5886
|
+
var LEGACY_DEFAULT_CRON_SCHEDULE = "*/5 * * * *";
|
|
5887
|
+
function legacyReadMatchRules(input) {
|
|
5888
|
+
return input.matchRules ?? {};
|
|
5889
|
+
}
|
|
5890
|
+
function legacyReadCronSchedule(input) {
|
|
5891
|
+
return input.cronSchedule ?? LEGACY_DEFAULT_CRON_SCHEDULE;
|
|
5892
|
+
}
|
|
5893
|
+
function legacyHasMatchRules(input) {
|
|
5894
|
+
return input.matchRules !== void 0;
|
|
5895
|
+
}
|
|
5896
|
+
function legacyHasCronSchedule(input) {
|
|
5897
|
+
return input.cronSchedule !== void 0;
|
|
5898
|
+
}
|
|
5899
|
+
function legacyAssignMatchRules(meta, matchRules) {
|
|
5900
|
+
meta.matchRules = matchRules;
|
|
5901
|
+
}
|
|
5902
|
+
function legacyAssignCronSchedule(meta, cronSchedule) {
|
|
5903
|
+
meta.cronSchedule = cronSchedule;
|
|
5904
|
+
}
|
|
5905
|
+
function legacyReadMetaMatchRules(meta) {
|
|
5906
|
+
return meta.matchRules ?? {};
|
|
5907
|
+
}
|
|
5908
|
+
function legacyReadMetaCronSchedule(meta) {
|
|
5909
|
+
return meta.cronSchedule;
|
|
5910
|
+
}
|
|
5626
5911
|
function resolveBaseDir(ctx) {
|
|
5627
5912
|
if (ctx.workspaceDir) return ctx.workspaceDir;
|
|
5628
5913
|
if (ctx.stateDir) {
|
|
@@ -5746,21 +6031,23 @@ function createLightRule(ctx, params) {
|
|
|
5746
6031
|
repeat_times: params.repeat_times
|
|
5747
6032
|
});
|
|
5748
6033
|
assertAncsRepeatTimes(repeatTimes);
|
|
6034
|
+
const effectiveMatchRules = legacyReadMatchRules(params);
|
|
6035
|
+
const effectiveCronSchedule = legacyReadCronSchedule(params);
|
|
5749
6036
|
const meta = {
|
|
5750
6037
|
name: params.name,
|
|
5751
6038
|
type: "light-rule",
|
|
5752
6039
|
description: params.description,
|
|
5753
|
-
matchRules: params.matchRules,
|
|
5754
6040
|
segments: params.segments,
|
|
5755
6041
|
repeat_times: repeatTimes,
|
|
5756
|
-
cronSchedule: params.cronSchedule,
|
|
5757
6042
|
enabled: true,
|
|
5758
6043
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5759
6044
|
};
|
|
6045
|
+
legacyAssignMatchRules(meta, effectiveMatchRules);
|
|
6046
|
+
legacyAssignCronSchedule(meta, effectiveCronSchedule);
|
|
5760
6047
|
writeMeta(taskDir, meta);
|
|
5761
6048
|
writeFileSync(
|
|
5762
6049
|
join3(taskDir, "fetch.py"),
|
|
5763
|
-
generateFetchPy(params.name,
|
|
6050
|
+
generateFetchPy(params.name, effectiveMatchRules),
|
|
5764
6051
|
"utf-8"
|
|
5765
6052
|
);
|
|
5766
6053
|
writeFileSync(
|
|
@@ -5774,7 +6061,7 @@ function createLightRule(ctx, params) {
|
|
|
5774
6061
|
action: "add",
|
|
5775
6062
|
job: {
|
|
5776
6063
|
name: `notif-${params.name}`,
|
|
5777
|
-
schedule:
|
|
6064
|
+
schedule: effectiveCronSchedule,
|
|
5778
6065
|
sessionTarget: "isolated",
|
|
5779
6066
|
message: `\u624B\u673A\u901A\u77E5\u5DF2\u7531\u72EC\u7ACB\u670D\u52A1\u5B9E\u65F6\u6355\u83B7\u5230 notifications/ \u76EE\u5F55\u7684 JSON \u6587\u4EF6\u4E2D\u3002
|
|
5780
6067
|
\u6267\u884C\uFF1Apython3 tasks/${params.name}/fetch.py --notifications-dir notifications
|
|
@@ -5797,8 +6084,8 @@ function updateLightRule(ctx, params) {
|
|
|
5797
6084
|
meta.description = params.description;
|
|
5798
6085
|
regenerateReadme = true;
|
|
5799
6086
|
}
|
|
5800
|
-
if (params
|
|
5801
|
-
meta
|
|
6087
|
+
if (legacyHasMatchRules(params)) {
|
|
6088
|
+
legacyAssignMatchRules(meta, legacyReadMatchRules(params));
|
|
5802
6089
|
regenerateFetch = true;
|
|
5803
6090
|
}
|
|
5804
6091
|
if (params.segments !== void 0) {
|
|
@@ -5813,8 +6100,8 @@ function updateLightRule(ctx, params) {
|
|
|
5813
6100
|
assertAncsRepeatTimes(meta.repeat_times);
|
|
5814
6101
|
regenerateReadme = true;
|
|
5815
6102
|
}
|
|
5816
|
-
if (params
|
|
5817
|
-
meta
|
|
6103
|
+
if (legacyHasCronSchedule(params)) {
|
|
6104
|
+
legacyAssignCronSchedule(meta, legacyReadCronSchedule(params));
|
|
5818
6105
|
}
|
|
5819
6106
|
if (params.enabled !== void 0) {
|
|
5820
6107
|
meta.enabled = params.enabled;
|
|
@@ -5824,7 +6111,7 @@ function updateLightRule(ctx, params) {
|
|
|
5824
6111
|
if (regenerateFetch) {
|
|
5825
6112
|
writeFileSync(
|
|
5826
6113
|
join3(taskDir, "fetch.py"),
|
|
5827
|
-
generateFetchPy(meta.name, meta
|
|
6114
|
+
generateFetchPy(meta.name, legacyReadMetaMatchRules(meta)),
|
|
5828
6115
|
"utf-8"
|
|
5829
6116
|
);
|
|
5830
6117
|
}
|
|
@@ -5835,11 +6122,11 @@ function updateLightRule(ctx, params) {
|
|
|
5835
6122
|
"utf-8"
|
|
5836
6123
|
);
|
|
5837
6124
|
}
|
|
5838
|
-
const cronHint = params
|
|
6125
|
+
const cronHint = legacyHasCronSchedule(params) ? {
|
|
5839
6126
|
action: "update",
|
|
5840
6127
|
job: {
|
|
5841
6128
|
name: `notif-${meta.name}`,
|
|
5842
|
-
schedule: meta
|
|
6129
|
+
schedule: legacyReadMetaCronSchedule(meta)
|
|
5843
6130
|
}
|
|
5844
6131
|
} : void 0;
|
|
5845
6132
|
return { meta, cronHint };
|
|
@@ -5881,7 +6168,7 @@ function resolveRuleIdentifier(params) {
|
|
|
5881
6168
|
}
|
|
5882
6169
|
return void 0;
|
|
5883
6170
|
}
|
|
5884
|
-
function registerLightRulesGateway(api,
|
|
6171
|
+
function registerLightRulesGateway(api, registry, logger, rememberBroadcast) {
|
|
5885
6172
|
const registerGatewayMethodWithBroadcastCapture = (method, handler) => {
|
|
5886
6173
|
api.registerGatewayMethod(method, (opts) => {
|
|
5887
6174
|
rememberBroadcast?.(opts.context?.broadcast);
|
|
@@ -5890,7 +6177,7 @@ function registerLightRulesGateway(api, ctx, logger, rememberBroadcast) {
|
|
|
5890
6177
|
};
|
|
5891
6178
|
registerGatewayMethodWithBroadcastCapture("lightrules.list", async ({ respond }) => {
|
|
5892
6179
|
try {
|
|
5893
|
-
const rules =
|
|
6180
|
+
const rules = registry.list().map((rule) => ({
|
|
5894
6181
|
...rule,
|
|
5895
6182
|
id: rule.name
|
|
5896
6183
|
}));
|
|
@@ -5934,7 +6221,7 @@ function registerLightRulesGateway(api, ctx, logger, rememberBroadcast) {
|
|
|
5934
6221
|
return;
|
|
5935
6222
|
}
|
|
5936
6223
|
try {
|
|
5937
|
-
const result =
|
|
6224
|
+
const result = await registry.create({
|
|
5938
6225
|
name,
|
|
5939
6226
|
description,
|
|
5940
6227
|
matchRules,
|
|
@@ -5994,7 +6281,7 @@ function registerLightRulesGateway(api, ctx, logger, rememberBroadcast) {
|
|
|
5994
6281
|
}
|
|
5995
6282
|
}
|
|
5996
6283
|
try {
|
|
5997
|
-
const result =
|
|
6284
|
+
const result = await registry.update({
|
|
5998
6285
|
name,
|
|
5999
6286
|
description,
|
|
6000
6287
|
matchRules,
|
|
@@ -6031,7 +6318,7 @@ function registerLightRulesGateway(api, ctx, logger, rememberBroadcast) {
|
|
|
6031
6318
|
return;
|
|
6032
6319
|
}
|
|
6033
6320
|
try {
|
|
6034
|
-
const result =
|
|
6321
|
+
const result = await registry.delete(name);
|
|
6035
6322
|
logger.info(`Light rule deleted: ${result.name}`);
|
|
6036
6323
|
respond(true, {
|
|
6037
6324
|
ok: true,
|
|
@@ -6051,6 +6338,126 @@ function registerLightRulesGateway(api, ctx, logger, rememberBroadcast) {
|
|
|
6051
6338
|
});
|
|
6052
6339
|
}
|
|
6053
6340
|
|
|
6341
|
+
// src/light-rules/registry.ts
|
|
6342
|
+
var LightRuleRegistry = class {
|
|
6343
|
+
ctx;
|
|
6344
|
+
/** name → meta 的内存索引;落盘成功后才更新 */
|
|
6345
|
+
index = /* @__PURE__ */ new Map();
|
|
6346
|
+
/** 写路径串行化锁:每次 mutate 都 chain 在前一次之后 */
|
|
6347
|
+
writeChain = Promise.resolve();
|
|
6348
|
+
constructor(ctx) {
|
|
6349
|
+
this.ctx = ctx;
|
|
6350
|
+
this.reload();
|
|
6351
|
+
}
|
|
6352
|
+
/**
|
|
6353
|
+
* 从磁盘重新加载全部规则到内存。仅在构造时和外部显式触发时使用。
|
|
6354
|
+
* 正常 CRUD 路径不应该调用本方法 —— 通过 create/update/delete 增量维护即可。
|
|
6355
|
+
*/
|
|
6356
|
+
reload() {
|
|
6357
|
+
this.index.clear();
|
|
6358
|
+
for (const meta of listLightRules(this.ctx)) {
|
|
6359
|
+
this.index.set(meta.name, meta);
|
|
6360
|
+
}
|
|
6361
|
+
}
|
|
6362
|
+
/** 全部规则(包含 disabled),调用方不要改返回值。 */
|
|
6363
|
+
list() {
|
|
6364
|
+
return Array.from(this.index.values());
|
|
6365
|
+
}
|
|
6366
|
+
/** 仅 enabled 的规则。事件驱动评估链路使用。 */
|
|
6367
|
+
getEnabled() {
|
|
6368
|
+
return this.list().filter((rule) => rule.enabled);
|
|
6369
|
+
}
|
|
6370
|
+
/** 按名字精确查找;不存在返回 null。 */
|
|
6371
|
+
get(name) {
|
|
6372
|
+
return this.index.get(name) ?? null;
|
|
6373
|
+
}
|
|
6374
|
+
/**
|
|
6375
|
+
* 创建规则。落盘成功后再写入内存索引。
|
|
6376
|
+
* 失败时 storage 抛 LightRuleError,内存索引保持不变。
|
|
6377
|
+
*/
|
|
6378
|
+
async create(params) {
|
|
6379
|
+
return this.runExclusive(() => {
|
|
6380
|
+
const result = createLightRule(this.ctx, params);
|
|
6381
|
+
this.index.set(result.meta.name, result.meta);
|
|
6382
|
+
return result;
|
|
6383
|
+
});
|
|
6384
|
+
}
|
|
6385
|
+
/**
|
|
6386
|
+
* 更新规则。落盘成功后再刷新内存条目。
|
|
6387
|
+
*/
|
|
6388
|
+
async update(params) {
|
|
6389
|
+
return this.runExclusive(() => {
|
|
6390
|
+
const result = updateLightRule(this.ctx, params);
|
|
6391
|
+
this.index.set(result.meta.name, result.meta);
|
|
6392
|
+
return result;
|
|
6393
|
+
});
|
|
6394
|
+
}
|
|
6395
|
+
/**
|
|
6396
|
+
* 删除规则。落盘成功后再从内存索引移除。
|
|
6397
|
+
*/
|
|
6398
|
+
async delete(name) {
|
|
6399
|
+
return this.runExclusive(() => {
|
|
6400
|
+
const result = deleteLightRule(this.ctx, name);
|
|
6401
|
+
this.index.delete(result.name);
|
|
6402
|
+
return result;
|
|
6403
|
+
});
|
|
6404
|
+
}
|
|
6405
|
+
/**
|
|
6406
|
+
* 拼装 Agent 评估用的 system prompt。
|
|
6407
|
+
*
|
|
6408
|
+
* 当前实现是一个最小可用骨架:列出全部 enabled 规则的 name + description。
|
|
6409
|
+
* M3 接入真实 Agent 链路时,会按 prd-lightrules-event-driven-refactor.md §Prompt 拼装
|
|
6410
|
+
* 的模板补齐"任务说明 / 输出约束"等段落。
|
|
6411
|
+
*
|
|
6412
|
+
* 现在先把方法签名冻结,避免后续散落在多个调用点。
|
|
6413
|
+
*/
|
|
6414
|
+
buildSystemPrompt() {
|
|
6415
|
+
const enabled = this.getEnabled();
|
|
6416
|
+
if (enabled.length === 0) {
|
|
6417
|
+
return "\u5F53\u524D\u6CA1\u6709\u542F\u7528\u4EFB\u4F55\u706F\u6548\u89C4\u5219\u3002";
|
|
6418
|
+
}
|
|
6419
|
+
const lines = [
|
|
6420
|
+
'\u4F60\u662F\u4E00\u4E2A\u5224\u65AD"\u901A\u77E5 \u2192 \u706F\u6548\u89C4\u5219"\u662F\u5426\u547D\u4E2D\u7684\u52A9\u624B\u3002',
|
|
6421
|
+
"",
|
|
6422
|
+
"\u4E0B\u9762\u662F\u7528\u6237\u5F53\u524D\u542F\u7528\u7684\u5168\u90E8\u706F\u6548\u89C4\u5219\uFF08\u6309\u521B\u5EFA\u65F6\u95F4\u5012\u5E8F\uFF09\uFF1A",
|
|
6423
|
+
""
|
|
6424
|
+
];
|
|
6425
|
+
const sorted = [...enabled].sort(
|
|
6426
|
+
(a, b) => (b.createdAt ?? "").localeCompare(a.createdAt ?? "")
|
|
6427
|
+
);
|
|
6428
|
+
sorted.forEach((rule, idx) => {
|
|
6429
|
+
lines.push(
|
|
6430
|
+
`[${idx + 1}] name: ${rule.name}`,
|
|
6431
|
+
` description: ${rule.description}`,
|
|
6432
|
+
""
|
|
6433
|
+
);
|
|
6434
|
+
});
|
|
6435
|
+
lines.push(
|
|
6436
|
+
'\u4EFB\u52A1\uFF1A\u6839\u636E\u7528\u6237\u63A5\u4E0B\u6765\u53D1\u7ED9\u4F60\u7684"\u901A\u77E5"\u5185\u5BB9\uFF0C\u5224\u65AD\u54EA\u4E9B\u89C4\u5219\u88AB\u547D\u4E2D\u3002',
|
|
6437
|
+
"- \u547D\u4E2D 0 \u6761\uFF1A\u4E0D\u8C03\u7528\u4EFB\u4F55\u5DE5\u5177\uFF0C\u76F4\u63A5\u7ED3\u675F\u3002",
|
|
6438
|
+
"- \u547D\u4E2D 1 \u6761\u6216\u591A\u6761\uFF1A\u5BF9\u6BCF\u4E00\u6761\u547D\u4E2D\u7684\u89C4\u5219\u8C03\u7528\u4E00\u6B21 trigger_light(rule_name)\u3002",
|
|
6439
|
+
"- \u4E0D\u8981\u56DE\u590D\u4EFB\u4F55\u81EA\u7531\u6587\u672C\uFF1B\u5224\u5B9A\u7ED3\u679C**\u53EA**\u901A\u8FC7 tool calling \u8868\u8FBE\u3002",
|
|
6440
|
+
'- \u62FF\u4E0D\u51C6\u7684\u65F6\u5019\u503E\u5411\u4E8E"\u4E0D\u89E6\u53D1"\uFF0C\u907F\u514D\u9A9A\u6270\u7528\u6237\u3002'
|
|
6441
|
+
);
|
|
6442
|
+
return lines.join("\n");
|
|
6443
|
+
}
|
|
6444
|
+
/**
|
|
6445
|
+
* 把一段同步操作排队进 write chain,保证 mutate 串行执行。
|
|
6446
|
+
*
|
|
6447
|
+
* 用 promise 链做 mutex 是最简单的方案:每次 runExclusive 都把
|
|
6448
|
+
* `writeChain` 推进一步。即使前一个任务失败,链也会继续往下走,
|
|
6449
|
+
* 后续任务不会被永久阻塞。
|
|
6450
|
+
*/
|
|
6451
|
+
runExclusive(fn) {
|
|
6452
|
+
const next = this.writeChain.then(
|
|
6453
|
+
() => fn(),
|
|
6454
|
+
() => fn()
|
|
6455
|
+
);
|
|
6456
|
+
this.writeChain = next.catch(() => void 0);
|
|
6457
|
+
return next;
|
|
6458
|
+
}
|
|
6459
|
+
};
|
|
6460
|
+
|
|
6054
6461
|
// src/plugin/auto-update.ts
|
|
6055
6462
|
init_env();
|
|
6056
6463
|
|
|
@@ -7761,7 +8168,10 @@ function registerRecStatus(rec, ctx) {
|
|
|
7761
8168
|
markers: entry.metadata.markers ?? [],
|
|
7762
8169
|
audioFile: entry.audioFile ?? null,
|
|
7763
8170
|
srtFile: entry.srtFile ?? null,
|
|
8171
|
+
transcriptDataFile: entry.transcriptDataFile ?? null,
|
|
7764
8172
|
transcriptFile: entry.transcriptFile ?? null,
|
|
8173
|
+
summaryFile: entry.summaryFile ?? null,
|
|
8174
|
+
title: entry.title ?? null,
|
|
7765
8175
|
ingestedAt: entry.ingestedAt,
|
|
7766
8176
|
updatedAt: entry.updatedAt
|
|
7767
8177
|
}
|
|
@@ -8374,11 +8784,26 @@ var NotificationStorage = class {
|
|
|
8374
8784
|
ingested: 0,
|
|
8375
8785
|
dedupedById: 0,
|
|
8376
8786
|
dedupedByContent: 0,
|
|
8377
|
-
invalid: 0
|
|
8787
|
+
invalid: 0,
|
|
8788
|
+
inserted: []
|
|
8378
8789
|
};
|
|
8379
8790
|
for (const n of items) {
|
|
8380
8791
|
const outcome = await this.writeNotification(n);
|
|
8381
|
-
|
|
8792
|
+
switch (outcome.kind) {
|
|
8793
|
+
case "ingested":
|
|
8794
|
+
result.ingested += 1;
|
|
8795
|
+
result.inserted.push(outcome.entry);
|
|
8796
|
+
break;
|
|
8797
|
+
case "dedupedById":
|
|
8798
|
+
result.dedupedById += 1;
|
|
8799
|
+
break;
|
|
8800
|
+
case "dedupedByContent":
|
|
8801
|
+
result.dedupedByContent += 1;
|
|
8802
|
+
break;
|
|
8803
|
+
case "invalid":
|
|
8804
|
+
result.invalid += 1;
|
|
8805
|
+
break;
|
|
8806
|
+
}
|
|
8382
8807
|
}
|
|
8383
8808
|
this.prune();
|
|
8384
8809
|
return result;
|
|
@@ -8387,7 +8812,7 @@ var NotificationStorage = class {
|
|
|
8387
8812
|
const ts = new Date(n.timestamp);
|
|
8388
8813
|
if (Number.isNaN(ts.getTime())) {
|
|
8389
8814
|
this.logger.warn(`\u5FFD\u7565\u975E\u6CD5 timestamp \u7684\u901A\u77E5: ${n.id}`);
|
|
8390
|
-
return "invalid";
|
|
8815
|
+
return { kind: "invalid" };
|
|
8391
8816
|
}
|
|
8392
8817
|
const dateKey = this.formatDate(ts);
|
|
8393
8818
|
const filePath = join12(this.dir, `${dateKey}.json`);
|
|
@@ -8395,10 +8820,10 @@ var NotificationStorage = class {
|
|
|
8395
8820
|
const entry = this.buildStoredNotification(n);
|
|
8396
8821
|
return this.withDateWriteLock(dateKey, async () => {
|
|
8397
8822
|
if (normalizedId && this.hasNotificationId(dateKey, normalizedId)) {
|
|
8398
|
-
return "dedupedById";
|
|
8823
|
+
return { kind: "dedupedById" };
|
|
8399
8824
|
}
|
|
8400
8825
|
if (this.hasNotificationContentKey(dateKey, filePath, entry)) {
|
|
8401
|
-
return "dedupedByContent";
|
|
8826
|
+
return { kind: "dedupedByContent" };
|
|
8402
8827
|
}
|
|
8403
8828
|
const appDisplayName = this.resolveDisplayName ? await this.resolveDisplayName(entry.appName) : entry.appName;
|
|
8404
8829
|
const storedEntry = {
|
|
@@ -8412,7 +8837,7 @@ var NotificationStorage = class {
|
|
|
8412
8837
|
this.recordNotificationId(dateKey, normalizedId);
|
|
8413
8838
|
}
|
|
8414
8839
|
this.recordNotificationContentKey(dateKey, filePath, storedEntry);
|
|
8415
|
-
return "ingested";
|
|
8840
|
+
return { kind: "ingested", entry: storedEntry };
|
|
8416
8841
|
});
|
|
8417
8842
|
}
|
|
8418
8843
|
buildStoredNotification(n) {
|
|
@@ -8616,7 +9041,7 @@ import {
|
|
|
8616
9041
|
writeFileSync as writeFileSync11,
|
|
8617
9042
|
rmSync as rmSync5
|
|
8618
9043
|
} from "fs";
|
|
8619
|
-
import { join as join13 } from "path";
|
|
9044
|
+
import { join as join13, basename as basename3 } from "path";
|
|
8620
9045
|
|
|
8621
9046
|
// src/recording/state-machine.ts
|
|
8622
9047
|
var VALID_TRANSITIONS = /* @__PURE__ */ new Map([
|
|
@@ -8654,6 +9079,7 @@ var TransitionError = class extends Error {
|
|
|
8654
9079
|
};
|
|
8655
9080
|
|
|
8656
9081
|
// src/recording/storage.ts
|
|
9082
|
+
init_transcript_document();
|
|
8657
9083
|
function extractAudioExt(ossUrl) {
|
|
8658
9084
|
try {
|
|
8659
9085
|
const pathname = new URL(ossUrl).pathname;
|
|
@@ -8667,8 +9093,42 @@ function extractAudioExt(ossUrl) {
|
|
|
8667
9093
|
}
|
|
8668
9094
|
var RECORDINGS_DIR = "recordings";
|
|
8669
9095
|
var AUDIO_DIR = "audio";
|
|
9096
|
+
var TRANSCRIPT_DATA_DIR = "transcript-data";
|
|
8670
9097
|
var TRANSCRIPTS_DIR = "transcripts";
|
|
9098
|
+
var SUMMARIES_DIR = "summaries";
|
|
8671
9099
|
var INDEX_FILE = "index.json";
|
|
9100
|
+
function stripMarkdownFence(markdown) {
|
|
9101
|
+
return markdown.replace(/\r\n/g, "\n");
|
|
9102
|
+
}
|
|
9103
|
+
function deriveTitleFromTranscriptPath(transcriptFile, recordingId) {
|
|
9104
|
+
if (!transcriptFile) return void 0;
|
|
9105
|
+
const name = basename3(transcriptFile, ".md");
|
|
9106
|
+
const prefix = `${recordingId}_`;
|
|
9107
|
+
if (name.startsWith(prefix)) {
|
|
9108
|
+
const derived = name.slice(prefix.length).trim();
|
|
9109
|
+
return derived || void 0;
|
|
9110
|
+
}
|
|
9111
|
+
return void 0;
|
|
9112
|
+
}
|
|
9113
|
+
function extractTranscriptContent(markdown) {
|
|
9114
|
+
const normalized = stripMarkdownFence(markdown);
|
|
9115
|
+
const firstDivider = normalized.indexOf("\n---\n");
|
|
9116
|
+
let body = firstDivider >= 0 ? normalized.slice(firstDivider + "\n---\n".length) : normalized;
|
|
9117
|
+
if (body.startsWith("\n")) {
|
|
9118
|
+
body = body.slice(1);
|
|
9119
|
+
}
|
|
9120
|
+
if (body.startsWith("### \u5173\u952E\u70B9")) {
|
|
9121
|
+
const secondDivider = body.indexOf("\n---\n");
|
|
9122
|
+
if (secondDivider >= 0) {
|
|
9123
|
+
body = body.slice(secondDivider + "\n---\n".length);
|
|
9124
|
+
if (body.startsWith("\n")) {
|
|
9125
|
+
body = body.slice(1);
|
|
9126
|
+
}
|
|
9127
|
+
}
|
|
9128
|
+
}
|
|
9129
|
+
const lines = body.split("\n").filter((line) => !/^\*\*\[关键点 .+\]\*\*$/.test(line.trim())).filter((line) => !/^- \*\*\[关键点 .+\]\*\*$/.test(line.trim()));
|
|
9130
|
+
return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
9131
|
+
}
|
|
8672
9132
|
function resolveRecordingStorageDir(ctx, logger) {
|
|
8673
9133
|
const stateRecDir = join13(
|
|
8674
9134
|
ctx.stateDir,
|
|
@@ -8698,18 +9158,24 @@ var RecordingStorage = class {
|
|
|
8698
9158
|
this.logger = logger;
|
|
8699
9159
|
this.dir = dir;
|
|
8700
9160
|
this.audioDir = join13(dir, AUDIO_DIR);
|
|
9161
|
+
this.transcriptDataDir = join13(dir, TRANSCRIPT_DATA_DIR);
|
|
8701
9162
|
this.transcriptsDir = join13(dir, TRANSCRIPTS_DIR);
|
|
9163
|
+
this.summariesDir = join13(dir, SUMMARIES_DIR);
|
|
8702
9164
|
this.indexPath = join13(dir, INDEX_FILE);
|
|
8703
9165
|
}
|
|
8704
9166
|
dir;
|
|
8705
9167
|
audioDir;
|
|
9168
|
+
transcriptDataDir;
|
|
8706
9169
|
transcriptsDir;
|
|
9170
|
+
summariesDir;
|
|
8707
9171
|
indexPath;
|
|
8708
9172
|
index = { recordings: [] };
|
|
8709
9173
|
/** 初始化目录结构并加载索引 */
|
|
8710
9174
|
async init() {
|
|
8711
9175
|
mkdirSync10(this.audioDir, { recursive: true });
|
|
9176
|
+
mkdirSync10(this.transcriptDataDir, { recursive: true });
|
|
8712
9177
|
mkdirSync10(this.transcriptsDir, { recursive: true });
|
|
9178
|
+
mkdirSync10(this.summariesDir, { recursive: true });
|
|
8713
9179
|
this.loadIndex();
|
|
8714
9180
|
this.logger.info(
|
|
8715
9181
|
`\u5F55\u97F3\u5B58\u50A8\u5DF2\u521D\u59CB\u5316: ${this.dir}\uFF08\u5171 ${this.index.recordings.length} \u6761\u8BB0\u5F55\uFF09`
|
|
@@ -8719,10 +9185,18 @@ var RecordingStorage = class {
|
|
|
8719
9185
|
getAudioDir() {
|
|
8720
9186
|
return this.audioDir;
|
|
8721
9187
|
}
|
|
9188
|
+
/** 获取转写 JSON 目录路径 */
|
|
9189
|
+
getTranscriptDataDir() {
|
|
9190
|
+
return this.transcriptDataDir;
|
|
9191
|
+
}
|
|
8722
9192
|
/** 获取转写目录路径 */
|
|
8723
9193
|
getTranscriptsDir() {
|
|
8724
9194
|
return this.transcriptsDir;
|
|
8725
9195
|
}
|
|
9196
|
+
/** 获取摘要目录路径 */
|
|
9197
|
+
getSummariesDir() {
|
|
9198
|
+
return this.summariesDir;
|
|
9199
|
+
}
|
|
8726
9200
|
// ─── 录音入库 ───
|
|
8727
9201
|
/**
|
|
8728
9202
|
* 收到 recordings.sync 后,将元数据写入索引。
|
|
@@ -8733,9 +9207,21 @@ var RecordingStorage = class {
|
|
|
8733
9207
|
const id = recordingId;
|
|
8734
9208
|
const existing = this.findById(id);
|
|
8735
9209
|
if (existing) {
|
|
9210
|
+
if (existing.transcriptDataFile) {
|
|
9211
|
+
rmSync5(join13(this.dir, existing.transcriptDataFile), { force: true });
|
|
9212
|
+
}
|
|
9213
|
+
if (existing.transcriptFile) {
|
|
9214
|
+
rmSync5(join13(this.dir, existing.transcriptFile), { force: true });
|
|
9215
|
+
}
|
|
9216
|
+
if (existing.summaryFile) {
|
|
9217
|
+
rmSync5(join13(this.dir, existing.summaryFile), { force: true });
|
|
9218
|
+
}
|
|
8736
9219
|
existing.metadata = metadata;
|
|
8737
9220
|
existing.status = "syncing_openclaw";
|
|
9221
|
+
existing.transcriptDataFile = void 0;
|
|
8738
9222
|
existing.transcriptFile = void 0;
|
|
9223
|
+
existing.summaryFile = void 0;
|
|
9224
|
+
existing.title = void 0;
|
|
8739
9225
|
existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8740
9226
|
this.logger.info(`\u5F55\u97F3\u5143\u6570\u636E\u5DF2\u66F4\u65B0: ${id}`);
|
|
8741
9227
|
} else {
|
|
@@ -8788,6 +9274,20 @@ var RecordingStorage = class {
|
|
|
8788
9274
|
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8789
9275
|
this.saveIndex();
|
|
8790
9276
|
}
|
|
9277
|
+
/**
|
|
9278
|
+
* 记录转写 JSON 文件路径
|
|
9279
|
+
*/
|
|
9280
|
+
setTranscriptDataFile(recordingId, filename) {
|
|
9281
|
+
const entry = this.findById(recordingId);
|
|
9282
|
+
if (!entry) return;
|
|
9283
|
+
const nextTranscriptDataFile = `${TRANSCRIPT_DATA_DIR}/${filename}`;
|
|
9284
|
+
if (entry.transcriptDataFile && entry.transcriptDataFile !== nextTranscriptDataFile) {
|
|
9285
|
+
rmSync5(join13(this.dir, entry.transcriptDataFile), { force: true });
|
|
9286
|
+
}
|
|
9287
|
+
entry.transcriptDataFile = nextTranscriptDataFile;
|
|
9288
|
+
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9289
|
+
this.saveIndex();
|
|
9290
|
+
}
|
|
8791
9291
|
/**
|
|
8792
9292
|
* 记录转写文件路径
|
|
8793
9293
|
*/
|
|
@@ -8802,6 +9302,64 @@ var RecordingStorage = class {
|
|
|
8802
9302
|
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8803
9303
|
this.saveIndex();
|
|
8804
9304
|
}
|
|
9305
|
+
/**
|
|
9306
|
+
* 记录摘要文件路径
|
|
9307
|
+
*/
|
|
9308
|
+
setSummaryFile(recordingId, filename) {
|
|
9309
|
+
const entry = this.findById(recordingId);
|
|
9310
|
+
if (!entry) return;
|
|
9311
|
+
const nextSummaryFile = `${SUMMARIES_DIR}/${filename}`;
|
|
9312
|
+
if (entry.summaryFile && entry.summaryFile !== nextSummaryFile) {
|
|
9313
|
+
rmSync5(join13(this.dir, entry.summaryFile), { force: true });
|
|
9314
|
+
}
|
|
9315
|
+
entry.summaryFile = nextSummaryFile;
|
|
9316
|
+
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9317
|
+
this.saveIndex();
|
|
9318
|
+
}
|
|
9319
|
+
/**
|
|
9320
|
+
* 记录转写标题
|
|
9321
|
+
*/
|
|
9322
|
+
setTitle(recordingId, title) {
|
|
9323
|
+
const entry = this.findById(recordingId);
|
|
9324
|
+
if (!entry) return;
|
|
9325
|
+
entry.title = title?.trim() || void 0;
|
|
9326
|
+
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9327
|
+
this.saveIndex();
|
|
9328
|
+
}
|
|
9329
|
+
/**
|
|
9330
|
+
* 读取摘要文本
|
|
9331
|
+
*/
|
|
9332
|
+
readSummary(recordingId) {
|
|
9333
|
+
const entry = this.findById(recordingId);
|
|
9334
|
+
if (!entry) return void 0;
|
|
9335
|
+
if (entry.summaryFile) {
|
|
9336
|
+
const summary = this.readRelativeTextFile(entry.summaryFile);
|
|
9337
|
+
if (summary?.trim()) {
|
|
9338
|
+
return summary.trim();
|
|
9339
|
+
}
|
|
9340
|
+
}
|
|
9341
|
+
const transcriptDoc = entry.transcriptDataFile ? this.readRelativeTranscriptDocument(entry.transcriptDataFile) : void 0;
|
|
9342
|
+
return extractTranscriptSummaryFromDocument(transcriptDoc);
|
|
9343
|
+
}
|
|
9344
|
+
/**
|
|
9345
|
+
* 优先从 transcript JSON 中读取正文,旧数据回退读取 Markdown。
|
|
9346
|
+
*/
|
|
9347
|
+
readTranscript(recordingId) {
|
|
9348
|
+
const entry = this.findById(recordingId);
|
|
9349
|
+
if (!entry) return void 0;
|
|
9350
|
+
if (entry.transcriptDataFile) {
|
|
9351
|
+
const transcriptDoc = this.readRelativeTranscriptDocument(entry.transcriptDataFile);
|
|
9352
|
+
const transcriptFromJson = extractTranscriptTextFromDocument(transcriptDoc);
|
|
9353
|
+
if (transcriptFromJson) {
|
|
9354
|
+
return transcriptFromJson;
|
|
9355
|
+
}
|
|
9356
|
+
}
|
|
9357
|
+
if (!entry.transcriptFile) return void 0;
|
|
9358
|
+
const markdown = this.readRelativeTextFile(entry.transcriptFile);
|
|
9359
|
+
if (!markdown) return void 0;
|
|
9360
|
+
const transcript = extractTranscriptContent(markdown);
|
|
9361
|
+
return transcript || void 0;
|
|
9362
|
+
}
|
|
8805
9363
|
// ─── 查询 ───
|
|
8806
9364
|
findById(id) {
|
|
8807
9365
|
return this.index.recordings.find((r) => r.id === id);
|
|
@@ -8847,14 +9405,24 @@ var RecordingStorage = class {
|
|
|
8847
9405
|
const srtPath = join13(this.dir, entry.srtFile);
|
|
8848
9406
|
rmSync5(srtPath, { force: true });
|
|
8849
9407
|
}
|
|
9408
|
+
if (entry.transcriptDataFile) {
|
|
9409
|
+
const transcriptDataPath = join13(this.dir, entry.transcriptDataFile);
|
|
9410
|
+
rmSync5(transcriptDataPath, { force: true });
|
|
9411
|
+
}
|
|
8850
9412
|
if (entry.transcriptFile) {
|
|
8851
9413
|
const transcriptPath = join13(this.dir, entry.transcriptFile);
|
|
8852
9414
|
rmSync5(transcriptPath, { force: true });
|
|
8853
9415
|
}
|
|
9416
|
+
if (entry.summaryFile) {
|
|
9417
|
+
const summaryPath = join13(this.dir, entry.summaryFile);
|
|
9418
|
+
rmSync5(summaryPath, { force: true });
|
|
9419
|
+
}
|
|
8854
9420
|
if (opts?.localOnly) {
|
|
8855
9421
|
entry.audioFile = void 0;
|
|
8856
9422
|
entry.srtFile = void 0;
|
|
9423
|
+
entry.transcriptDataFile = void 0;
|
|
8857
9424
|
entry.transcriptFile = void 0;
|
|
9425
|
+
entry.summaryFile = void 0;
|
|
8858
9426
|
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8859
9427
|
this.saveIndex();
|
|
8860
9428
|
this.logger.info(`\u5F55\u97F3\u672C\u5730\u6587\u4EF6\u5DF2\u5220\u9664\uFF08\u4FDD\u7559\u7D22\u5F15\uFF09: ${recordingId}`);
|
|
@@ -8881,6 +9449,12 @@ var RecordingStorage = class {
|
|
|
8881
9449
|
buildSrtFilename(recordingId) {
|
|
8882
9450
|
return `${recordingId}.srt`;
|
|
8883
9451
|
}
|
|
9452
|
+
/**
|
|
9453
|
+
* 生成转写 JSON 文件名
|
|
9454
|
+
*/
|
|
9455
|
+
buildTranscriptDataFilename(recordingId) {
|
|
9456
|
+
return buildTranscriptDataFilename(recordingId);
|
|
9457
|
+
}
|
|
8884
9458
|
/**
|
|
8885
9459
|
* 生成转写文件名
|
|
8886
9460
|
* @param summary 摘要标题(≤ 10 字)
|
|
@@ -8889,6 +9463,12 @@ var RecordingStorage = class {
|
|
|
8889
9463
|
const safeSummary = summary.replace(/[/\\:*?"<>|]/g, "").trim().slice(0, 20);
|
|
8890
9464
|
return safeSummary ? `${recordingId}_${safeSummary}.md` : `${recordingId}.md`;
|
|
8891
9465
|
}
|
|
9466
|
+
/**
|
|
9467
|
+
* 生成摘要文件名
|
|
9468
|
+
*/
|
|
9469
|
+
buildSummaryFilename(recordingId) {
|
|
9470
|
+
return `${recordingId}.md`;
|
|
9471
|
+
}
|
|
8892
9472
|
/**
|
|
8893
9473
|
* 获取音频文件的绝对路径。ossUrl 用于推断文件扩展名
|
|
8894
9474
|
*/
|
|
@@ -8901,6 +9481,18 @@ var RecordingStorage = class {
|
|
|
8901
9481
|
getSrtFilePath(recordingId) {
|
|
8902
9482
|
return join13(this.audioDir, this.buildSrtFilename(recordingId));
|
|
8903
9483
|
}
|
|
9484
|
+
/**
|
|
9485
|
+
* 获取转写 JSON 文件的绝对路径
|
|
9486
|
+
*/
|
|
9487
|
+
getTranscriptDataFilePath(recordingId) {
|
|
9488
|
+
return join13(this.transcriptDataDir, this.buildTranscriptDataFilename(recordingId));
|
|
9489
|
+
}
|
|
9490
|
+
/**
|
|
9491
|
+
* 获取摘要文件的绝对路径
|
|
9492
|
+
*/
|
|
9493
|
+
getSummaryFilePath(recordingId) {
|
|
9494
|
+
return join13(this.summariesDir, this.buildSummaryFilename(recordingId));
|
|
9495
|
+
}
|
|
8904
9496
|
// ─── Persistence ───
|
|
8905
9497
|
loadIndex() {
|
|
8906
9498
|
if (!existsSync17(this.indexPath)) {
|
|
@@ -8910,6 +9502,7 @@ var RecordingStorage = class {
|
|
|
8910
9502
|
try {
|
|
8911
9503
|
const raw = JSON.parse(readFileSync18(this.indexPath, "utf-8"));
|
|
8912
9504
|
if (raw && Array.isArray(raw.recordings)) {
|
|
9505
|
+
let needsRewrite = false;
|
|
8913
9506
|
const normalized = raw.recordings.filter((entry) => entry && typeof entry === "object").map((entry) => {
|
|
8914
9507
|
const compacted = {
|
|
8915
9508
|
id: entry.id,
|
|
@@ -8923,13 +9516,81 @@ var RecordingStorage = class {
|
|
|
8923
9516
|
if (typeof entry.transcriptFile === "string") {
|
|
8924
9517
|
compacted.transcriptFile = entry.transcriptFile;
|
|
8925
9518
|
}
|
|
9519
|
+
let transcriptDoc = typeof entry.transcriptDataFile === "string" ? this.readRelativeTranscriptDocument(entry.transcriptDataFile) : void 0;
|
|
9520
|
+
if (typeof entry.transcriptDataFile === "string" && transcriptDoc) {
|
|
9521
|
+
compacted.transcriptDataFile = entry.transcriptDataFile;
|
|
9522
|
+
} else {
|
|
9523
|
+
const legacyTranscriptText = typeof entry.transcript === "string" && entry.transcript.trim() ? entry.transcript.trim() : compacted.transcriptFile ? extractTranscriptContent(
|
|
9524
|
+
this.readRelativeTextFile(compacted.transcriptFile) ?? ""
|
|
9525
|
+
) || void 0 : void 0;
|
|
9526
|
+
const legacyTitle = typeof entry.title === "string" && entry.title.trim() ? entry.title.trim() : deriveTitleFromTranscriptPath(compacted.transcriptFile, compacted.id);
|
|
9527
|
+
const legacySummary = typeof entry.summary === "string" && entry.summary.trim() ? entry.summary.trim() : void 0;
|
|
9528
|
+
if (legacyTranscriptText) {
|
|
9529
|
+
transcriptDoc = buildTranscriptDocument({
|
|
9530
|
+
recordingId: compacted.id,
|
|
9531
|
+
generatedAt: compacted.updatedAt,
|
|
9532
|
+
source: {
|
|
9533
|
+
provider: "legacy-markdown"
|
|
9534
|
+
},
|
|
9535
|
+
title: legacyTitle,
|
|
9536
|
+
summary: legacySummary,
|
|
9537
|
+
text: legacyTranscriptText,
|
|
9538
|
+
segments: []
|
|
9539
|
+
});
|
|
9540
|
+
const transcriptDataFilename = this.buildTranscriptDataFilename(compacted.id);
|
|
9541
|
+
writeFileSync11(
|
|
9542
|
+
join13(this.transcriptDataDir, transcriptDataFilename),
|
|
9543
|
+
JSON.stringify(transcriptDoc, null, 2),
|
|
9544
|
+
"utf-8"
|
|
9545
|
+
);
|
|
9546
|
+
compacted.transcriptDataFile = `${TRANSCRIPT_DATA_DIR}/${transcriptDataFilename}`;
|
|
9547
|
+
needsRewrite = true;
|
|
9548
|
+
}
|
|
9549
|
+
}
|
|
9550
|
+
if (typeof entry.summaryFile === "string") {
|
|
9551
|
+
compacted.summaryFile = entry.summaryFile;
|
|
9552
|
+
} else if (typeof entry.summary === "string" && entry.summary.trim()) {
|
|
9553
|
+
const summaryFilename = this.buildSummaryFilename(entry.id);
|
|
9554
|
+
writeFileSync11(
|
|
9555
|
+
join13(this.summariesDir, summaryFilename),
|
|
9556
|
+
entry.summary.trim(),
|
|
9557
|
+
"utf-8"
|
|
9558
|
+
);
|
|
9559
|
+
compacted.summaryFile = `${SUMMARIES_DIR}/${summaryFilename}`;
|
|
9560
|
+
needsRewrite = true;
|
|
9561
|
+
} else {
|
|
9562
|
+
const summaryFromDocument = extractTranscriptSummaryFromDocument(transcriptDoc);
|
|
9563
|
+
if (summaryFromDocument) {
|
|
9564
|
+
const summaryFilename = this.buildSummaryFilename(entry.id);
|
|
9565
|
+
writeFileSync11(
|
|
9566
|
+
join13(this.summariesDir, summaryFilename),
|
|
9567
|
+
summaryFromDocument,
|
|
9568
|
+
"utf-8"
|
|
9569
|
+
);
|
|
9570
|
+
compacted.summaryFile = `${SUMMARIES_DIR}/${summaryFilename}`;
|
|
9571
|
+
needsRewrite = true;
|
|
9572
|
+
}
|
|
9573
|
+
}
|
|
9574
|
+
if (typeof entry.title === "string" && entry.title.trim()) {
|
|
9575
|
+
compacted.title = entry.title.trim();
|
|
9576
|
+
} else {
|
|
9577
|
+
const titleFromDocument = extractTranscriptTitleFromDocument(transcriptDoc);
|
|
9578
|
+
const derivedTitle = titleFromDocument ?? deriveTitleFromTranscriptPath(
|
|
9579
|
+
compacted.transcriptFile,
|
|
9580
|
+
compacted.id
|
|
9581
|
+
);
|
|
9582
|
+
if (derivedTitle) {
|
|
9583
|
+
compacted.title = derivedTitle;
|
|
9584
|
+
needsRewrite = true;
|
|
9585
|
+
}
|
|
9586
|
+
}
|
|
8926
9587
|
return compacted;
|
|
8927
9588
|
});
|
|
8928
9589
|
const hadLargeFields = raw.recordings.some(
|
|
8929
9590
|
(entry) => entry && typeof entry === "object" && ("transcript" in entry || "summary" in entry)
|
|
8930
9591
|
);
|
|
8931
9592
|
this.index = { recordings: normalized };
|
|
8932
|
-
if (hadLargeFields) {
|
|
9593
|
+
if (hadLargeFields || needsRewrite) {
|
|
8933
9594
|
this.saveIndex();
|
|
8934
9595
|
}
|
|
8935
9596
|
} else {
|
|
@@ -8940,6 +9601,20 @@ var RecordingStorage = class {
|
|
|
8940
9601
|
this.index = { recordings: [] };
|
|
8941
9602
|
}
|
|
8942
9603
|
}
|
|
9604
|
+
readRelativeTextFile(relativePath) {
|
|
9605
|
+
try {
|
|
9606
|
+
return readFileSync18(join13(this.dir, relativePath), "utf-8");
|
|
9607
|
+
} catch {
|
|
9608
|
+
return void 0;
|
|
9609
|
+
}
|
|
9610
|
+
}
|
|
9611
|
+
readRelativeTranscriptDocument(relativePath) {
|
|
9612
|
+
const raw = this.readRelativeTextFile(relativePath);
|
|
9613
|
+
if (!raw) {
|
|
9614
|
+
return void 0;
|
|
9615
|
+
}
|
|
9616
|
+
return parseTranscriptDocument(raw);
|
|
9617
|
+
}
|
|
8943
9618
|
saveIndex() {
|
|
8944
9619
|
writeFileSync11(
|
|
8945
9620
|
this.indexPath,
|
|
@@ -8951,6 +9626,9 @@ var RecordingStorage = class {
|
|
|
8951
9626
|
}
|
|
8952
9627
|
};
|
|
8953
9628
|
|
|
9629
|
+
// src/recording/index.ts
|
|
9630
|
+
init_transcript_document();
|
|
9631
|
+
|
|
8954
9632
|
// src/recording/downloader.ts
|
|
8955
9633
|
import { createWriteStream, existsSync as existsSync18, mkdirSync as mkdirSync11, statSync as statSync4, unlinkSync } from "fs";
|
|
8956
9634
|
import { dirname as dirname6 } from "path";
|
|
@@ -9052,6 +9730,7 @@ function emitRecordingStatus(recordingId, storage, logger, notifyStatus, error,
|
|
|
9052
9730
|
transfer_status: entry.status,
|
|
9053
9731
|
audioFile: entry.audioFile,
|
|
9054
9732
|
srtFile: entry.srtFile,
|
|
9733
|
+
transcriptDataFile: entry.transcriptDataFile,
|
|
9055
9734
|
transcriptFile: entry.transcriptFile,
|
|
9056
9735
|
transcript: extras?.transcript,
|
|
9057
9736
|
summary: extras?.summary,
|
|
@@ -9150,12 +9829,21 @@ async function triggerTranscription(recordingId, storage, asrConfig, logger, opt
|
|
|
9150
9829
|
recordingName: entry.metadata.name,
|
|
9151
9830
|
durationSec: entry.metadata.duration_sec,
|
|
9152
9831
|
createdAt: entry.metadata.created_at,
|
|
9832
|
+
transcriptDataDir: storage.getTranscriptDataDir(),
|
|
9153
9833
|
transcriptsDir: storage.getTranscriptsDir(),
|
|
9834
|
+
summariesDir: storage.getSummariesDir(),
|
|
9154
9835
|
recordingId,
|
|
9155
9836
|
logger
|
|
9156
9837
|
});
|
|
9157
9838
|
if (result.ok && result.transcriptFilename) {
|
|
9839
|
+
if (result.transcriptDataFilename) {
|
|
9840
|
+
storage.setTranscriptDataFile(recordingId, result.transcriptDataFilename);
|
|
9841
|
+
}
|
|
9158
9842
|
storage.setTranscriptFile(recordingId, result.transcriptFilename);
|
|
9843
|
+
if (result.summaryFilename) {
|
|
9844
|
+
storage.setSummaryFile(recordingId, result.summaryFilename);
|
|
9845
|
+
}
|
|
9846
|
+
storage.setTitle(recordingId, result.title);
|
|
9159
9847
|
storage.updateStatus(recordingId, "transcribed");
|
|
9160
9848
|
emitRecordingStatus(
|
|
9161
9849
|
recordingId,
|
|
@@ -10737,7 +11425,8 @@ function registerStorageLifecycle(deps) {
|
|
|
10737
11425
|
logger,
|
|
10738
11426
|
lightRuleCtx,
|
|
10739
11427
|
setStorage,
|
|
10740
|
-
setRecordingStorage
|
|
11428
|
+
setRecordingStorage,
|
|
11429
|
+
onStorageReady
|
|
10741
11430
|
} = deps;
|
|
10742
11431
|
let storage = null;
|
|
10743
11432
|
let recordingStorage = null;
|
|
@@ -10765,6 +11454,7 @@ function registerStorageLifecycle(deps) {
|
|
|
10765
11454
|
if (ctx.stateDir) {
|
|
10766
11455
|
lightRuleCtx.stateDir = ctx.stateDir;
|
|
10767
11456
|
}
|
|
11457
|
+
onStorageReady?.();
|
|
10768
11458
|
},
|
|
10769
11459
|
async stop() {
|
|
10770
11460
|
await storage?.close();
|
|
@@ -10847,9 +11537,14 @@ function createEmptyIngestResult() {
|
|
|
10847
11537
|
ingested: 0,
|
|
10848
11538
|
dedupedById: 0,
|
|
10849
11539
|
dedupedByContent: 0,
|
|
10850
|
-
invalid: 0
|
|
11540
|
+
invalid: 0,
|
|
11541
|
+
inserted: []
|
|
10851
11542
|
};
|
|
10852
11543
|
}
|
|
11544
|
+
function toIngestResponse(result) {
|
|
11545
|
+
const { inserted: _inserted, ...rest } = result;
|
|
11546
|
+
return rest;
|
|
11547
|
+
}
|
|
10853
11548
|
function registerNotificationInterfaces(deps) {
|
|
10854
11549
|
const {
|
|
10855
11550
|
api,
|
|
@@ -10879,7 +11574,7 @@ function registerNotificationInterfaces(deps) {
|
|
|
10879
11574
|
}
|
|
10880
11575
|
const filtered = filterNotifications(items);
|
|
10881
11576
|
const result = filtered.length ? await storage.ingest(filtered) : createEmptyIngestResult();
|
|
10882
|
-
respond(true, result);
|
|
11577
|
+
respond(true, toIngestResponse(result));
|
|
10883
11578
|
}
|
|
10884
11579
|
);
|
|
10885
11580
|
api.registerHttpRoute({
|
|
@@ -10919,7 +11614,7 @@ function registerNotificationInterfaces(deps) {
|
|
|
10919
11614
|
const filtered = filterNotifications(body.notifications);
|
|
10920
11615
|
const result = filtered.length ? await storage.ingest(filtered) : createEmptyIngestResult();
|
|
10921
11616
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
10922
|
-
res.end(JSON.stringify({ ok: true, ...result }));
|
|
11617
|
+
res.end(JSON.stringify({ ok: true, ...toIngestResponse(result) }));
|
|
10923
11618
|
}
|
|
10924
11619
|
});
|
|
10925
11620
|
logger.info("Gateway \u901A\u77E5\u65B9\u6CD5\u5DF2\u6CE8\u518C: notifications.push");
|
|
@@ -10955,11 +11650,12 @@ function buildRecordingListItem(entry) {
|
|
|
10955
11650
|
has_srt: !!entry.srtFile,
|
|
10956
11651
|
has_transcript: !!entry.transcriptFile,
|
|
10957
11652
|
audioFile: entry.audioFile,
|
|
11653
|
+
transcriptDataFile: entry.transcriptDataFile,
|
|
10958
11654
|
transcriptFile: entry.transcriptFile,
|
|
10959
11655
|
updatedAt: entry.updatedAt
|
|
10960
11656
|
};
|
|
10961
11657
|
}
|
|
10962
|
-
function buildRecordingDetail(entry) {
|
|
11658
|
+
function buildRecordingDetail(entry, extras) {
|
|
10963
11659
|
return {
|
|
10964
11660
|
recordingId: entry.id,
|
|
10965
11661
|
name: entry.metadata.name,
|
|
@@ -10973,7 +11669,12 @@ function buildRecordingDetail(entry) {
|
|
|
10973
11669
|
transfer_status: entry.status,
|
|
10974
11670
|
audioFile: entry.audioFile,
|
|
10975
11671
|
srtFile: entry.srtFile,
|
|
11672
|
+
transcriptDataFile: entry.transcriptDataFile,
|
|
10976
11673
|
transcriptFile: entry.transcriptFile,
|
|
11674
|
+
summaryFile: entry.summaryFile,
|
|
11675
|
+
title: entry.title,
|
|
11676
|
+
summary: extras?.summary,
|
|
11677
|
+
transcript: extras?.transcript,
|
|
10977
11678
|
ingestedAt: entry.ingestedAt,
|
|
10978
11679
|
updatedAt: entry.updatedAt
|
|
10979
11680
|
};
|
|
@@ -11081,7 +11782,10 @@ function registerRecordingInterfaces(deps) {
|
|
|
11081
11782
|
});
|
|
11082
11783
|
return;
|
|
11083
11784
|
}
|
|
11084
|
-
const {
|
|
11785
|
+
const {
|
|
11786
|
+
recordingId,
|
|
11787
|
+
includeTranscriptContent
|
|
11788
|
+
} = params;
|
|
11085
11789
|
if (!recordingId) {
|
|
11086
11790
|
respond(false, null, {
|
|
11087
11791
|
code: "INVALID_PARAMS",
|
|
@@ -11097,7 +11801,13 @@ function registerRecordingInterfaces(deps) {
|
|
|
11097
11801
|
});
|
|
11098
11802
|
return;
|
|
11099
11803
|
}
|
|
11100
|
-
respond(
|
|
11804
|
+
respond(
|
|
11805
|
+
true,
|
|
11806
|
+
buildRecordingDetail(entry, {
|
|
11807
|
+
summary: recordingStorage.readSummary(recordingId),
|
|
11808
|
+
transcript: includeTranscriptContent ? recordingStorage.readTranscript(recordingId) : void 0
|
|
11809
|
+
})
|
|
11810
|
+
);
|
|
11101
11811
|
}
|
|
11102
11812
|
);
|
|
11103
11813
|
registerGatewayMethod(
|
|
@@ -11376,6 +12086,7 @@ var index_default = {
|
|
|
11376
12086
|
}
|
|
11377
12087
|
broadcastFn("recording.status", event);
|
|
11378
12088
|
}
|
|
12089
|
+
const lightRuleRegistry = new LightRuleRegistry(lightRuleCtx);
|
|
11379
12090
|
registerStorageLifecycle({
|
|
11380
12091
|
api,
|
|
11381
12092
|
config,
|
|
@@ -11386,6 +12097,9 @@ var index_default = {
|
|
|
11386
12097
|
},
|
|
11387
12098
|
setRecordingStorage(nextRecordingStorage) {
|
|
11388
12099
|
recordingStorage = nextRecordingStorage;
|
|
12100
|
+
},
|
|
12101
|
+
onStorageReady() {
|
|
12102
|
+
lightRuleRegistry.reload();
|
|
11389
12103
|
}
|
|
11390
12104
|
});
|
|
11391
12105
|
const tunnelService = registerRelayTunnelLifecycle({
|
|
@@ -11411,7 +12125,7 @@ var index_default = {
|
|
|
11411
12125
|
registerGatewayMethod: registerGatewayMethodWithBroadcastCapture,
|
|
11412
12126
|
shouldBroadcastStatusOnHttp: () => !!broadcastFn
|
|
11413
12127
|
});
|
|
11414
|
-
registerLightRulesGateway(api,
|
|
12128
|
+
registerLightRulesGateway(api, lightRuleRegistry, logger, cacheBroadcast);
|
|
11415
12129
|
logger.info(
|
|
11416
12130
|
"Gateway \u706F\u6548\u89C4\u5219\u65B9\u6CD5\u5DF2\u6CE8\u518C: lightrules.list / lightrules.create / lightrules.update / lightrules.delete"
|
|
11417
12131
|
);
|