recappi 0.1.77 → 0.1.79
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 +840 -244
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -9,6 +9,117 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
// src/tui/askInline.ts
|
|
13
|
+
function formatAskTimecode(ms) {
|
|
14
|
+
const total = Math.max(0, Math.floor(ms / 1e3));
|
|
15
|
+
const h = Math.floor(total / 3600);
|
|
16
|
+
const m = Math.floor(total % 3600 / 60);
|
|
17
|
+
const s = total % 60;
|
|
18
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
19
|
+
return h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${m}:${pad(s)}`;
|
|
20
|
+
}
|
|
21
|
+
function askCitationInlineLabel(citation) {
|
|
22
|
+
if (typeof citation.startMs === "number") return formatAskTimecode(citation.startMs);
|
|
23
|
+
const label = citation.label?.trim();
|
|
24
|
+
if (label) {
|
|
25
|
+
const firstRangePart = label.split(/[-–—]/)[0]?.trim();
|
|
26
|
+
if (firstRangePart) return firstRangePart;
|
|
27
|
+
}
|
|
28
|
+
if (typeof citation.index === "number") return `#${citation.index + 1}`;
|
|
29
|
+
return "Source";
|
|
30
|
+
}
|
|
31
|
+
function hasCitationMarkers(content) {
|
|
32
|
+
MARKER.lastIndex = 0;
|
|
33
|
+
return MARKER.test(content);
|
|
34
|
+
}
|
|
35
|
+
function strippedAskAnswer(content) {
|
|
36
|
+
return content.replace(LOOSE_MARKER, "");
|
|
37
|
+
}
|
|
38
|
+
function renderAskInline(content, citations) {
|
|
39
|
+
const bySegmentId = /* @__PURE__ */ new Map();
|
|
40
|
+
for (const citation of citations) {
|
|
41
|
+
if (citation.segmentId && !bySegmentId.has(citation.segmentId)) {
|
|
42
|
+
bySegmentId.set(citation.segmentId, citation);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (!hasCitationMarkers(content) || bySegmentId.size === 0) {
|
|
46
|
+
const text = strippedAskAnswer(content);
|
|
47
|
+
return text ? [{ kind: "text", text }] : [];
|
|
48
|
+
}
|
|
49
|
+
const segments = [];
|
|
50
|
+
let lastVisibleChar;
|
|
51
|
+
const pushText = (text) => {
|
|
52
|
+
if (!text) return;
|
|
53
|
+
const prev = segments[segments.length - 1];
|
|
54
|
+
if (prev && prev.kind === "text") prev.text += text;
|
|
55
|
+
else segments.push({ kind: "text", text });
|
|
56
|
+
lastVisibleChar = text[text.length - 1];
|
|
57
|
+
};
|
|
58
|
+
const pushCitation = (citation) => {
|
|
59
|
+
if (lastVisibleChar !== void 0 && !/\s/.test(lastVisibleChar)) pushText(" ");
|
|
60
|
+
const label = askCitationInlineLabel(citation);
|
|
61
|
+
segments.push({ kind: "citation", label, citation });
|
|
62
|
+
lastVisibleChar = label[label.length - 1];
|
|
63
|
+
};
|
|
64
|
+
MARKER.lastIndex = 0;
|
|
65
|
+
let lastIndex = 0;
|
|
66
|
+
let match;
|
|
67
|
+
while ((match = MARKER.exec(content)) !== null) {
|
|
68
|
+
const textBefore = content.slice(lastIndex, match.index);
|
|
69
|
+
const citation = bySegmentId.get(match[1]);
|
|
70
|
+
if (citation) {
|
|
71
|
+
pushText(textBefore);
|
|
72
|
+
pushCitation(citation);
|
|
73
|
+
} else {
|
|
74
|
+
const nextChar = content[match.index + match[0].length];
|
|
75
|
+
pushText(cleanupBeforeRemovedMarker(textBefore, nextChar));
|
|
76
|
+
}
|
|
77
|
+
lastIndex = match.index + match[0].length;
|
|
78
|
+
}
|
|
79
|
+
pushText(content.slice(lastIndex));
|
|
80
|
+
return segments;
|
|
81
|
+
}
|
|
82
|
+
function cleanupBeforeRemovedMarker(text, nextChar) {
|
|
83
|
+
if (!nextChar || !TRAILING_PUNCTUATION.includes(nextChar)) return text;
|
|
84
|
+
return text.replace(/[ \t]+$/, "");
|
|
85
|
+
}
|
|
86
|
+
function truncateSnippet(snippet, max = 100) {
|
|
87
|
+
const clean = snippet.replace(/\s+/g, " ").trim();
|
|
88
|
+
return clean.length > max ? `${clean.slice(0, max - 1)}\u2026` : clean;
|
|
89
|
+
}
|
|
90
|
+
function formatAskAnswerPlain(content, citations) {
|
|
91
|
+
const segments = renderAskInline(content, citations);
|
|
92
|
+
const body = segments.map((segment) => segment.kind === "text" ? segment.text : `\u27E8${segment.label}\u27E9`).join("").trim();
|
|
93
|
+
const seen = /* @__PURE__ */ new Set();
|
|
94
|
+
const cited = [];
|
|
95
|
+
for (const segment of segments) {
|
|
96
|
+
if (segment.kind !== "citation") continue;
|
|
97
|
+
const key = segment.citation.segmentId ?? segment.label;
|
|
98
|
+
if (seen.has(key)) continue;
|
|
99
|
+
seen.add(key);
|
|
100
|
+
cited.push(segment.citation);
|
|
101
|
+
}
|
|
102
|
+
if (cited.length === 0) return body;
|
|
103
|
+
const sources = cited.map((citation) => {
|
|
104
|
+
const label = askCitationInlineLabel(citation);
|
|
105
|
+
const meta3 = [citation.speaker?.trim(), citation.snippet ? truncateSnippet(citation.snippet) : ""].filter(Boolean).join(" \xB7 ");
|
|
106
|
+
return ` \u27E8${label}\u27E9${meta3 ? ` ${meta3}` : ""}`;
|
|
107
|
+
});
|
|
108
|
+
return `${body}
|
|
109
|
+
|
|
110
|
+
Sources:
|
|
111
|
+
${sources.join("\n")}`;
|
|
112
|
+
}
|
|
113
|
+
var MARKER, LOOSE_MARKER, TRAILING_PUNCTUATION;
|
|
114
|
+
var init_askInline = __esm({
|
|
115
|
+
"src/tui/askInline.ts"() {
|
|
116
|
+
"use strict";
|
|
117
|
+
MARKER = /\[\[(seg-[^\]]+)\]\]/g;
|
|
118
|
+
LOOSE_MARKER = /\s*\[?\[seg-[^\]]+\]\]?/g;
|
|
119
|
+
TRAILING_PUNCTUATION = ".\u3002!\uFF01?\uFF1F,\uFF0C;\uFF1B:\uFF1A";
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
12
123
|
// src/recordingCore.ts
|
|
13
124
|
function recordingCaptureMappingFromSelection(selection = DEFAULT_RECORDING_SELECTION, sources = DEFAULT_RECORDING_SOURCES) {
|
|
14
125
|
const source = sources.find((candidate) => candidate.id === selection.sourceId) ?? sources[0];
|
|
@@ -1000,7 +1111,7 @@ function OverviewView({
|
|
|
1000
1111
|
/* @__PURE__ */ jsx12(Text10, { color: "yellow", children: `${queued} queued` })
|
|
1001
1112
|
] }) : null,
|
|
1002
1113
|
stats?.recordings.totalDurationMs != null ? /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: ` \xB7 ${formatClockMs(stats.recordings.totalDurationMs)} transcribed` }) : null,
|
|
1003
|
-
revalidating ? /* @__PURE__ */ jsx12(Text10, { dimColor: true, children:
|
|
1114
|
+
revalidating ? /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: ` \xB7 ${"\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F"[spinnerFrame % 10]} syncing` }) : null
|
|
1004
1115
|
] }),
|
|
1005
1116
|
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", alignItems: "flex-start", children: [
|
|
1006
1117
|
/* @__PURE__ */ jsx12(Box10, { flexGrow: 1, flexDirection: "column", children: /* @__PURE__ */ jsx12(
|
|
@@ -1254,7 +1365,7 @@ function RecordingDetailView({
|
|
|
1254
1365
|
scrollable ? " \xB7 \u2191\u2193 scroll" : "",
|
|
1255
1366
|
ready ? " \xB7 " : "",
|
|
1256
1367
|
`o open \xB7 d download \xB7 f finder`,
|
|
1257
|
-
" \xB7 T re-transcribe \xB7 s re-summarize \xB7 e export",
|
|
1368
|
+
" \xB7 T re-transcribe \xB7 s re-summarize \xB7 a ask \xB7 e export",
|
|
1258
1369
|
item.activeTranscriptId ? " \xB7 t full" : "",
|
|
1259
1370
|
links.webUrl ? " \xB7 w web" : "",
|
|
1260
1371
|
" \xB7 r refresh \xB7 esc back"
|
|
@@ -1364,13 +1475,177 @@ var init_RecordingDetailView = __esm({
|
|
|
1364
1475
|
}
|
|
1365
1476
|
});
|
|
1366
1477
|
|
|
1367
|
-
// src/tui/
|
|
1368
|
-
import {
|
|
1478
|
+
// src/tui/AskScreen.tsx
|
|
1479
|
+
import { useCallback, useRef as useRef2, useState as useState5 } from "react";
|
|
1369
1480
|
import { Box as Box13, Text as Text13, useInput as useInput5 } from "ink";
|
|
1370
1481
|
import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1482
|
+
function AskScreen({
|
|
1483
|
+
recordingId,
|
|
1484
|
+
title,
|
|
1485
|
+
askRecording,
|
|
1486
|
+
onBack,
|
|
1487
|
+
onOpenTranscript,
|
|
1488
|
+
spinnerFrame
|
|
1489
|
+
}) {
|
|
1490
|
+
const [phase, setPhase] = useState5("input");
|
|
1491
|
+
const [question, setQuestion] = useState5("");
|
|
1492
|
+
const [asked, setAsked] = useState5("");
|
|
1493
|
+
const [content, setContent] = useState5("");
|
|
1494
|
+
const [citations, setCitations] = useState5([]);
|
|
1495
|
+
const [error51, setError] = useState5(void 0);
|
|
1496
|
+
const runIdRef = useRef2(0);
|
|
1497
|
+
const submit = useCallback(
|
|
1498
|
+
(raw) => {
|
|
1499
|
+
const query = raw.trim();
|
|
1500
|
+
if (!query) return;
|
|
1501
|
+
const runId = ++runIdRef.current;
|
|
1502
|
+
setAsked(query);
|
|
1503
|
+
setPhase("asking");
|
|
1504
|
+
setContent("");
|
|
1505
|
+
setCitations([]);
|
|
1506
|
+
setError(void 0);
|
|
1507
|
+
void (async () => {
|
|
1508
|
+
try {
|
|
1509
|
+
let text = "";
|
|
1510
|
+
let cites = [];
|
|
1511
|
+
for await (const event of askRecording({ recordingId, question: query })) {
|
|
1512
|
+
if (runIdRef.current !== runId) return;
|
|
1513
|
+
if (event.type === "answer_delta") {
|
|
1514
|
+
text += event.delta;
|
|
1515
|
+
setContent(text);
|
|
1516
|
+
} else if (event.type === "citation") {
|
|
1517
|
+
cites = [...cites, event.citation];
|
|
1518
|
+
setCitations(cites);
|
|
1519
|
+
} else if (event.type === "done") {
|
|
1520
|
+
if (typeof event.content === "string" && event.content) {
|
|
1521
|
+
text = event.content;
|
|
1522
|
+
setContent(text);
|
|
1523
|
+
}
|
|
1524
|
+
if (event.citations.length) {
|
|
1525
|
+
cites = event.citations;
|
|
1526
|
+
setCitations(cites);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
if (runIdRef.current === runId) setPhase("done");
|
|
1531
|
+
} catch (err) {
|
|
1532
|
+
if (runIdRef.current === runId) {
|
|
1533
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
1534
|
+
setPhase("error");
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
})();
|
|
1538
|
+
},
|
|
1539
|
+
[askRecording, recordingId]
|
|
1540
|
+
);
|
|
1541
|
+
const reset = () => {
|
|
1542
|
+
runIdRef.current++;
|
|
1543
|
+
setPhase("input");
|
|
1544
|
+
setQuestion("");
|
|
1545
|
+
setAsked("");
|
|
1546
|
+
setContent("");
|
|
1547
|
+
setCitations([]);
|
|
1548
|
+
setError(void 0);
|
|
1549
|
+
};
|
|
1550
|
+
useInput5((input, key) => {
|
|
1551
|
+
if (key.escape) {
|
|
1552
|
+
runIdRef.current++;
|
|
1553
|
+
onBack();
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
if (phase === "input") {
|
|
1557
|
+
if (key.return) return submit(question);
|
|
1558
|
+
if (key.backspace || key.delete) return setQuestion((q) => q.slice(0, -1));
|
|
1559
|
+
if (input && !key.ctrl && !key.meta) setQuestion((q) => q + input);
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
if (phase === "done" || phase === "error") {
|
|
1563
|
+
if (input === "a") return reset();
|
|
1564
|
+
if (input === "t" && onOpenTranscript) return onOpenTranscript();
|
|
1565
|
+
}
|
|
1566
|
+
});
|
|
1567
|
+
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingX: 1, children: [
|
|
1568
|
+
/* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
|
|
1569
|
+
"\u2039 Ask",
|
|
1570
|
+
title ? ` \xB7 ${title}` : ""
|
|
1571
|
+
] }),
|
|
1572
|
+
phase === "input" ? /* @__PURE__ */ jsxs12(Box13, { marginTop: 1, flexDirection: "column", children: [
|
|
1573
|
+
/* @__PURE__ */ jsxs12(Text13, { children: [
|
|
1574
|
+
/* @__PURE__ */ jsx15(Text13, { color: "cyan", children: "? " }),
|
|
1575
|
+
/* @__PURE__ */ jsx15(Text13, { children: question }),
|
|
1576
|
+
/* @__PURE__ */ jsx15(Text13, { color: "cyan", children: "\u258F" })
|
|
1577
|
+
] }),
|
|
1578
|
+
/* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "Type a question \xB7 \u23CE ask \xB7 esc back" }) })
|
|
1579
|
+
] }) : /* @__PURE__ */ jsxs12(Box13, { marginTop: 1, flexDirection: "column", children: [
|
|
1580
|
+
/* @__PURE__ */ jsx15(Text13, { dimColor: true, wrap: "truncate-end", children: `? ${asked}` }),
|
|
1581
|
+
/* @__PURE__ */ jsx15(Box13, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx15(AnswerBody, { phase, content, citations, error: error51, spinnerFrame }) }),
|
|
1582
|
+
phase === "done" ? /* @__PURE__ */ jsx15(Sources, { citations }) : null,
|
|
1583
|
+
/* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
|
|
1584
|
+
phase === "asking" ? "esc cancel" : "a ask again",
|
|
1585
|
+
(phase === "done" || phase === "error") && onOpenTranscript ? " \xB7 t transcript" : "",
|
|
1586
|
+
phase !== "asking" ? " \xB7 esc back" : ""
|
|
1587
|
+
] }) })
|
|
1588
|
+
] })
|
|
1589
|
+
] });
|
|
1590
|
+
}
|
|
1591
|
+
function AnswerBody({
|
|
1592
|
+
phase,
|
|
1593
|
+
content,
|
|
1594
|
+
citations,
|
|
1595
|
+
error: error51,
|
|
1596
|
+
spinnerFrame
|
|
1597
|
+
}) {
|
|
1598
|
+
if (phase === "error") {
|
|
1599
|
+
return /* @__PURE__ */ jsx15(Text13, { color: "red", children: error51 ? `Ask failed: ${error51}` : "Ask failed" });
|
|
1600
|
+
}
|
|
1601
|
+
if (phase === "asking" && !content) {
|
|
1602
|
+
return /* @__PURE__ */ jsx15(Text13, { color: "cyan", children: `${SPINNER2[spinnerFrame % SPINNER2.length]} Thinking\u2026` });
|
|
1603
|
+
}
|
|
1604
|
+
if (phase === "asking") {
|
|
1605
|
+
return /* @__PURE__ */ jsx15(Text13, { children: strippedAskAnswer(content) });
|
|
1606
|
+
}
|
|
1607
|
+
const segments = renderAskInline(content, citations);
|
|
1608
|
+
return /* @__PURE__ */ jsx15(Text13, { children: segments.map(
|
|
1609
|
+
(segment, i) => segment.kind === "text" ? /* @__PURE__ */ jsx15(Text13, { children: segment.text }, i) : /* @__PURE__ */ jsx15(Text13, { color: "cyan", children: `\u27E8${segment.label}\u27E9` }, i)
|
|
1610
|
+
) });
|
|
1611
|
+
}
|
|
1612
|
+
function Sources({ citations }) {
|
|
1613
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1614
|
+
const unique = [];
|
|
1615
|
+
for (const citation of citations) {
|
|
1616
|
+
const key = citation.segmentId ?? askCitationInlineLabel(citation);
|
|
1617
|
+
if (seen.has(key)) continue;
|
|
1618
|
+
seen.add(key);
|
|
1619
|
+
unique.push(citation);
|
|
1620
|
+
}
|
|
1621
|
+
if (unique.length === 0) return null;
|
|
1622
|
+
return /* @__PURE__ */ jsxs12(Box13, { marginTop: 1, flexDirection: "column", children: [
|
|
1623
|
+
/* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "Sources" }),
|
|
1624
|
+
unique.map((citation, i) => {
|
|
1625
|
+
const meta3 = [citation.speaker?.trim(), citation.snippet?.trim()].filter(Boolean).join(" \xB7 ");
|
|
1626
|
+
return /* @__PURE__ */ jsxs12(Text13, { wrap: "truncate-end", children: [
|
|
1627
|
+
/* @__PURE__ */ jsx15(Text13, { color: "cyan", children: `\u27E8${askCitationInlineLabel(citation)}\u27E9` }),
|
|
1628
|
+
meta3 ? /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: ` ${meta3}` }) : null
|
|
1629
|
+
] }, i);
|
|
1630
|
+
})
|
|
1631
|
+
] });
|
|
1632
|
+
}
|
|
1633
|
+
var SPINNER2;
|
|
1634
|
+
var init_AskScreen = __esm({
|
|
1635
|
+
"src/tui/AskScreen.tsx"() {
|
|
1636
|
+
"use strict";
|
|
1637
|
+
init_askInline();
|
|
1638
|
+
SPINNER2 = "\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F";
|
|
1639
|
+
}
|
|
1640
|
+
});
|
|
1641
|
+
|
|
1642
|
+
// src/tui/TranscriptView.tsx
|
|
1643
|
+
import { useMemo as useMemo3, useState as useState6 } from "react";
|
|
1644
|
+
import { Box as Box14, Text as Text14, useInput as useInput6 } from "ink";
|
|
1645
|
+
import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1371
1646
|
function TranscriptView({ loading, data, error: error51 }) {
|
|
1372
1647
|
const size = useTerminalSize();
|
|
1373
|
-
const [scroll, setScroll] =
|
|
1648
|
+
const [scroll, setScroll] = useState6(0);
|
|
1374
1649
|
const segments = data?.segments ?? [];
|
|
1375
1650
|
const innerWidth = Math.max(10, size.columns - 2);
|
|
1376
1651
|
const heights = useMemo3(
|
|
@@ -1383,7 +1658,7 @@ function TranscriptView({ loading, data, error: error51 }) {
|
|
|
1383
1658
|
const budget = Math.max(3, size.rows - 3);
|
|
1384
1659
|
const win = windowByHeights(heights, scroll, budget);
|
|
1385
1660
|
const page = Math.max(1, budget - 1);
|
|
1386
|
-
|
|
1661
|
+
useInput6((input, key) => {
|
|
1387
1662
|
if (key.downArrow || input === "j") setScroll((s) => Math.min(win.maxScroll, s + 1));
|
|
1388
1663
|
else if (key.upArrow || input === "k") setScroll((s) => Math.max(0, s - 1));
|
|
1389
1664
|
else if (key.pageDown || input === " ") setScroll((s) => Math.min(win.maxScroll, s + page));
|
|
@@ -1392,42 +1667,42 @@ function TranscriptView({ loading, data, error: error51 }) {
|
|
|
1392
1667
|
else if (input === "G") setScroll(win.maxScroll);
|
|
1393
1668
|
});
|
|
1394
1669
|
if (loading) {
|
|
1395
|
-
return /* @__PURE__ */
|
|
1670
|
+
return /* @__PURE__ */ jsx16(Box14, { paddingX: 1, children: /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "Loading transcript\u2026" }) });
|
|
1396
1671
|
}
|
|
1397
1672
|
if (error51) {
|
|
1398
|
-
return /* @__PURE__ */
|
|
1399
|
-
/* @__PURE__ */
|
|
1673
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", paddingX: 1, children: [
|
|
1674
|
+
/* @__PURE__ */ jsxs13(Text14, { color: "red", children: [
|
|
1400
1675
|
"! ",
|
|
1401
1676
|
error51
|
|
1402
1677
|
] }),
|
|
1403
|
-
/* @__PURE__ */
|
|
1678
|
+
/* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "q / esc / \u2190 back" })
|
|
1404
1679
|
] });
|
|
1405
1680
|
}
|
|
1406
1681
|
if (!data) {
|
|
1407
|
-
return /* @__PURE__ */
|
|
1682
|
+
return /* @__PURE__ */ jsx16(Box14, { paddingX: 1, children: /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "No transcript." }) });
|
|
1408
1683
|
}
|
|
1409
1684
|
const title = data.summary?.title ?? "Transcript";
|
|
1410
1685
|
const total = segments.length;
|
|
1411
1686
|
const more = win.maxScroll > 0;
|
|
1412
1687
|
const position = total === 0 ? "" : `${win.start + 1}\u2013${win.end} / ${total}`;
|
|
1413
|
-
return /* @__PURE__ */
|
|
1414
|
-
/* @__PURE__ */
|
|
1688
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", paddingX: 1, children: [
|
|
1689
|
+
/* @__PURE__ */ jsxs13(Text14, { bold: true, color: "green", children: [
|
|
1415
1690
|
title,
|
|
1416
|
-
more ? /* @__PURE__ */
|
|
1691
|
+
more ? /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: ` ${position}` }) : null
|
|
1417
1692
|
] }),
|
|
1418
|
-
/* @__PURE__ */
|
|
1419
|
-
/* @__PURE__ */
|
|
1693
|
+
/* @__PURE__ */ jsx16(Box14, { marginTop: 1, flexDirection: "column", children: total === 0 ? /* @__PURE__ */ jsx16(Text14, { children: data.text }) : segments.slice(win.start, win.end).map((segment, index) => /* @__PURE__ */ jsxs13(Text14, { children: [
|
|
1694
|
+
/* @__PURE__ */ jsxs13(Text14, { dimColor: true, children: [
|
|
1420
1695
|
"[",
|
|
1421
1696
|
formatClockMs(segment.startMs),
|
|
1422
1697
|
"] "
|
|
1423
1698
|
] }),
|
|
1424
|
-
segment.speaker ? /* @__PURE__ */
|
|
1699
|
+
segment.speaker ? /* @__PURE__ */ jsxs13(Text14, { color: "cyan", children: [
|
|
1425
1700
|
segment.speaker,
|
|
1426
1701
|
": "
|
|
1427
1702
|
] }) : null,
|
|
1428
1703
|
segment.text
|
|
1429
1704
|
] }, win.start + index)) }),
|
|
1430
|
-
/* @__PURE__ */
|
|
1705
|
+
/* @__PURE__ */ jsx16(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs13(Text14, { dimColor: true, children: [
|
|
1431
1706
|
more ? "\u2191\u2193 scroll \xB7 PgUp/PgDn \xB7 g/G top/bottom \xB7 " : "",
|
|
1432
1707
|
"q / esc / \u2190 back"
|
|
1433
1708
|
] }) })
|
|
@@ -1442,8 +1717,8 @@ var init_TranscriptView = __esm({
|
|
|
1442
1717
|
});
|
|
1443
1718
|
|
|
1444
1719
|
// src/tui/PermissionPreflightView.tsx
|
|
1445
|
-
import { Box as
|
|
1446
|
-
import { jsx as
|
|
1720
|
+
import { Box as Box15, Text as Text15 } from "ink";
|
|
1721
|
+
import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1447
1722
|
function statusGlyph2(status) {
|
|
1448
1723
|
switch (status) {
|
|
1449
1724
|
case "granted":
|
|
@@ -1459,41 +1734,41 @@ function PermissionPreflightView({
|
|
|
1459
1734
|
}) {
|
|
1460
1735
|
const allGranted = items.length > 0 && items.every((item) => item.status === "granted" && !item.requiresProcessRestart);
|
|
1461
1736
|
const hasRestartRequired = items.some((item) => item.requiresProcessRestart);
|
|
1462
|
-
return /* @__PURE__ */
|
|
1463
|
-
/* @__PURE__ */
|
|
1464
|
-
/* @__PURE__ */
|
|
1465
|
-
/* @__PURE__ */
|
|
1737
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 1, children: [
|
|
1738
|
+
/* @__PURE__ */ jsxs14(Text15, { children: [
|
|
1739
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "\u2039 " }),
|
|
1740
|
+
/* @__PURE__ */ jsx17(Text15, { bold: true, children: "Recording permissions" })
|
|
1466
1741
|
] }),
|
|
1467
|
-
/* @__PURE__ */
|
|
1742
|
+
/* @__PURE__ */ jsx17(Box15, { marginTop: 1, flexDirection: "column", children: items.length === 0 ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "Checking permissions\u2026" }) : items.map((item) => {
|
|
1468
1743
|
const status = statusGlyph2(item.status);
|
|
1469
1744
|
const restart = item.requiresProcessRestart === true;
|
|
1470
1745
|
const color = restart ? "yellow" : status.color;
|
|
1471
1746
|
const hint = restart ? item.hint ?? `${item.name} enabled. Run recappi record again to start.` : item.status === "granted" ? void 0 : item.hint ?? DEFAULT_HINTS[item.name];
|
|
1472
|
-
return /* @__PURE__ */
|
|
1473
|
-
/* @__PURE__ */
|
|
1474
|
-
/* @__PURE__ */
|
|
1475
|
-
/* @__PURE__ */
|
|
1476
|
-
/* @__PURE__ */
|
|
1747
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
|
|
1748
|
+
/* @__PURE__ */ jsxs14(Text15, { children: [
|
|
1749
|
+
/* @__PURE__ */ jsx17(Text15, { bold: true, color, children: status.glyph }),
|
|
1750
|
+
/* @__PURE__ */ jsx17(Text15, { bold: true, children: ` ${item.name}` }),
|
|
1751
|
+
/* @__PURE__ */ jsx17(Text15, { color, children: ` ${status.label}` })
|
|
1477
1752
|
] }),
|
|
1478
|
-
hint ? /* @__PURE__ */
|
|
1753
|
+
hint ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: ` ${hint}` }) : null
|
|
1479
1754
|
] }, item.name);
|
|
1480
1755
|
}) }),
|
|
1481
|
-
/* @__PURE__ */
|
|
1482
|
-
/* @__PURE__ */
|
|
1483
|
-
/* @__PURE__ */
|
|
1484
|
-
/* @__PURE__ */
|
|
1485
|
-
] }) : /* @__PURE__ */
|
|
1486
|
-
/* @__PURE__ */
|
|
1487
|
-
/* @__PURE__ */
|
|
1488
|
-
/* @__PURE__ */
|
|
1756
|
+
/* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: allGranted ? /* @__PURE__ */ jsx17(Text15, { bold: true, color: "green", children: "\u2713 All set \u2014 ready to record." }) : hasRestartRequired ? /* @__PURE__ */ jsxs14(Text15, { children: [
|
|
1757
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "Run recappi record again to start, or press " }),
|
|
1758
|
+
/* @__PURE__ */ jsx17(Text15, { bold: true, color: "cyan", children: "r" }),
|
|
1759
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " to retry." })
|
|
1760
|
+
] }) : /* @__PURE__ */ jsxs14(Text15, { children: [
|
|
1761
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "Grant the permissions above, then press " }),
|
|
1762
|
+
/* @__PURE__ */ jsx17(Text15, { bold: true, color: "cyan", children: "r" }),
|
|
1763
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " to recheck." })
|
|
1489
1764
|
] }) }),
|
|
1490
|
-
/* @__PURE__ */
|
|
1491
|
-
/* @__PURE__ */
|
|
1492
|
-
/* @__PURE__ */
|
|
1493
|
-
/* @__PURE__ */
|
|
1494
|
-
/* @__PURE__ */
|
|
1495
|
-
/* @__PURE__ */
|
|
1496
|
-
/* @__PURE__ */
|
|
1765
|
+
/* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text15, { children: [
|
|
1766
|
+
/* @__PURE__ */ jsx17(Text15, { color: "cyan", children: "r" }),
|
|
1767
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " recheck \xB7 " }),
|
|
1768
|
+
/* @__PURE__ */ jsx17(Text15, { color: "cyan", children: "o" }),
|
|
1769
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " open System Settings \xB7 " }),
|
|
1770
|
+
/* @__PURE__ */ jsx17(Text15, { color: "cyan", children: "esc" }),
|
|
1771
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " back" })
|
|
1497
1772
|
] }) })
|
|
1498
1773
|
] });
|
|
1499
1774
|
}
|
|
@@ -1509,9 +1784,9 @@ var init_PermissionPreflightView = __esm({
|
|
|
1509
1784
|
});
|
|
1510
1785
|
|
|
1511
1786
|
// src/tui/RecordSetupView.tsx
|
|
1512
|
-
import { useEffect as useEffect3, useRef as
|
|
1513
|
-
import { Box as
|
|
1514
|
-
import { jsx as
|
|
1787
|
+
import { useEffect as useEffect3, useRef as useRef3, useState as useState7 } from "react";
|
|
1788
|
+
import { Box as Box16, Text as Text16, useInput as useInput7 } from "ink";
|
|
1789
|
+
import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1515
1790
|
function levelDb2(level) {
|
|
1516
1791
|
if (level <= 0.03) return "silent";
|
|
1517
1792
|
return `${Math.round(level * 60 - 60)} dB`;
|
|
@@ -1522,11 +1797,11 @@ function meterBar(level, width) {
|
|
|
1522
1797
|
return "\u2587".repeat(filled) + "\u2591".repeat(Math.max(0, width - filled));
|
|
1523
1798
|
}
|
|
1524
1799
|
function InputMeter({ level }) {
|
|
1525
|
-
if (level == null) return /* @__PURE__ */
|
|
1800
|
+
if (level == null) return /* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "\u2014" });
|
|
1526
1801
|
const silent = level <= 0.03;
|
|
1527
|
-
return /* @__PURE__ */
|
|
1528
|
-
/* @__PURE__ */
|
|
1529
|
-
/* @__PURE__ */
|
|
1802
|
+
return /* @__PURE__ */ jsxs15(Text16, { children: [
|
|
1803
|
+
/* @__PURE__ */ jsx18(Text16, { color: silent ? "yellow" : "cyan", children: meterBar(level, METER_W) }),
|
|
1804
|
+
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: ` ${levelDb2(level)}` })
|
|
1530
1805
|
] });
|
|
1531
1806
|
}
|
|
1532
1807
|
function RecordSetupView({
|
|
@@ -1536,13 +1811,13 @@ function RecordSetupView({
|
|
|
1536
1811
|
onCancel,
|
|
1537
1812
|
onSelectionChange
|
|
1538
1813
|
}) {
|
|
1539
|
-
const [srcIdx, setSrcIdx] =
|
|
1540
|
-
const [includeMic, setIncludeMic] =
|
|
1541
|
-
const [micIdx, setMicIdx] =
|
|
1814
|
+
const [srcIdx, setSrcIdx] = useState7(0);
|
|
1815
|
+
const [includeMic, setIncludeMic] = useState7(true);
|
|
1816
|
+
const [micIdx, setMicIdx] = useState7(
|
|
1542
1817
|
() => Math.max(0, model.microphones?.findIndex((device) => device.isDefault) ?? 0)
|
|
1543
1818
|
);
|
|
1544
|
-
const [sceneIdx, setSceneIdx] =
|
|
1545
|
-
const userPickedMic =
|
|
1819
|
+
const [sceneIdx, setSceneIdx] = useState7(0);
|
|
1820
|
+
const userPickedMic = useRef3(false);
|
|
1546
1821
|
const sources = model.sources;
|
|
1547
1822
|
const microphones = model.microphones ?? [];
|
|
1548
1823
|
const selected = sources[Math.min(srcIdx, Math.max(0, sources.length - 1))];
|
|
@@ -1573,7 +1848,7 @@ function RecordSetupView({
|
|
|
1573
1848
|
const di = microphones.findIndex((device) => device.isDefault);
|
|
1574
1849
|
if (di > 0) setMicIdx(di);
|
|
1575
1850
|
}, [microphones.length]);
|
|
1576
|
-
|
|
1851
|
+
useInput7((input, key) => {
|
|
1577
1852
|
if (key.upArrow || input === "k") setSrcIdx((i) => Math.max(0, i - 1));
|
|
1578
1853
|
else if (key.downArrow || input === "j") setSrcIdx((i) => Math.min(sources.length - 1, i + 1));
|
|
1579
1854
|
else if (input === " ") setIncludeMic((m) => !m);
|
|
@@ -1584,26 +1859,26 @@ function RecordSetupView({
|
|
|
1584
1859
|
else if (key.return && selected) onStart(selection);
|
|
1585
1860
|
else if (key.escape) onCancel();
|
|
1586
1861
|
});
|
|
1587
|
-
const sourceList = /* @__PURE__ */
|
|
1588
|
-
/* @__PURE__ */
|
|
1589
|
-
/* @__PURE__ */
|
|
1590
|
-
/* @__PURE__ */
|
|
1862
|
+
const sourceList = /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
1863
|
+
/* @__PURE__ */ jsxs15(Box16, { children: [
|
|
1864
|
+
/* @__PURE__ */ jsx18(Box16, { width: 36, children: /* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "SOURCE" }) }),
|
|
1865
|
+
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "INPUT" })
|
|
1591
1866
|
] }),
|
|
1592
1867
|
sources.map((s, i) => {
|
|
1593
1868
|
const on = i === srcIdx;
|
|
1594
|
-
return /* @__PURE__ */
|
|
1595
|
-
/* @__PURE__ */
|
|
1869
|
+
return /* @__PURE__ */ jsxs15(Box16, { children: [
|
|
1870
|
+
/* @__PURE__ */ jsx18(Box16, { width: 36, children: /* @__PURE__ */ jsxs15(Text16, { color: on ? "cyan" : void 0, wrap: "truncate-end", children: [
|
|
1596
1871
|
on ? "\u25B8 \u25CF " : " \u25CB ",
|
|
1597
1872
|
s.label
|
|
1598
1873
|
] }) }),
|
|
1599
|
-
/* @__PURE__ */
|
|
1874
|
+
/* @__PURE__ */ jsx18(InputMeter, { level: levels?.bySourceId?.[s.id] })
|
|
1600
1875
|
] }, s.id);
|
|
1601
1876
|
}),
|
|
1602
|
-
!hasAppSource ? /* @__PURE__ */
|
|
1877
|
+
!hasAppSource ? /* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "No app-specific sources available right now" }) : null
|
|
1603
1878
|
] });
|
|
1604
|
-
const capturePlan = /* @__PURE__ */
|
|
1605
|
-
/* @__PURE__ */
|
|
1606
|
-
/* @__PURE__ */
|
|
1879
|
+
const capturePlan = /* @__PURE__ */ jsxs15(Text16, { children: [
|
|
1880
|
+
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Capture " }),
|
|
1881
|
+
/* @__PURE__ */ jsx18(Text16, { children: includeMic ? `${selected?.label ?? "System audio"} + microphone` : `${selected?.label ?? "System audio"} only` })
|
|
1607
1882
|
] });
|
|
1608
1883
|
const shortcuts = [
|
|
1609
1884
|
hasMultipleSources ? "\u2191\u2193 source" : void 0,
|
|
@@ -1613,33 +1888,33 @@ function RecordSetupView({
|
|
|
1613
1888
|
"\u23CE start recording",
|
|
1614
1889
|
"esc cancel"
|
|
1615
1890
|
].filter(Boolean).join(" \xB7 ");
|
|
1616
|
-
return /* @__PURE__ */
|
|
1617
|
-
/* @__PURE__ */
|
|
1618
|
-
/* @__PURE__ */
|
|
1891
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", paddingX: 1, children: [
|
|
1892
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: "green", children: "New recording" }),
|
|
1893
|
+
/* @__PURE__ */ jsxs15(Box16, { marginTop: 1, flexDirection: "column", children: [
|
|
1619
1894
|
sourceList,
|
|
1620
|
-
/* @__PURE__ */
|
|
1895
|
+
/* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: capturePlan })
|
|
1621
1896
|
] }),
|
|
1622
|
-
/* @__PURE__ */
|
|
1623
|
-
/* @__PURE__ */
|
|
1624
|
-
/* @__PURE__ */
|
|
1625
|
-
/* @__PURE__ */
|
|
1626
|
-
/* @__PURE__ */
|
|
1897
|
+
/* @__PURE__ */ jsxs15(Box16, { marginTop: 1, flexDirection: "column", children: [
|
|
1898
|
+
/* @__PURE__ */ jsxs15(Text16, { children: [
|
|
1899
|
+
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Microphone " }),
|
|
1900
|
+
/* @__PURE__ */ jsx18(Text16, { color: includeMic ? "green" : "gray", children: includeMic ? "[x] include mic" : "[ ] include mic" }),
|
|
1901
|
+
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " (space)" })
|
|
1627
1902
|
] }),
|
|
1628
|
-
selectedMic ? /* @__PURE__ */
|
|
1629
|
-
/* @__PURE__ */
|
|
1630
|
-
/* @__PURE__ */
|
|
1903
|
+
selectedMic ? /* @__PURE__ */ jsxs15(Box16, { children: [
|
|
1904
|
+
/* @__PURE__ */ jsx18(Box16, { width: 36, children: /* @__PURE__ */ jsxs15(Text16, { dimColor: !includeMic, wrap: "truncate-end", children: [
|
|
1905
|
+
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Mic device " }),
|
|
1631
1906
|
selectedMic.label
|
|
1632
1907
|
] }) }),
|
|
1633
|
-
includeMic ? /* @__PURE__ */
|
|
1908
|
+
includeMic ? /* @__PURE__ */ jsx18(InputMeter, { level: levels?.byMicrophoneId?.[selectedMic.id] }) : null
|
|
1634
1909
|
] }) : null,
|
|
1635
|
-
includeMic && hasMultipleMicrophones ? /* @__PURE__ */
|
|
1636
|
-
model.scenes.length > 0 ? /* @__PURE__ */
|
|
1637
|
-
/* @__PURE__ */
|
|
1638
|
-
/* @__PURE__ */
|
|
1639
|
-
model.scenes.length > 1 ? /* @__PURE__ */
|
|
1910
|
+
includeMic && hasMultipleMicrophones ? /* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " (m to change device)" }) : null,
|
|
1911
|
+
model.scenes.length > 0 ? /* @__PURE__ */ jsxs15(Text16, { children: [
|
|
1912
|
+
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Scene " }),
|
|
1913
|
+
/* @__PURE__ */ jsx18(Text16, { children: model.scenes[sceneIdx]?.label ?? "Default" }),
|
|
1914
|
+
model.scenes.length > 1 ? /* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " (s to change)" }) : null
|
|
1640
1915
|
] }) : null
|
|
1641
1916
|
] }),
|
|
1642
|
-
/* @__PURE__ */
|
|
1917
|
+
/* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(Text16, { dimColor: true, children: shortcuts }) })
|
|
1643
1918
|
] });
|
|
1644
1919
|
}
|
|
1645
1920
|
var METER_W;
|
|
@@ -1651,9 +1926,9 @@ var init_RecordSetupView = __esm({
|
|
|
1651
1926
|
});
|
|
1652
1927
|
|
|
1653
1928
|
// src/tui/RecordFrame.tsx
|
|
1654
|
-
import { useState as
|
|
1655
|
-
import { Box as
|
|
1656
|
-
import { jsx as
|
|
1929
|
+
import { useState as useState8 } from "react";
|
|
1930
|
+
import { Box as Box17, Text as Text17, useInput as useInput8 } from "ink";
|
|
1931
|
+
import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1657
1932
|
function levelDb3(level) {
|
|
1658
1933
|
if (level <= 0.03) return "silent";
|
|
1659
1934
|
return `${Math.round(level * 60 - 60)} dB`;
|
|
@@ -1662,14 +1937,14 @@ function CompactMeter({ label, level }) {
|
|
|
1662
1937
|
const width = 12;
|
|
1663
1938
|
const filled = Math.max(0, Math.min(width, Math.round(Math.max(0, Math.min(1, level)) * width)));
|
|
1664
1939
|
const silent = level <= 0.03;
|
|
1665
|
-
return /* @__PURE__ */
|
|
1666
|
-
/* @__PURE__ */
|
|
1940
|
+
return /* @__PURE__ */ jsxs16(Text17, { children: [
|
|
1941
|
+
/* @__PURE__ */ jsxs16(Text17, { dimColor: true, children: [
|
|
1667
1942
|
label,
|
|
1668
1943
|
" "
|
|
1669
1944
|
] }),
|
|
1670
|
-
/* @__PURE__ */
|
|
1671
|
-
/* @__PURE__ */
|
|
1672
|
-
/* @__PURE__ */
|
|
1945
|
+
/* @__PURE__ */ jsx19(Text17, { color: silent ? "yellow" : "cyan", children: "\u25CF".repeat(filled) }),
|
|
1946
|
+
/* @__PURE__ */ jsx19(Text17, { dimColor: true, children: "\xB7".repeat(width - filled) }),
|
|
1947
|
+
/* @__PURE__ */ jsx19(Text17, { dimColor: true, children: ` ${levelDb3(level)}` })
|
|
1673
1948
|
] });
|
|
1674
1949
|
}
|
|
1675
1950
|
function CaptionColumn({
|
|
@@ -1688,7 +1963,7 @@ function CaptionColumn({
|
|
|
1688
1963
|
chosen.unshift(lines[i]);
|
|
1689
1964
|
used += h;
|
|
1690
1965
|
}
|
|
1691
|
-
return /* @__PURE__ */
|
|
1966
|
+
return /* @__PURE__ */ jsx19(Box17, { width, flexDirection: "column", children: chosen.length === 0 ? /* @__PURE__ */ jsx19(Text17, { dimColor: true, children: "Listening for speech\u2026" }) : chosen.map((l, i) => /* @__PURE__ */ jsx19(Text17, { dimColor: dim, wrap: "wrap", children: l }, i)) });
|
|
1692
1967
|
}
|
|
1693
1968
|
function outcomeLine(telemetry, artifact) {
|
|
1694
1969
|
if (telemetry.status === "recording" || telemetry.status === "starting") {
|
|
@@ -1720,10 +1995,10 @@ function RecordFrame({
|
|
|
1720
1995
|
spinnerFrame = 0
|
|
1721
1996
|
}) {
|
|
1722
1997
|
const size = useTerminalSize();
|
|
1723
|
-
const [captionMode, setCaptionMode] =
|
|
1724
|
-
const [scrollBack, setScrollBack] =
|
|
1998
|
+
const [captionMode, setCaptionMode] = useState8("both");
|
|
1999
|
+
const [scrollBack, setScrollBack] = useState8(0);
|
|
1725
2000
|
const PAGE = 8;
|
|
1726
|
-
|
|
2001
|
+
useInput8((input, key) => {
|
|
1727
2002
|
if (input === "c") {
|
|
1728
2003
|
setCaptionMode((m) => m === "both" ? "source" : m === "source" ? "translation" : "both");
|
|
1729
2004
|
} else if (key.upArrow || input === "k") setScrollBack((s) => s + 1);
|
|
@@ -1751,49 +2026,49 @@ function RecordFrame({
|
|
|
1751
2026
|
] : [];
|
|
1752
2027
|
const status = telemetry.sizeBytes ? formatBytes3(telemetry.sizeBytes) : "";
|
|
1753
2028
|
const sourceLine = [telemetry.sourceLabel, telemetry.micEnabled ? "Microphone" : null, status || null].filter(Boolean).join(" \xB7 ");
|
|
1754
|
-
return /* @__PURE__ */
|
|
1755
|
-
/* @__PURE__ */
|
|
1756
|
-
/* @__PURE__ */
|
|
1757
|
-
/* @__PURE__ */
|
|
1758
|
-
/* @__PURE__ */
|
|
2029
|
+
return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", paddingX: 1, height: size.rows, children: [
|
|
2030
|
+
/* @__PURE__ */ jsxs16(Box17, { justifyContent: "space-between", children: [
|
|
2031
|
+
/* @__PURE__ */ jsxs16(Text17, { children: [
|
|
2032
|
+
/* @__PURE__ */ jsx19(Text17, { bold: true, color: "green", children: "recappi" }),
|
|
2033
|
+
/* @__PURE__ */ jsx19(Text17, { dimColor: true, children: " \xB7 Recording" })
|
|
1759
2034
|
] }),
|
|
1760
|
-
/* @__PURE__ */
|
|
1761
|
-
/* @__PURE__ */
|
|
1762
|
-
/* @__PURE__ */
|
|
2035
|
+
/* @__PURE__ */ jsxs16(Text17, { children: [
|
|
2036
|
+
/* @__PURE__ */ jsx19(Text17, { bold: true, color: recording ? "red" : "gray", children: stateLabel }),
|
|
2037
|
+
/* @__PURE__ */ jsx19(Text17, { dimColor: true, children: ` ${elapsed}${ids ? ` \xB7 ${ids}` : ""}` })
|
|
1763
2038
|
] })
|
|
1764
2039
|
] }),
|
|
1765
|
-
/* @__PURE__ */
|
|
1766
|
-
/* @__PURE__ */
|
|
1767
|
-
/* @__PURE__ */
|
|
1768
|
-
/* @__PURE__ */
|
|
1769
|
-
/* @__PURE__ */
|
|
2040
|
+
/* @__PURE__ */ jsx19(Text17, { dimColor: true, children: "\u2500".repeat(innerWidth) }),
|
|
2041
|
+
/* @__PURE__ */ jsxs16(Box17, { flexGrow: 1, children: [
|
|
2042
|
+
/* @__PURE__ */ jsxs16(Box17, { width: listWidth, flexDirection: "column", children: [
|
|
2043
|
+
/* @__PURE__ */ jsx19(Text17, { dimColor: true, children: `RECORDINGS \xB7 ${recordings.length}` }),
|
|
2044
|
+
/* @__PURE__ */ jsx19(Box17, { marginTop: 1, flexDirection: "column", children: recordings.slice(0, Math.max(1, size.rows - 8)).map((rec, i) => {
|
|
1770
2045
|
const st = recordingProcessingState(rec, void 0, spinnerFrame);
|
|
1771
2046
|
const sel = i === selectedIndex;
|
|
1772
|
-
return /* @__PURE__ */
|
|
1773
|
-
/* @__PURE__ */
|
|
1774
|
-
/* @__PURE__ */
|
|
1775
|
-
/* @__PURE__ */
|
|
2047
|
+
return /* @__PURE__ */ jsxs16(Box17, { children: [
|
|
2048
|
+
/* @__PURE__ */ jsx19(Box17, { width: 2, children: /* @__PURE__ */ jsx19(Text17, { color: "cyan", children: sel ? "\u25B8" : "" }) }),
|
|
2049
|
+
/* @__PURE__ */ jsx19(Box17, { width: 2, children: /* @__PURE__ */ jsx19(Text17, { color: st.color, children: st.glyph }) }),
|
|
2050
|
+
/* @__PURE__ */ jsx19(Box17, { width: listWidth - 4, children: /* @__PURE__ */ jsx19(Text17, { bold: sel, wrap: "truncate-end", children: recordingTitle2(rec) }) })
|
|
1776
2051
|
] }, rec.recordingId);
|
|
1777
2052
|
}) })
|
|
1778
2053
|
] }),
|
|
1779
|
-
/* @__PURE__ */
|
|
1780
|
-
/* @__PURE__ */
|
|
1781
|
-
/* @__PURE__ */
|
|
1782
|
-
/* @__PURE__ */
|
|
1783
|
-
telemetry.level ? /* @__PURE__ */
|
|
1784
|
-
/* @__PURE__ */
|
|
1785
|
-
telemetry.micEnabled ? /* @__PURE__ */
|
|
1786
|
-
telemetry.micEnabled ? /* @__PURE__ */
|
|
1787
|
-
] }) : /* @__PURE__ */
|
|
1788
|
-
/* @__PURE__ */
|
|
1789
|
-
/* @__PURE__ */
|
|
1790
|
-
captionMode !== "translation" ? /* @__PURE__ */
|
|
1791
|
-
captionMode === "both" ? /* @__PURE__ */
|
|
1792
|
-
captionMode !== "source" ? /* @__PURE__ */
|
|
1793
|
-
scrollBack > 0 ? /* @__PURE__ */
|
|
2054
|
+
/* @__PURE__ */ jsx19(Box17, { width: 3, flexDirection: "column", alignItems: "center", children: Array.from({ length: Math.max(1, size.rows - 6) }, (_, i) => /* @__PURE__ */ jsx19(Text17, { dimColor: true, children: "\u2502" }, i)) }),
|
|
2055
|
+
/* @__PURE__ */ jsxs16(Box17, { width: rightWidth, flexDirection: "column", children: [
|
|
2056
|
+
/* @__PURE__ */ jsx19(Text17, { bold: true, wrap: "truncate-end", children: title }),
|
|
2057
|
+
/* @__PURE__ */ jsx19(Text17, { dimColor: true, wrap: "truncate-end", children: sourceLine }),
|
|
2058
|
+
telemetry.level ? /* @__PURE__ */ jsxs16(Box17, { children: [
|
|
2059
|
+
/* @__PURE__ */ jsx19(CompactMeter, { label: "System", level: telemetry.level.system ?? 0 }),
|
|
2060
|
+
telemetry.micEnabled ? /* @__PURE__ */ jsx19(Text17, { dimColor: true, children: " " }) : null,
|
|
2061
|
+
telemetry.micEnabled ? /* @__PURE__ */ jsx19(CompactMeter, { label: "Mic", level: telemetry.level.mic ?? 0 }) : null
|
|
2062
|
+
] }) : /* @__PURE__ */ jsx19(Text17, { dimColor: true, children: "Capturing audio\u2026" }),
|
|
2063
|
+
/* @__PURE__ */ jsxs16(Box17, { marginTop: 1, flexDirection: "column", flexGrow: 1, children: [
|
|
2064
|
+
/* @__PURE__ */ jsxs16(Box17, { children: [
|
|
2065
|
+
captionMode !== "translation" ? /* @__PURE__ */ jsx19(Box17, { width: captionMode === "both" ? Math.floor((rightWidth - 3) / 2) : rightWidth, children: /* @__PURE__ */ jsx19(Text17, { bold: true, dimColor: true, children: "ORIGINAL" }) }) : null,
|
|
2066
|
+
captionMode === "both" ? /* @__PURE__ */ jsx19(Box17, { width: 3 }) : null,
|
|
2067
|
+
captionMode !== "source" ? /* @__PURE__ */ jsx19(Text17, { bold: true, dimColor: true, children: "TRANSLATION" }) : null,
|
|
2068
|
+
scrollBack > 0 ? /* @__PURE__ */ jsx19(Text17, { color: "yellow", children: " \u23F8 scrolled \xB7 G live" }) : null
|
|
1794
2069
|
] }),
|
|
1795
|
-
/* @__PURE__ */
|
|
1796
|
-
captionMode !== "translation" ? /* @__PURE__ */
|
|
2070
|
+
/* @__PURE__ */ jsxs16(Box17, { children: [
|
|
2071
|
+
captionMode !== "translation" ? /* @__PURE__ */ jsx19(
|
|
1797
2072
|
CaptionColumn,
|
|
1798
2073
|
{
|
|
1799
2074
|
lines: sourceLines,
|
|
@@ -1802,8 +2077,8 @@ function RecordFrame({
|
|
|
1802
2077
|
scrollBack
|
|
1803
2078
|
}
|
|
1804
2079
|
) : null,
|
|
1805
|
-
captionMode === "both" ? /* @__PURE__ */
|
|
1806
|
-
captionMode !== "source" ? /* @__PURE__ */
|
|
2080
|
+
captionMode === "both" ? /* @__PURE__ */ jsx19(Box17, { width: 3, flexDirection: "column", children: Array.from({ length: Math.min(captionRows, 12) }, (_, i) => /* @__PURE__ */ jsx19(Text17, { dimColor: true, children: "\u2502" }, i)) }) : null,
|
|
2081
|
+
captionMode !== "source" ? /* @__PURE__ */ jsx19(
|
|
1807
2082
|
CaptionColumn,
|
|
1808
2083
|
{
|
|
1809
2084
|
lines: translationLines,
|
|
@@ -1815,14 +2090,14 @@ function RecordFrame({
|
|
|
1815
2090
|
) : null
|
|
1816
2091
|
] })
|
|
1817
2092
|
] }),
|
|
1818
|
-
/* @__PURE__ */
|
|
1819
|
-
/* @__PURE__ */
|
|
1820
|
-
/* @__PURE__ */
|
|
2093
|
+
/* @__PURE__ */ jsxs16(Box17, { marginTop: 1, children: [
|
|
2094
|
+
/* @__PURE__ */ jsx19(Text17, { bold: true, dimColor: true, children: "OUTCOME " }),
|
|
2095
|
+
/* @__PURE__ */ jsx19(Text17, { dimColor: true, children: outcomeLine(telemetry, artifact) })
|
|
1821
2096
|
] })
|
|
1822
2097
|
] })
|
|
1823
2098
|
] }),
|
|
1824
|
-
/* @__PURE__ */
|
|
1825
|
-
/* @__PURE__ */
|
|
2099
|
+
/* @__PURE__ */ jsx19(Text17, { dimColor: true, children: "\u2500".repeat(innerWidth) }),
|
|
2100
|
+
/* @__PURE__ */ jsx19(Text17, { dimColor: true, children: recording ? `q stop & save \xB7 c captions (${captionMode}) \xB7 \u2191\u2193 scroll \xB7 G live` : `\u23CE open \xB7 T re-transcribe \xB7 c captions (${captionMode}) \xB7 \u2191\u2193 scroll \xB7 1 overview 2 jobs 3 account` })
|
|
1826
2101
|
] });
|
|
1827
2102
|
}
|
|
1828
2103
|
var trimLead2, wrappedRows2;
|
|
@@ -1838,9 +2113,9 @@ var init_RecordFrame = __esm({
|
|
|
1838
2113
|
});
|
|
1839
2114
|
|
|
1840
2115
|
// src/tui/AppShell.tsx
|
|
1841
|
-
import { useCallback, useEffect as useEffect4, useRef as
|
|
1842
|
-
import { Box as
|
|
1843
|
-
import { Fragment as Fragment6, jsx as
|
|
2116
|
+
import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef4, useState as useState9 } from "react";
|
|
2117
|
+
import { Box as Box18, Text as Text18, useApp, useInput as useInput9 } from "ink";
|
|
2118
|
+
import { Fragment as Fragment6, jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1844
2119
|
function recordErrorCopy(code, message) {
|
|
1845
2120
|
switch (code) {
|
|
1846
2121
|
case "record.helper_unavailable":
|
|
@@ -2011,6 +2286,7 @@ function AppShell({
|
|
|
2011
2286
|
onSyncRecordingText,
|
|
2012
2287
|
onSyncRecordingAudio,
|
|
2013
2288
|
onExportRecording,
|
|
2289
|
+
askRecording,
|
|
2014
2290
|
initialView = "overview",
|
|
2015
2291
|
openUrl: openUrl2,
|
|
2016
2292
|
copyText: copyText2,
|
|
@@ -2020,47 +2296,47 @@ function AppShell({
|
|
|
2020
2296
|
}) {
|
|
2021
2297
|
const { exit } = useApp();
|
|
2022
2298
|
const size = useTerminalSize();
|
|
2023
|
-
const [jobs, setJobs] =
|
|
2024
|
-
const [recordings, setRecordings] =
|
|
2025
|
-
const [recordingsNextCursor, setRecordingsNextCursor] =
|
|
2026
|
-
const [recordingsTotalCount, setRecordingsTotalCount] =
|
|
2027
|
-
const [stats, setStats] =
|
|
2028
|
-
const [accountStatus, setAccountStatus] =
|
|
2029
|
-
const [origin, setOrigin] =
|
|
2030
|
-
const [stack, setStack] =
|
|
2031
|
-
const [selected, setSelected] =
|
|
2032
|
-
const [spinnerFrame, setSpinnerFrame] =
|
|
2033
|
-
const [loadingMoreRecordings, setLoadingMoreRecordings] =
|
|
2034
|
-
const [revalidatingRecordings, setRevalidatingRecordings] =
|
|
2035
|
-
const [loaded, setLoaded] =
|
|
2036
|
-
const [loadError, setLoadError] =
|
|
2037
|
-
const [notice, setNotice] =
|
|
2038
|
-
const [summaryCache, setSummaryCache] =
|
|
2039
|
-
const [transcriptCache, setTranscriptCache] =
|
|
2299
|
+
const [jobs, setJobs] = useState9([]);
|
|
2300
|
+
const [recordings, setRecordings] = useState9([]);
|
|
2301
|
+
const [recordingsNextCursor, setRecordingsNextCursor] = useState9(null);
|
|
2302
|
+
const [recordingsTotalCount, setRecordingsTotalCount] = useState9(void 0);
|
|
2303
|
+
const [stats, setStats] = useState9(void 0);
|
|
2304
|
+
const [accountStatus, setAccountStatus] = useState9("loading");
|
|
2305
|
+
const [origin, setOrigin] = useState9("");
|
|
2306
|
+
const [stack, setStack] = useState9([{ kind: initialView }]);
|
|
2307
|
+
const [selected, setSelected] = useState9(0);
|
|
2308
|
+
const [spinnerFrame, setSpinnerFrame] = useState9(0);
|
|
2309
|
+
const [loadingMoreRecordings, setLoadingMoreRecordings] = useState9(false);
|
|
2310
|
+
const [revalidatingRecordings, setRevalidatingRecordings] = useState9(false);
|
|
2311
|
+
const [loaded, setLoaded] = useState9(false);
|
|
2312
|
+
const [loadError, setLoadError] = useState9(void 0);
|
|
2313
|
+
const [notice, setNotice] = useState9(void 0);
|
|
2314
|
+
const [summaryCache, setSummaryCache] = useState9(() => /* @__PURE__ */ new Map());
|
|
2315
|
+
const [transcriptCache, setTranscriptCache] = useState9(
|
|
2040
2316
|
() => /* @__PURE__ */ new Map()
|
|
2041
2317
|
);
|
|
2042
|
-
const [audioCache, setAudioCache] =
|
|
2043
|
-
const [downloadedIds, setDownloadedIds] =
|
|
2044
|
-
const summaryRefreshRef =
|
|
2045
|
-
const [liveRecord, setLiveRecord] =
|
|
2046
|
-
const [recordSetupInputs, setRecordSetupInputs] =
|
|
2318
|
+
const [audioCache, setAudioCache] = useState9(() => /* @__PURE__ */ new Map());
|
|
2319
|
+
const [downloadedIds, setDownloadedIds] = useState9(() => /* @__PURE__ */ new Set());
|
|
2320
|
+
const summaryRefreshRef = useRef4(null);
|
|
2321
|
+
const [liveRecord, setLiveRecord] = useState9(void 0);
|
|
2322
|
+
const [recordSetupInputs, setRecordSetupInputs] = useState9({
|
|
2047
2323
|
sources: DEFAULT_RECORDING_SOURCES,
|
|
2048
2324
|
microphones: []
|
|
2049
2325
|
});
|
|
2050
|
-
const [recordSetupSelection, setRecordSetupSelection] =
|
|
2326
|
+
const [recordSetupSelection, setRecordSetupSelection] = useState9(
|
|
2051
2327
|
DEFAULT_RECORDING_SELECTION
|
|
2052
2328
|
);
|
|
2053
|
-
const [recordSetupLevels, setRecordSetupLevels] =
|
|
2329
|
+
const [recordSetupLevels, setRecordSetupLevels] = useState9({
|
|
2054
2330
|
bySourceId: {},
|
|
2055
2331
|
byMicrophoneId: {}
|
|
2056
2332
|
});
|
|
2057
|
-
const autoTranscribeStartedSessionIds =
|
|
2333
|
+
const autoTranscribeStartedSessionIds = useRef4(/* @__PURE__ */ new Set());
|
|
2058
2334
|
const recordSetupModel = {
|
|
2059
2335
|
sources: recordSetupInputs.sources.length > 0 ? recordSetupInputs.sources : DEFAULT_RECORDING_SOURCES,
|
|
2060
2336
|
microphones: recordSetupInputs.microphones ?? [],
|
|
2061
2337
|
scenes: DEFAULT_RECORDING_SCENES
|
|
2062
2338
|
};
|
|
2063
|
-
const refreshDownloadedIds =
|
|
2339
|
+
const refreshDownloadedIds = useCallback2(async () => {
|
|
2064
2340
|
if (!listDownloadedRecordingIds) return;
|
|
2065
2341
|
try {
|
|
2066
2342
|
setDownloadedIds(await listDownloadedRecordingIds());
|
|
@@ -2071,6 +2347,12 @@ function AppShell({
|
|
|
2071
2347
|
void refreshDownloadedIds();
|
|
2072
2348
|
}, [refreshDownloadedIds]);
|
|
2073
2349
|
const screen = stack[stack.length - 1];
|
|
2350
|
+
const recordingsRef = useRef4(recordings);
|
|
2351
|
+
recordingsRef.current = recordings;
|
|
2352
|
+
const selectedRef = useRef4(selected);
|
|
2353
|
+
selectedRef.current = selected;
|
|
2354
|
+
const screenRef = useRef4(screen);
|
|
2355
|
+
screenRef.current = screen;
|
|
2074
2356
|
useEffect4(() => {
|
|
2075
2357
|
if (screen.kind !== "recordSetup" || !startRecordSetupPreview) return;
|
|
2076
2358
|
let cancelled = false;
|
|
@@ -2119,7 +2401,7 @@ function AppShell({
|
|
|
2119
2401
|
screen.kind,
|
|
2120
2402
|
startRecordSetupPreview
|
|
2121
2403
|
]);
|
|
2122
|
-
const beginLiveRecord =
|
|
2404
|
+
const beginLiveRecord = useCallback2(
|
|
2123
2405
|
(selection = DEFAULT_RECORDING_SELECTION) => {
|
|
2124
2406
|
const capture = recordingCaptureMappingFromSelection(selection, recordSetupModel.sources);
|
|
2125
2407
|
const telemetry = {
|
|
@@ -2161,7 +2443,7 @@ function AppShell({
|
|
|
2161
2443
|
},
|
|
2162
2444
|
[now, recordSetupModel.sources, startLiveRecord]
|
|
2163
2445
|
);
|
|
2164
|
-
const stopLiveRecord =
|
|
2446
|
+
const stopLiveRecord = useCallback2(async () => {
|
|
2165
2447
|
const current = liveRecord;
|
|
2166
2448
|
if (current?.kind === "live") {
|
|
2167
2449
|
const stoppingTelemetry = { ...current.telemetry, status: "stopping" };
|
|
@@ -2248,7 +2530,7 @@ function AppShell({
|
|
|
2248
2530
|
};
|
|
2249
2531
|
}, [peekTranscriptId, fetchTranscript]);
|
|
2250
2532
|
const peekSummary = peekTranscriptId ? summaryCache.get(peekTranscriptId) : void 0;
|
|
2251
|
-
const refresh =
|
|
2533
|
+
const refresh = useCallback2(async ({ resetRecordings = false } = {}) => {
|
|
2252
2534
|
let showingCachedRecordings = false;
|
|
2253
2535
|
if (resetRecordings && fetchCachedRecordings) {
|
|
2254
2536
|
try {
|
|
@@ -2278,9 +2560,15 @@ function AppShell({
|
|
|
2278
2560
|
setLoadError(jobsR.reason instanceof Error ? jobsR.reason.message : String(jobsR.reason));
|
|
2279
2561
|
}
|
|
2280
2562
|
if (recR.status === "fulfilled" && recR.value) {
|
|
2281
|
-
|
|
2563
|
+
const prevSelectedId = screenRef.current.kind === "overview" ? recordingsRef.current[selectedRef.current]?.recordingId : void 0;
|
|
2564
|
+
const freshItems = recR.value.items;
|
|
2565
|
+
setRecordings(freshItems);
|
|
2282
2566
|
setRecordingsNextCursor(recR.value.nextCursor ?? null);
|
|
2283
2567
|
setRecordingsTotalCount(recR.value.totalCount);
|
|
2568
|
+
if (prevSelectedId) {
|
|
2569
|
+
const idx = freshItems.findIndex((r) => r.recordingId === prevSelectedId);
|
|
2570
|
+
setSelected((s) => idx >= 0 ? idx : Math.min(s, Math.max(0, freshItems.length - 1)));
|
|
2571
|
+
}
|
|
2284
2572
|
}
|
|
2285
2573
|
if (statsR.status === "fulfilled" && statsR.value) setStats(statsR.value);
|
|
2286
2574
|
if (accountR.status === "fulfilled") {
|
|
@@ -2291,7 +2579,7 @@ function AppShell({
|
|
|
2291
2579
|
setLoaded(true);
|
|
2292
2580
|
if (showingCachedRecordings) setRevalidatingRecordings(false);
|
|
2293
2581
|
}, [fetchJobs, fetchRecordings, fetchCachedRecordings, fetchDashboardStats, fetchAccountStatus]);
|
|
2294
|
-
const transcribeStoppedRecording =
|
|
2582
|
+
const transcribeStoppedRecording = useCallback2(async () => {
|
|
2295
2583
|
const current = liveRecord;
|
|
2296
2584
|
if (current?.kind !== "stopped") return;
|
|
2297
2585
|
const artifact = current.artifact;
|
|
@@ -2371,7 +2659,7 @@ function AppShell({
|
|
|
2371
2659
|
setNotice("Transcription failed. Press enter to retry.");
|
|
2372
2660
|
}
|
|
2373
2661
|
}, [liveRecord, refresh, transcribeRecordingArtifact]);
|
|
2374
|
-
const retranscribeStoppedRecording =
|
|
2662
|
+
const retranscribeStoppedRecording = useCallback2(async () => {
|
|
2375
2663
|
const current = liveRecord;
|
|
2376
2664
|
if (current?.kind !== "stopped") return;
|
|
2377
2665
|
const artifact = current.artifact;
|
|
@@ -2430,7 +2718,7 @@ function AppShell({
|
|
|
2430
2718
|
setNotice("Re-transcription failed. Press T to retry.");
|
|
2431
2719
|
}
|
|
2432
2720
|
}, [liveRecord, onRetranscribe, refresh]);
|
|
2433
|
-
const retranscribeExistingRecording =
|
|
2721
|
+
const retranscribeExistingRecording = useCallback2(
|
|
2434
2722
|
async (recordingId) => {
|
|
2435
2723
|
if (!onRetranscribe) {
|
|
2436
2724
|
setNotice("Re-transcribe is not available in this CLI session.");
|
|
@@ -2447,7 +2735,7 @@ function AppShell({
|
|
|
2447
2735
|
},
|
|
2448
2736
|
[onRetranscribe, refresh]
|
|
2449
2737
|
);
|
|
2450
|
-
const refetchTranscript =
|
|
2738
|
+
const refetchTranscript = useCallback2(
|
|
2451
2739
|
(transcriptId) => {
|
|
2452
2740
|
setTranscriptCache((m) => new Map(m).set(transcriptId, "loading"));
|
|
2453
2741
|
setSummaryCache((m) => {
|
|
@@ -2459,7 +2747,7 @@ function AppShell({
|
|
|
2459
2747
|
},
|
|
2460
2748
|
[fetchTranscript]
|
|
2461
2749
|
);
|
|
2462
|
-
const resummarizeExistingRecording =
|
|
2750
|
+
const resummarizeExistingRecording = useCallback2(
|
|
2463
2751
|
async (recordingId) => {
|
|
2464
2752
|
if (!onResummarize) {
|
|
2465
2753
|
setNotice("Re-summarize is not available in this CLI session.");
|
|
@@ -2481,8 +2769,8 @@ function AppShell({
|
|
|
2481
2769
|
},
|
|
2482
2770
|
[onResummarize, refresh, recordings, now, refetchTranscript]
|
|
2483
2771
|
);
|
|
2484
|
-
const syncedTextRef =
|
|
2485
|
-
const syncRecordingText2 =
|
|
2772
|
+
const syncedTextRef = useRef4(/* @__PURE__ */ new Set());
|
|
2773
|
+
const syncRecordingText2 = useCallback2(
|
|
2486
2774
|
async (recordingId, opts = {}) => {
|
|
2487
2775
|
if (!onSyncRecordingText) return;
|
|
2488
2776
|
if (opts.manual) setNotice("Syncing text locally\u2026");
|
|
@@ -2496,7 +2784,7 @@ function AppShell({
|
|
|
2496
2784
|
},
|
|
2497
2785
|
[onSyncRecordingText]
|
|
2498
2786
|
);
|
|
2499
|
-
const syncRecordingAudio2 =
|
|
2787
|
+
const syncRecordingAudio2 = useCallback2(
|
|
2500
2788
|
async (recordingId) => {
|
|
2501
2789
|
if (!onSyncRecordingAudio) {
|
|
2502
2790
|
setNotice("Audio sync is not available in this CLI session.");
|
|
@@ -2512,7 +2800,7 @@ function AppShell({
|
|
|
2512
2800
|
},
|
|
2513
2801
|
[onSyncRecordingAudio]
|
|
2514
2802
|
);
|
|
2515
|
-
const exportRecordingForAgent =
|
|
2803
|
+
const exportRecordingForAgent = useCallback2(
|
|
2516
2804
|
async (recordingId) => {
|
|
2517
2805
|
if (!onExportRecording) {
|
|
2518
2806
|
setNotice("Export is not available in this CLI session.");
|
|
@@ -2540,7 +2828,7 @@ function AppShell({
|
|
|
2540
2828
|
autoTranscribeStartedSessionIds.current.add(artifact.sessionId);
|
|
2541
2829
|
void transcribeStoppedRecording();
|
|
2542
2830
|
}, [liveRecord, transcribeRecordingArtifact, transcribeStoppedRecording]);
|
|
2543
|
-
const loadMoreRecordings =
|
|
2831
|
+
const loadMoreRecordings = useCallback2(async () => {
|
|
2544
2832
|
if (!fetchRecordings || !recordingsNextCursor || loadingMoreRecordings) return;
|
|
2545
2833
|
setLoadingMoreRecordings(true);
|
|
2546
2834
|
try {
|
|
@@ -2570,7 +2858,7 @@ function AppShell({
|
|
|
2570
2858
|
const id = setInterval(() => void refresh(), pollMs);
|
|
2571
2859
|
return () => clearInterval(id);
|
|
2572
2860
|
}, [refresh, pollMs]);
|
|
2573
|
-
const transcriptCacheRef =
|
|
2861
|
+
const transcriptCacheRef = useRef4(transcriptCache);
|
|
2574
2862
|
transcriptCacheRef.current = transcriptCache;
|
|
2575
2863
|
useEffect4(() => {
|
|
2576
2864
|
const id = setInterval(() => {
|
|
@@ -2640,7 +2928,7 @@ function AppShell({
|
|
|
2640
2928
|
selected,
|
|
2641
2929
|
visibleRecordingRows
|
|
2642
2930
|
]);
|
|
2643
|
-
const openTranscript =
|
|
2931
|
+
const openTranscript = useCallback2(
|
|
2644
2932
|
async (transcriptId) => {
|
|
2645
2933
|
setStack((st) => [...st, { kind: "transcript", loading: true }]);
|
|
2646
2934
|
try {
|
|
@@ -2679,7 +2967,7 @@ function AppShell({
|
|
|
2679
2967
|
void syncRecordingText2(detailRecordingId);
|
|
2680
2968
|
}, [detailRecordingId, syncRecordingText2]);
|
|
2681
2969
|
const setAudio = (recordingId, action) => setAudioCache((m) => new Map(m).set(recordingId, action));
|
|
2682
|
-
const runAudio =
|
|
2970
|
+
const runAudio = useCallback2(
|
|
2683
2971
|
async (recordingId, mode) => {
|
|
2684
2972
|
if (!recordingAudio) {
|
|
2685
2973
|
setNotice("Audio actions are not available");
|
|
@@ -2717,8 +3005,9 @@ function AppShell({
|
|
|
2717
3005
|
setStack((st) => st[st.length - 1]?.kind === "record" ? st : [...st, { kind: "record" }]);
|
|
2718
3006
|
setNotice(void 0);
|
|
2719
3007
|
};
|
|
2720
|
-
|
|
3008
|
+
useInput9((input, key) => {
|
|
2721
3009
|
setNotice(void 0);
|
|
3010
|
+
if (screen.kind === "ask") return;
|
|
2722
3011
|
if (screen.kind === "recordSetup") {
|
|
2723
3012
|
if (input === "q" || key.leftArrow) back();
|
|
2724
3013
|
return;
|
|
@@ -2827,6 +3116,8 @@ function AppShell({
|
|
|
2827
3116
|
const links = rec ? resolveRecordingLinks(rec.recordingId, rec.origin) : {};
|
|
2828
3117
|
if (input === "T" && rec) void retranscribeExistingRecording(rec.recordingId);
|
|
2829
3118
|
else if ((input === "s" || input === "S") && rec) void resummarizeExistingRecording(rec.recordingId);
|
|
3119
|
+
else if (input === "a" && rec && askRecording)
|
|
3120
|
+
setStack((st) => [...st, { kind: "ask", recordingId: rec.recordingId }]);
|
|
2830
3121
|
else if (input === "e" && rec) void exportRecordingForAgent(rec.recordingId);
|
|
2831
3122
|
else if (input === "t" && rec?.activeTranscriptId) void openTranscript(rec.activeTranscriptId);
|
|
2832
3123
|
else if (input === "o" && rec) void runAudio(rec.recordingId, "open");
|
|
@@ -2842,19 +3133,34 @@ function AppShell({
|
|
|
2842
3133
|
return;
|
|
2843
3134
|
}
|
|
2844
3135
|
});
|
|
3136
|
+
if (screen.kind === "ask" && askRecording) {
|
|
3137
|
+
const rec = recordings.find((r) => r.recordingId === screen.recordingId);
|
|
3138
|
+
const activeTranscriptId = rec?.activeTranscriptId;
|
|
3139
|
+
return /* @__PURE__ */ jsx20(
|
|
3140
|
+
AskScreen,
|
|
3141
|
+
{
|
|
3142
|
+
recordingId: screen.recordingId,
|
|
3143
|
+
title: rec ? recordingTitle2(rec) : void 0,
|
|
3144
|
+
askRecording,
|
|
3145
|
+
spinnerFrame,
|
|
3146
|
+
onBack: back,
|
|
3147
|
+
onOpenTranscript: activeTranscriptId ? () => void openTranscript(activeTranscriptId) : void 0
|
|
3148
|
+
}
|
|
3149
|
+
);
|
|
3150
|
+
}
|
|
2845
3151
|
if (screen.kind === "transcript") {
|
|
2846
|
-
return /* @__PURE__ */
|
|
3152
|
+
return /* @__PURE__ */ jsx20(TranscriptView, { loading: screen.loading, data: screen.data, error: screen.error });
|
|
2847
3153
|
}
|
|
2848
3154
|
if (screen.kind === "jobDetail") {
|
|
2849
3155
|
const job = jobs.find((j) => j.jobId === screen.jobId);
|
|
2850
|
-
if (!job) return !loaded ? /* @__PURE__ */
|
|
2851
|
-
return /* @__PURE__ */
|
|
3156
|
+
if (!job) return !loaded ? /* @__PURE__ */ jsx20(Loading, { label: "job" }) : /* @__PURE__ */ jsx20(Missing, { label: "Job" });
|
|
3157
|
+
return /* @__PURE__ */ jsx20(Detail, { notice, children: /* @__PURE__ */ jsx20(JobDetailView, { item: job, origin, spinnerFrame, nowMs: now() }) });
|
|
2852
3158
|
}
|
|
2853
3159
|
if (screen.kind === "recordingDetail") {
|
|
2854
3160
|
const rec = recordings.find((r) => r.recordingId === screen.recordingId);
|
|
2855
|
-
if (!rec) return !loaded ? /* @__PURE__ */
|
|
3161
|
+
if (!rec) return !loaded ? /* @__PURE__ */ jsx20(Loading, { label: "recording" }) : /* @__PURE__ */ jsx20(Missing, { label: "Recording" });
|
|
2856
3162
|
const detailTranscript = rec.activeTranscriptId ? transcriptCache.get(rec.activeTranscriptId) : void 0;
|
|
2857
|
-
return /* @__PURE__ */
|
|
3163
|
+
return /* @__PURE__ */ jsx20(Detail, { notice, children: /* @__PURE__ */ jsx20(
|
|
2858
3164
|
RecordingDetailView,
|
|
2859
3165
|
{
|
|
2860
3166
|
item: rec,
|
|
@@ -2865,7 +3171,7 @@ function AppShell({
|
|
|
2865
3171
|
) });
|
|
2866
3172
|
}
|
|
2867
3173
|
if (screen.kind === "recordSetup") {
|
|
2868
|
-
return /* @__PURE__ */
|
|
3174
|
+
return /* @__PURE__ */ jsx20(Box18, { flexDirection: "column", height: size.rows, paddingX: 1, children: /* @__PURE__ */ jsx20(
|
|
2869
3175
|
RecordSetupView,
|
|
2870
3176
|
{
|
|
2871
3177
|
model: recordSetupModel,
|
|
@@ -2878,10 +3184,10 @@ function AppShell({
|
|
|
2878
3184
|
}
|
|
2879
3185
|
if (screen.kind === "record") {
|
|
2880
3186
|
if (liveRecord?.kind === "live" && liveRecord.session.mode === "live_captions") {
|
|
2881
|
-
return /* @__PURE__ */
|
|
3187
|
+
return /* @__PURE__ */ jsx20(LiveCaptionsScreen, { source: liveRecord.session.source, now });
|
|
2882
3188
|
}
|
|
2883
3189
|
if (liveRecord?.kind === "live" || liveRecord?.kind === "starting" || liveRecord?.kind === "stopping" || liveRecord?.kind === "stopped") {
|
|
2884
|
-
return /* @__PURE__ */
|
|
3190
|
+
return /* @__PURE__ */ jsx20(Detail, { notice, children: /* @__PURE__ */ jsx20(
|
|
2885
3191
|
RecordFrame,
|
|
2886
3192
|
{
|
|
2887
3193
|
telemetry: liveRecord.telemetry,
|
|
@@ -2902,18 +3208,18 @@ function AppShell({
|
|
|
2902
3208
|
}
|
|
2903
3209
|
) });
|
|
2904
3210
|
}
|
|
2905
|
-
return /* @__PURE__ */
|
|
2906
|
-
/* @__PURE__ */
|
|
3211
|
+
return /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", height: size.rows, paddingX: 1, children: [
|
|
3212
|
+
/* @__PURE__ */ jsx20(Box18, { flexGrow: 1, flexDirection: "column", paddingX: 1, paddingTop: 1, children: liveRecord?.kind === "error" ? (() => {
|
|
2907
3213
|
if (liveRecord.code === "record.permission_required") {
|
|
2908
|
-
return /* @__PURE__ */
|
|
3214
|
+
return /* @__PURE__ */ jsx20(PermissionPreflightView, { items: permissionItemsFromRecordError(liveRecord.data) });
|
|
2909
3215
|
}
|
|
2910
3216
|
const copy = recordErrorCopy(liveRecord.code, liveRecord.message);
|
|
2911
|
-
return /* @__PURE__ */
|
|
2912
|
-
/* @__PURE__ */
|
|
2913
|
-
copy.detail ? /* @__PURE__ */
|
|
3217
|
+
return /* @__PURE__ */ jsxs17(Fragment6, { children: [
|
|
3218
|
+
/* @__PURE__ */ jsx20(Text18, { color: copy.tone, children: copy.title }),
|
|
3219
|
+
copy.detail ? /* @__PURE__ */ jsx20(Text18, { dimColor: true, children: copy.detail }) : null
|
|
2914
3220
|
] });
|
|
2915
|
-
})() : /* @__PURE__ */
|
|
2916
|
-
/* @__PURE__ */
|
|
3221
|
+
})() : /* @__PURE__ */ jsx20(Text18, { dimColor: true, children: "Starting recording\u2026" }) }),
|
|
3222
|
+
/* @__PURE__ */ jsx20(Footer, { keys: "r retry \xB7 o settings \xB7 q / esc / \u2190 back" })
|
|
2917
3223
|
] });
|
|
2918
3224
|
}
|
|
2919
3225
|
const tab = screen.kind === "jobs" ? "jobs" : screen.kind === "account" ? "account" : "overview";
|
|
@@ -2922,7 +3228,7 @@ function AppShell({
|
|
|
2922
3228
|
if (!loaded) {
|
|
2923
3229
|
position = "";
|
|
2924
3230
|
const spin = "\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F"[spinnerFrame % 10];
|
|
2925
|
-
body = /* @__PURE__ */
|
|
3231
|
+
body = /* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsxs17(Text18, { color: "cyan", children: [
|
|
2926
3232
|
spin,
|
|
2927
3233
|
" Loading\u2026"
|
|
2928
3234
|
] }) });
|
|
@@ -2938,7 +3244,7 @@ function AppShell({
|
|
|
2938
3244
|
const showPeek = size.columns >= 100;
|
|
2939
3245
|
const peekWidth = showPeek ? 34 : 0;
|
|
2940
3246
|
const listColumns = showPeek ? Math.max(30, size.columns - peekWidth - 3) : size.columns;
|
|
2941
|
-
body = /* @__PURE__ */
|
|
3247
|
+
body = /* @__PURE__ */ jsx20(
|
|
2942
3248
|
OverviewView,
|
|
2943
3249
|
{
|
|
2944
3250
|
recordings: recordings.slice(win.start, win.end),
|
|
@@ -2959,11 +3265,11 @@ function AppShell({
|
|
|
2959
3265
|
);
|
|
2960
3266
|
} else if (screen.kind === "account") {
|
|
2961
3267
|
position = "";
|
|
2962
|
-
body = /* @__PURE__ */
|
|
3268
|
+
body = /* @__PURE__ */ jsx20(AccountView, { status: accountStatus, nowMs: now() });
|
|
2963
3269
|
} else {
|
|
2964
3270
|
const win = listWindow(selected, jobs.length, Math.max(3, size.rows - 4));
|
|
2965
3271
|
position = jobs.length ? `${selected + 1} / ${jobs.length}` : "0";
|
|
2966
|
-
body = /* @__PURE__ */
|
|
3272
|
+
body = /* @__PURE__ */ jsx20(
|
|
2967
3273
|
JobsView,
|
|
2968
3274
|
{
|
|
2969
3275
|
items: jobs.slice(win.start, win.end),
|
|
@@ -2974,38 +3280,38 @@ function AppShell({
|
|
|
2974
3280
|
);
|
|
2975
3281
|
}
|
|
2976
3282
|
const footerKeys = screen.kind === "jobs" ? `${position} \xB7 \u2191\u2193 select \xB7 \u23CE job \xB7 t transcript \xB7 n record \xB7 1 overview \xB7 3 account \xB7 r refresh \xB7 q quit` : screen.kind === "account" ? "Account \xB7 n record \xB7 1 overview \xB7 2 jobs \xB7 r refresh \xB7 q quit" : `${position} \xB7 \u2191\u2193 scroll \xB7 \u23CE open \xB7 t transcript \xB7 n record \xB7 2 jobs \xB7 3 account \xB7 r refresh \xB7 q quit`;
|
|
2977
|
-
return /* @__PURE__ */
|
|
2978
|
-
/* @__PURE__ */
|
|
2979
|
-
/* @__PURE__ */
|
|
3283
|
+
return /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", height: size.rows, paddingX: 1, children: [
|
|
3284
|
+
/* @__PURE__ */ jsx20(Header, { active: tab }),
|
|
3285
|
+
/* @__PURE__ */ jsxs17(Box18, { flexGrow: 1, flexDirection: "column", children: [
|
|
2980
3286
|
body,
|
|
2981
|
-
loadError && jobs.length === 0 && recordings.length === 0 ? /* @__PURE__ */
|
|
3287
|
+
loadError && jobs.length === 0 && recordings.length === 0 ? /* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsxs17(Text18, { color: "red", children: [
|
|
2982
3288
|
"! ",
|
|
2983
3289
|
loadError
|
|
2984
3290
|
] }) }) : null
|
|
2985
3291
|
] }),
|
|
2986
|
-
/* @__PURE__ */
|
|
3292
|
+
/* @__PURE__ */ jsx20(Footer, { keys: footerKeys })
|
|
2987
3293
|
] });
|
|
2988
3294
|
}
|
|
2989
3295
|
function Detail({
|
|
2990
3296
|
notice,
|
|
2991
3297
|
children
|
|
2992
3298
|
}) {
|
|
2993
|
-
return /* @__PURE__ */
|
|
3299
|
+
return /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", children: [
|
|
2994
3300
|
children,
|
|
2995
|
-
notice ? /* @__PURE__ */
|
|
3301
|
+
notice ? /* @__PURE__ */ jsx20(Box18, { paddingX: 1, children: /* @__PURE__ */ jsx20(Text18, { color: "green", children: notice }) }) : null
|
|
2996
3302
|
] });
|
|
2997
3303
|
}
|
|
2998
3304
|
function Missing({ label }) {
|
|
2999
|
-
return /* @__PURE__ */
|
|
3000
|
-
/* @__PURE__ */
|
|
3305
|
+
return /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", paddingX: 1, children: [
|
|
3306
|
+
/* @__PURE__ */ jsxs17(Text18, { dimColor: true, children: [
|
|
3001
3307
|
label,
|
|
3002
3308
|
" no longer in the list."
|
|
3003
3309
|
] }),
|
|
3004
|
-
/* @__PURE__ */
|
|
3310
|
+
/* @__PURE__ */ jsx20(Text18, { dimColor: true, children: "esc back \xB7 q quit" })
|
|
3005
3311
|
] });
|
|
3006
3312
|
}
|
|
3007
3313
|
function Loading({ label }) {
|
|
3008
|
-
return /* @__PURE__ */
|
|
3314
|
+
return /* @__PURE__ */ jsx20(Box18, { paddingX: 1, children: /* @__PURE__ */ jsxs17(Text18, { color: "cyan", children: [
|
|
3009
3315
|
"Loading ",
|
|
3010
3316
|
label,
|
|
3011
3317
|
"\u2026"
|
|
@@ -3021,6 +3327,7 @@ var init_AppShell = __esm({
|
|
|
3021
3327
|
init_OverviewView();
|
|
3022
3328
|
init_JobDetailView();
|
|
3023
3329
|
init_RecordingDetailView();
|
|
3330
|
+
init_AskScreen();
|
|
3024
3331
|
init_TranscriptView();
|
|
3025
3332
|
init_LiveCaptionsScreen();
|
|
3026
3333
|
init_PermissionPreflightView();
|
|
@@ -3047,7 +3354,7 @@ __export(tui_exports, {
|
|
|
3047
3354
|
runDashboard: () => runDashboard,
|
|
3048
3355
|
useTerminalSize: () => useTerminalSize
|
|
3049
3356
|
});
|
|
3050
|
-
import
|
|
3357
|
+
import React12 from "react";
|
|
3051
3358
|
import { render as render2 } from "ink";
|
|
3052
3359
|
import { spawn as spawn3 } from "child_process";
|
|
3053
3360
|
function openUrl(url2) {
|
|
@@ -3068,7 +3375,7 @@ function copyText(text) {
|
|
|
3068
3375
|
async function runDashboard(deps) {
|
|
3069
3376
|
const renderApp = deps.renderApp ?? render2;
|
|
3070
3377
|
const app = renderApp(
|
|
3071
|
-
|
|
3378
|
+
React12.createElement(AppShell, {
|
|
3072
3379
|
fetchJobs: deps.fetchJobs,
|
|
3073
3380
|
fetchTranscript: deps.fetchTranscript,
|
|
3074
3381
|
fetchRecordings: deps.fetchRecordings,
|
|
@@ -3086,6 +3393,9 @@ async function runDashboard(deps) {
|
|
|
3086
3393
|
onSyncRecordingText: deps.syncRecordingText,
|
|
3087
3394
|
onSyncRecordingAudio: deps.syncRecordingAudio,
|
|
3088
3395
|
onExportRecording: deps.exportRecording,
|
|
3396
|
+
fetchAskThread: deps.fetchAskThread,
|
|
3397
|
+
fetchAskSuggestions: deps.fetchAskSuggestions,
|
|
3398
|
+
askRecording: deps.askRecording,
|
|
3089
3399
|
initialView: deps.initialView ?? "overview",
|
|
3090
3400
|
openUrl,
|
|
3091
3401
|
copyText
|
|
@@ -18925,6 +19235,30 @@ function requireAccountPartition(input) {
|
|
|
18925
19235
|
function openCliStore(opts = {}) {
|
|
18926
19236
|
return new CliLocalStore(opts);
|
|
18927
19237
|
}
|
|
19238
|
+
function isLocalStoreUnavailableError(error51) {
|
|
19239
|
+
return typeof error51 === "object" && error51 !== null && error51.localStoreUnavailable === true;
|
|
19240
|
+
}
|
|
19241
|
+
var warnedLocalStoreUnavailable = false;
|
|
19242
|
+
function tryOpenCliStore(opts = {}) {
|
|
19243
|
+
const { onUnavailable, open = openCliStore, ...storeOpts } = opts;
|
|
19244
|
+
try {
|
|
19245
|
+
return open(storeOpts);
|
|
19246
|
+
} catch (error51) {
|
|
19247
|
+
if (isLocalStoreUnavailableError(error51)) {
|
|
19248
|
+
if (!warnedLocalStoreUnavailable) {
|
|
19249
|
+
warnedLocalStoreUnavailable = true;
|
|
19250
|
+
(onUnavailable ?? defaultWarnLocalStoreUnavailable)();
|
|
19251
|
+
}
|
|
19252
|
+
return null;
|
|
19253
|
+
}
|
|
19254
|
+
throw error51;
|
|
19255
|
+
}
|
|
19256
|
+
}
|
|
19257
|
+
function defaultWarnLocalStoreUnavailable() {
|
|
19258
|
+
process.stderr.write(
|
|
19259
|
+
"Local cache unavailable (needs Node.js 22+ for node:sqlite). Cloud commands work normally.\n"
|
|
19260
|
+
);
|
|
19261
|
+
}
|
|
18928
19262
|
var CliLocalStore = class {
|
|
18929
19263
|
db;
|
|
18930
19264
|
now;
|
|
@@ -19220,9 +19554,11 @@ function suppressNodeSqliteWarning(run) {
|
|
|
19220
19554
|
return run();
|
|
19221
19555
|
} catch (error51) {
|
|
19222
19556
|
if (isMissingNodeSqlite(error51)) {
|
|
19223
|
-
|
|
19557
|
+
const err = cliError("internal.unexpected", "Local SQLite store requires Node.js 22 or newer.", {
|
|
19224
19558
|
hint: "Upgrade Node.js, then retry. Cloud-only commands that do not touch local state can still run."
|
|
19225
19559
|
});
|
|
19560
|
+
err.localStoreUnavailable = true;
|
|
19561
|
+
throw err;
|
|
19226
19562
|
}
|
|
19227
19563
|
throw error51;
|
|
19228
19564
|
} finally {
|
|
@@ -19502,6 +19838,62 @@ var RecappiApiClient = class {
|
|
|
19502
19838
|
summaryStatus: parsed.summaryStatus
|
|
19503
19839
|
});
|
|
19504
19840
|
}
|
|
19841
|
+
async fetchAskThread(recordingId) {
|
|
19842
|
+
const parsed = await this.getJson(
|
|
19843
|
+
`/api/recordings/${encodeURIComponent(recordingId)}/ask-thread`
|
|
19844
|
+
);
|
|
19845
|
+
const thread = isRecord2(parsed.thread) ? {
|
|
19846
|
+
id: stringValue2(parsed.thread.id) ?? "",
|
|
19847
|
+
recordingId: stringValue2(parsed.thread.recordingId) ?? recordingId,
|
|
19848
|
+
...numberValue2(parsed.thread.createdAt) !== void 0 ? { createdAt: numberValue2(parsed.thread.createdAt) } : {},
|
|
19849
|
+
...numberValue2(parsed.thread.updatedAt) !== void 0 ? { updatedAt: numberValue2(parsed.thread.updatedAt) } : {}
|
|
19850
|
+
} : null;
|
|
19851
|
+
return {
|
|
19852
|
+
thread: thread && thread.id ? thread : null,
|
|
19853
|
+
messages: Array.isArray(parsed.messages) ? parsed.messages.filter(isRecord2).map(mapAskThreadMessage) : [],
|
|
19854
|
+
origin: this.auth.origin
|
|
19855
|
+
};
|
|
19856
|
+
}
|
|
19857
|
+
async fetchAskSuggestions(recordingId, opts = {}) {
|
|
19858
|
+
const params = new URLSearchParams();
|
|
19859
|
+
if (opts.language) params.set("language", opts.language);
|
|
19860
|
+
const suffix = params.size > 0 ? `?${params}` : "";
|
|
19861
|
+
const parsed = await this.getJson(
|
|
19862
|
+
`/api/recordings/${encodeURIComponent(recordingId)}/ask-suggestions${suffix}`
|
|
19863
|
+
);
|
|
19864
|
+
return {
|
|
19865
|
+
suggestions: Array.isArray(parsed.suggestions) ? parsed.suggestions.filter(isRecord2).flatMap(mapAskSuggestion) : [],
|
|
19866
|
+
...typeof parsed.transcriptId === "string" ? { transcriptId: parsed.transcriptId } : {},
|
|
19867
|
+
...typeof parsed.model === "string" ? { model: parsed.model } : {},
|
|
19868
|
+
...typeof parsed.language === "string" ? { language: parsed.language } : {},
|
|
19869
|
+
...typeof parsed.cached === "boolean" ? { cached: parsed.cached } : {},
|
|
19870
|
+
origin: this.auth.origin
|
|
19871
|
+
};
|
|
19872
|
+
}
|
|
19873
|
+
async *askRecordingStream(opts) {
|
|
19874
|
+
const response = await this.request(
|
|
19875
|
+
"POST",
|
|
19876
|
+
`/api/recordings/${encodeURIComponent(opts.recordingId)}/ask-thread/messages`,
|
|
19877
|
+
JSON.stringify({
|
|
19878
|
+
question: opts.question,
|
|
19879
|
+
webSearch: opts.webSearch === true,
|
|
19880
|
+
...opts.model ? { model: opts.model } : {}
|
|
19881
|
+
}),
|
|
19882
|
+
{
|
|
19883
|
+
headers: {
|
|
19884
|
+
accept: "text/event-stream",
|
|
19885
|
+
"content-type": "application/json"
|
|
19886
|
+
}
|
|
19887
|
+
}
|
|
19888
|
+
);
|
|
19889
|
+
if (!response.body) {
|
|
19890
|
+
throw cliError("cloud.invalid_response", "Ask stream response was empty.");
|
|
19891
|
+
}
|
|
19892
|
+
for await (const frame of parseSseFrames(response.body)) {
|
|
19893
|
+
const event = decodeAskStreamEvent(frame.event, frame.data);
|
|
19894
|
+
if (event) yield event;
|
|
19895
|
+
}
|
|
19896
|
+
}
|
|
19505
19897
|
async downloadRecordingAudio(recordingId, opts = {}) {
|
|
19506
19898
|
const response = await this.request(
|
|
19507
19899
|
"GET",
|
|
@@ -19549,21 +19941,23 @@ var RecappiApiClient = class {
|
|
|
19549
19941
|
let accountScopedArtifacts = 0;
|
|
19550
19942
|
let unattributedArtifacts = 0;
|
|
19551
19943
|
if (status.loggedIn && status.userId) {
|
|
19552
|
-
const store =
|
|
19944
|
+
const store = tryOpenCliStore({
|
|
19553
19945
|
dbPath: storePath,
|
|
19554
19946
|
env: this.env,
|
|
19555
19947
|
homeDir: this.homeDir
|
|
19556
19948
|
});
|
|
19557
|
-
|
|
19558
|
-
|
|
19559
|
-
|
|
19560
|
-
|
|
19561
|
-
|
|
19562
|
-
|
|
19563
|
-
|
|
19564
|
-
|
|
19565
|
-
|
|
19566
|
-
|
|
19949
|
+
if (store) {
|
|
19950
|
+
try {
|
|
19951
|
+
const account = requireAccountPartition({
|
|
19952
|
+
backendOrigin: this.auth.origin,
|
|
19953
|
+
userId: status.userId
|
|
19954
|
+
});
|
|
19955
|
+
store.recordAccountSeen(account, status.email);
|
|
19956
|
+
accountScopedArtifacts = store.listLocalArtifactsForAccount(account).length;
|
|
19957
|
+
unattributedArtifacts = store.listUnattributedLocalArtifacts().length;
|
|
19958
|
+
} finally {
|
|
19959
|
+
store.close();
|
|
19960
|
+
}
|
|
19567
19961
|
}
|
|
19568
19962
|
}
|
|
19569
19963
|
const billing = status.loggedIn ? await this.billingStatus() : void 0;
|
|
@@ -20154,6 +20548,139 @@ function stringValue2(value) {
|
|
|
20154
20548
|
function numberValue2(value) {
|
|
20155
20549
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
20156
20550
|
}
|
|
20551
|
+
function mapAskThreadMessage(row) {
|
|
20552
|
+
const id = stringValue2(row.id) ?? "";
|
|
20553
|
+
const role = row.role === "user" ? "user" : "assistant";
|
|
20554
|
+
return {
|
|
20555
|
+
id,
|
|
20556
|
+
role,
|
|
20557
|
+
...typeof row.status === "string" ? { status: row.status } : {},
|
|
20558
|
+
...numberValue2(row.sequence) !== void 0 ? { sequence: numberValue2(row.sequence) } : {},
|
|
20559
|
+
content: stringValue2(row.content) ?? "",
|
|
20560
|
+
...typeof row.model === "string" || row.model === null ? { model: row.model } : {},
|
|
20561
|
+
...typeof row.webSearch === "boolean" ? { webSearch: row.webSearch } : {},
|
|
20562
|
+
...typeof row.errorMessage === "string" || row.errorMessage === null ? { errorMessage: row.errorMessage } : {},
|
|
20563
|
+
...numberValue2(row.createdAt) !== void 0 ? { createdAt: numberValue2(row.createdAt) } : {},
|
|
20564
|
+
...numberValue2(row.updatedAt) !== void 0 ? { updatedAt: numberValue2(row.updatedAt) } : {},
|
|
20565
|
+
citations: Array.isArray(row.citations) ? row.citations.filter(isRecord2).map(mapAskCitation) : []
|
|
20566
|
+
};
|
|
20567
|
+
}
|
|
20568
|
+
function mapAskSuggestion(row) {
|
|
20569
|
+
const question = stringValue2(row.question)?.trim();
|
|
20570
|
+
if (!question) return [];
|
|
20571
|
+
return [
|
|
20572
|
+
{
|
|
20573
|
+
...typeof row.id === "string" ? { id: row.id } : {},
|
|
20574
|
+
question,
|
|
20575
|
+
...typeof row.reason === "string" && row.reason.trim() ? { reason: row.reason } : {}
|
|
20576
|
+
}
|
|
20577
|
+
];
|
|
20578
|
+
}
|
|
20579
|
+
function mapAskCitation(row) {
|
|
20580
|
+
return {
|
|
20581
|
+
...typeof row.id === "string" ? { id: row.id } : {},
|
|
20582
|
+
...typeof row.segmentId === "string" ? { segmentId: row.segmentId } : {},
|
|
20583
|
+
...numberValue2(row.index) !== void 0 ? { index: numberValue2(row.index) } : {},
|
|
20584
|
+
...typeof row.startMs === "number" || row.startMs === null ? { startMs: row.startMs } : {},
|
|
20585
|
+
...typeof row.endMs === "number" || row.endMs === null ? { endMs: row.endMs } : {},
|
|
20586
|
+
...typeof row.label === "string" ? { label: row.label } : {},
|
|
20587
|
+
...typeof row.speaker === "string" ? { speaker: row.speaker } : {},
|
|
20588
|
+
...typeof row.snippet === "string" ? { snippet: row.snippet } : {},
|
|
20589
|
+
...numberValue2(row.order) !== void 0 ? { order: numberValue2(row.order) } : {}
|
|
20590
|
+
};
|
|
20591
|
+
}
|
|
20592
|
+
async function* parseSseFrames(stream) {
|
|
20593
|
+
const reader = stream.getReader();
|
|
20594
|
+
const decoder = new TextDecoder();
|
|
20595
|
+
let buffer = "";
|
|
20596
|
+
try {
|
|
20597
|
+
for (; ; ) {
|
|
20598
|
+
const { value, done } = await reader.read();
|
|
20599
|
+
if (done) break;
|
|
20600
|
+
buffer += decoder.decode(value, { stream: true });
|
|
20601
|
+
let delimiter = findSseDelimiter(buffer);
|
|
20602
|
+
while (delimiter) {
|
|
20603
|
+
const frameText = buffer.slice(0, delimiter.index);
|
|
20604
|
+
buffer = buffer.slice(delimiter.index + delimiter.length);
|
|
20605
|
+
const frame = parseSseFrame(frameText);
|
|
20606
|
+
if (frame) yield frame;
|
|
20607
|
+
delimiter = findSseDelimiter(buffer);
|
|
20608
|
+
}
|
|
20609
|
+
}
|
|
20610
|
+
buffer += decoder.decode();
|
|
20611
|
+
if (buffer.trim()) {
|
|
20612
|
+
const frame = parseSseFrame(buffer);
|
|
20613
|
+
if (frame) yield frame;
|
|
20614
|
+
}
|
|
20615
|
+
} finally {
|
|
20616
|
+
reader.releaseLock();
|
|
20617
|
+
}
|
|
20618
|
+
}
|
|
20619
|
+
function findSseDelimiter(buffer) {
|
|
20620
|
+
const lf = buffer.indexOf("\n\n");
|
|
20621
|
+
const crlf = buffer.indexOf("\r\n\r\n");
|
|
20622
|
+
if (lf === -1 && crlf === -1) return void 0;
|
|
20623
|
+
if (lf === -1) return { index: crlf, length: 4 };
|
|
20624
|
+
if (crlf === -1) return { index: lf, length: 2 };
|
|
20625
|
+
return crlf < lf ? { index: crlf, length: 4 } : { index: lf, length: 2 };
|
|
20626
|
+
}
|
|
20627
|
+
function parseSseFrame(frameText) {
|
|
20628
|
+
let event = "message";
|
|
20629
|
+
const data = [];
|
|
20630
|
+
for (const rawLine of frameText.split(/\r?\n/)) {
|
|
20631
|
+
if (!rawLine || rawLine.startsWith(":")) continue;
|
|
20632
|
+
if (rawLine.startsWith("event:")) {
|
|
20633
|
+
event = rawLine.slice("event:".length).trim() || "message";
|
|
20634
|
+
} else if (rawLine.startsWith("data:")) {
|
|
20635
|
+
const value = rawLine.slice("data:".length);
|
|
20636
|
+
data.push(value.startsWith(" ") ? value.slice(1) : value);
|
|
20637
|
+
}
|
|
20638
|
+
}
|
|
20639
|
+
if (data.length === 0) return void 0;
|
|
20640
|
+
return { event, data: data.join("\n") };
|
|
20641
|
+
}
|
|
20642
|
+
function decodeAskStreamEvent(eventName, data) {
|
|
20643
|
+
const parsed = JSON.parse(data);
|
|
20644
|
+
if (!isRecord2(parsed)) return void 0;
|
|
20645
|
+
const type = stringValue2(parsed.type) ?? eventName;
|
|
20646
|
+
switch (eventName === "message" ? type : eventName) {
|
|
20647
|
+
case "metadata":
|
|
20648
|
+
return {
|
|
20649
|
+
type: "metadata",
|
|
20650
|
+
...typeof parsed.recordingId === "string" ? { recordingId: parsed.recordingId } : {},
|
|
20651
|
+
...typeof parsed.transcriptId === "string" ? { transcriptId: parsed.transcriptId } : {},
|
|
20652
|
+
...typeof parsed.threadId === "string" ? { threadId: parsed.threadId } : {},
|
|
20653
|
+
...typeof parsed.userMessageId === "string" ? { userMessageId: parsed.userMessageId } : {},
|
|
20654
|
+
...typeof parsed.assistantMessageId === "string" ? { assistantMessageId: parsed.assistantMessageId } : {},
|
|
20655
|
+
...typeof parsed.model === "string" ? { model: parsed.model } : {},
|
|
20656
|
+
...typeof parsed.webSearch === "boolean" ? { webSearch: parsed.webSearch } : {},
|
|
20657
|
+
...numberValue2(parsed.segmentCount) !== void 0 ? { segmentCount: numberValue2(parsed.segmentCount) } : {},
|
|
20658
|
+
...typeof parsed.citationMarker === "string" ? { citationMarker: parsed.citationMarker } : {}
|
|
20659
|
+
};
|
|
20660
|
+
case "answer_delta":
|
|
20661
|
+
return { type: "answer_delta", delta: stringValue2(parsed.delta) ?? "" };
|
|
20662
|
+
case "citation": {
|
|
20663
|
+
const citation = isRecord2(parsed.citation) ? parsed.citation : parsed;
|
|
20664
|
+
return { type: "citation", citation: mapAskCitation(citation) };
|
|
20665
|
+
}
|
|
20666
|
+
case "done":
|
|
20667
|
+
return {
|
|
20668
|
+
type: "done",
|
|
20669
|
+
...typeof parsed.threadId === "string" ? { threadId: parsed.threadId } : {},
|
|
20670
|
+
...typeof parsed.userMessageId === "string" ? { userMessageId: parsed.userMessageId } : {},
|
|
20671
|
+
...typeof parsed.assistantMessageId === "string" ? { assistantMessageId: parsed.assistantMessageId } : {},
|
|
20672
|
+
...typeof parsed.content === "string" ? { content: parsed.content } : {},
|
|
20673
|
+
citations: Array.isArray(parsed.citations) ? parsed.citations.filter(isRecord2).map(mapAskCitation) : []
|
|
20674
|
+
};
|
|
20675
|
+
case "error":
|
|
20676
|
+
throw cliError(
|
|
20677
|
+
"cloud.http_error",
|
|
20678
|
+
stringValue2(parsed.message) ?? stringValue2(parsed.error) ?? "The assistant ran into an error."
|
|
20679
|
+
);
|
|
20680
|
+
default:
|
|
20681
|
+
return void 0;
|
|
20682
|
+
}
|
|
20683
|
+
}
|
|
20157
20684
|
function recordingCloudUrl(origin, recordingId) {
|
|
20158
20685
|
return `${origin.replace(/\/+$/, "")}/recordings/${encodeURIComponent(recordingId)}`;
|
|
20159
20686
|
}
|
|
@@ -20292,7 +20819,7 @@ async function findReusableDownload(recordingId, deps) {
|
|
|
20292
20819
|
if (!artifact || !await isReadableFile(artifact.localPath)) return null;
|
|
20293
20820
|
const opened = store.markLocalArtifactOpened(artifact.id);
|
|
20294
20821
|
return artifactToDownload(opened, recordingId);
|
|
20295
|
-
});
|
|
20822
|
+
}, null);
|
|
20296
20823
|
}
|
|
20297
20824
|
async function rememberDownload(download, deps) {
|
|
20298
20825
|
return withStore(deps, (store, account) => {
|
|
@@ -20310,12 +20837,13 @@ async function rememberDownload(download, deps) {
|
|
|
20310
20837
|
}
|
|
20311
20838
|
});
|
|
20312
20839
|
return store.markLocalArtifactOpened(artifact.id);
|
|
20313
|
-
});
|
|
20840
|
+
}, null);
|
|
20314
20841
|
}
|
|
20315
20842
|
async function listExistingDownloads(deps) {
|
|
20316
20843
|
const artifacts = await withStore(
|
|
20317
20844
|
deps,
|
|
20318
|
-
(store, account) => account ? store.listLocalArtifactsForAccount(account, { kind: "download" }) : []
|
|
20845
|
+
(store, account) => account ? store.listLocalArtifactsForAccount(account, { kind: "download" }) : [],
|
|
20846
|
+
[]
|
|
20319
20847
|
);
|
|
20320
20848
|
const existing = [];
|
|
20321
20849
|
for (const artifact of artifacts) {
|
|
@@ -20323,8 +20851,9 @@ async function listExistingDownloads(deps) {
|
|
|
20323
20851
|
}
|
|
20324
20852
|
return existing;
|
|
20325
20853
|
}
|
|
20326
|
-
async function withStore(deps, run) {
|
|
20327
|
-
const store = deps.store ??
|
|
20854
|
+
async function withStore(deps, run, fallback) {
|
|
20855
|
+
const store = deps.store ?? tryOpenCliStore({ homeDir: deps.homeDir, env: deps.env });
|
|
20856
|
+
if (!store) return fallback;
|
|
20328
20857
|
try {
|
|
20329
20858
|
return await run(store, deps.account ?? null);
|
|
20330
20859
|
} finally {
|
|
@@ -20427,6 +20956,7 @@ var COMMON_TASKS = [
|
|
|
20427
20956
|
{ label: "Export recording bundle", command: "recappi recordings export <recordingId> --dir ./bundle" },
|
|
20428
20957
|
{ label: "List / find recordings", command: "recappi recordings list" },
|
|
20429
20958
|
{ label: "Read a transcript", command: "recappi transcript get <transcriptId>" },
|
|
20959
|
+
{ label: "Ask about a recording", command: 'recappi ask <recordingId> "<question>"' },
|
|
20430
20960
|
{ label: "Download / open audio", command: "recappi audio <recordingId> --open" },
|
|
20431
20961
|
{ label: "Check a transcription job", command: "recappi jobs wait <jobId>" },
|
|
20432
20962
|
{ label: "Account \xB7 quota \xB7 usage", command: "recappi account status" },
|
|
@@ -20605,6 +21135,23 @@ var COMMAND_METADATA = {
|
|
|
20605
21135
|
{ description: "Wait for a transcription job", command: "recappi jobs wait <jobId>" }
|
|
20606
21136
|
],
|
|
20607
21137
|
relatedCommands: ["jobs list", "transcript get"]
|
|
21138
|
+
},
|
|
21139
|
+
ask: {
|
|
21140
|
+
capabilities: [
|
|
21141
|
+
"Ask a question about a recording and stream the answer",
|
|
21142
|
+
"Answers cite the transcript with inline \u27E8mm:ss\u27E9 references"
|
|
21143
|
+
],
|
|
21144
|
+
examples: [
|
|
21145
|
+
{
|
|
21146
|
+
description: "Ask about a recording",
|
|
21147
|
+
command: 'recappi ask <recordingId> "What were the decisions?"'
|
|
21148
|
+
},
|
|
21149
|
+
{
|
|
21150
|
+
description: "Get the raw answer + citations for an agent",
|
|
21151
|
+
command: 'recappi ask <recordingId> "Summarize the risks" --json'
|
|
21152
|
+
}
|
|
21153
|
+
],
|
|
21154
|
+
relatedCommands: ["recordings get", "transcript get", "recordings list"]
|
|
20608
21155
|
}
|
|
20609
21156
|
};
|
|
20610
21157
|
function commonTasksHelpText() {
|
|
@@ -20657,7 +21204,7 @@ async function findIndexedRecordingSessionDir(recordingId, deps) {
|
|
|
20657
21204
|
const manifest = await readJsonRecord(path6.join(artifact.localPath, "remote-session.json"));
|
|
20658
21205
|
if (manifest?.recordingId && manifest.recordingId !== recordingId) return void 0;
|
|
20659
21206
|
return artifact.localPath;
|
|
20660
|
-
});
|
|
21207
|
+
}, void 0);
|
|
20661
21208
|
}
|
|
20662
21209
|
async function rememberRecordingSession(input, deps) {
|
|
20663
21210
|
return withSessionStore(deps, (store, account) => {
|
|
@@ -20680,7 +21227,7 @@ async function rememberRecordingSession(input, deps) {
|
|
|
20680
21227
|
indexedAt: (input.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
20681
21228
|
})
|
|
20682
21229
|
});
|
|
20683
|
-
});
|
|
21230
|
+
}, null);
|
|
20684
21231
|
}
|
|
20685
21232
|
async function listCachedRecordingSessions(deps) {
|
|
20686
21233
|
const limit = deps.limit ?? 50;
|
|
@@ -20698,7 +21245,7 @@ async function listCachedRecordingSessions(deps) {
|
|
|
20698
21245
|
recordings.push(recording);
|
|
20699
21246
|
}
|
|
20700
21247
|
return recordings.sort((a, b) => b.createdAt - a.createdAt || b.updatedAt - a.updatedAt);
|
|
20701
|
-
});
|
|
21248
|
+
}, []);
|
|
20702
21249
|
return recordingListDataSchema.parse({
|
|
20703
21250
|
items,
|
|
20704
21251
|
limit,
|
|
@@ -20715,8 +21262,9 @@ async function recordingFromArtifact(artifact) {
|
|
|
20715
21262
|
const parsed = recordingDataSchema.safeParse(fromFile);
|
|
20716
21263
|
return parsed.success ? parsed.data : null;
|
|
20717
21264
|
}
|
|
20718
|
-
async function withSessionStore(deps, run) {
|
|
20719
|
-
const store = deps.store ??
|
|
21265
|
+
async function withSessionStore(deps, run, fallback) {
|
|
21266
|
+
const store = deps.store ?? tryOpenCliStore({ homeDir: deps.homeDir, env: deps.env });
|
|
21267
|
+
if (!store) return fallback;
|
|
20720
21268
|
try {
|
|
20721
21269
|
return await run(store, deps.account ?? null);
|
|
20722
21270
|
} finally {
|
|
@@ -21238,6 +21786,9 @@ function formatTimestamp(ms) {
|
|
|
21238
21786
|
return hours > 0 ? `${hours}:${mm}:${ss}` : `${mm}:${ss}`;
|
|
21239
21787
|
}
|
|
21240
21788
|
|
|
21789
|
+
// src/cli.ts
|
|
21790
|
+
init_askInline();
|
|
21791
|
+
|
|
21241
21792
|
// src/progressStepper.ts
|
|
21242
21793
|
var STEP_DEFS = [
|
|
21243
21794
|
{ key: "check", label: "Check" },
|
|
@@ -23787,6 +24338,9 @@ async function runCli(deps = {}) {
|
|
|
23787
24338
|
env: deps.env,
|
|
23788
24339
|
homeDir: deps.homeDir
|
|
23789
24340
|
}),
|
|
24341
|
+
fetchAskThread: (recordingId) => client.fetchAskThread(recordingId),
|
|
24342
|
+
fetchAskSuggestions: (recordingId, options) => client.fetchAskSuggestions(recordingId, options),
|
|
24343
|
+
askRecording: (options) => client.askRecordingStream(options),
|
|
23790
24344
|
initialView: parsed.initialView
|
|
23791
24345
|
});
|
|
23792
24346
|
return 0;
|
|
@@ -24027,6 +24581,35 @@ async function runCli(deps = {}) {
|
|
|
24027
24581
|
renderSuccess("recordings resummarize", data, render3);
|
|
24028
24582
|
return 0;
|
|
24029
24583
|
}
|
|
24584
|
+
if (parsed.kind === "ask") {
|
|
24585
|
+
if (render3.mode === "human") render3.stderr("Asking\u2026\n");
|
|
24586
|
+
let content = "";
|
|
24587
|
+
let citations = [];
|
|
24588
|
+
for await (const event of client.askRecordingStream({
|
|
24589
|
+
recordingId: parsed.recordingId,
|
|
24590
|
+
question: parsed.question,
|
|
24591
|
+
...parsed.webSearch ? { webSearch: true } : {},
|
|
24592
|
+
...parsed.model ? { model: parsed.model } : {}
|
|
24593
|
+
})) {
|
|
24594
|
+
if (event.type === "answer_delta") content += event.delta;
|
|
24595
|
+
else if (event.type === "citation") citations.push(event.citation);
|
|
24596
|
+
else if (event.type === "done") {
|
|
24597
|
+
if (typeof event.content === "string" && event.content) content = event.content;
|
|
24598
|
+
if (event.citations.length) citations = event.citations;
|
|
24599
|
+
}
|
|
24600
|
+
}
|
|
24601
|
+
if (render3.mode === "human") {
|
|
24602
|
+
render3.stdout(`${formatAskAnswerPlain(content, citations)}
|
|
24603
|
+
`);
|
|
24604
|
+
} else {
|
|
24605
|
+
renderSuccess(
|
|
24606
|
+
"ask",
|
|
24607
|
+
{ recordingId: parsed.recordingId, question: parsed.question, answer: content, citations },
|
|
24608
|
+
render3
|
|
24609
|
+
);
|
|
24610
|
+
}
|
|
24611
|
+
return 0;
|
|
24612
|
+
}
|
|
24030
24613
|
if (parsed.kind === "dashboard-stats") {
|
|
24031
24614
|
const data = await client.dashboardStats();
|
|
24032
24615
|
renderSuccess("dashboard stats", data, render3);
|
|
@@ -24258,6 +24841,19 @@ Agent mode:
|
|
|
24258
24841
|
...opts.force === true ? { force: true } : {}
|
|
24259
24842
|
});
|
|
24260
24843
|
});
|
|
24844
|
+
const ask = program.command("ask <recordingId> <question>").description("Ask a question about a recording; streams an answer with inline citations").option("--web-search", "allow web search for extra context").option("--model <name>", "Ask model", parseStringOption("--model")).addHelpText("after", commandMetadataHelpText("ask"));
|
|
24845
|
+
addCommonOptions(ask);
|
|
24846
|
+
ask.action((recordingId, question, opts, command) => {
|
|
24847
|
+
onSelect({
|
|
24848
|
+
kind: "ask",
|
|
24849
|
+
options: collectGlobalOptions(command),
|
|
24850
|
+
commandName: "ask",
|
|
24851
|
+
recordingId,
|
|
24852
|
+
question,
|
|
24853
|
+
...opts.webSearch === true ? { webSearch: true } : {},
|
|
24854
|
+
...typeof opts.model === "string" ? { model: opts.model } : {}
|
|
24855
|
+
});
|
|
24856
|
+
});
|
|
24261
24857
|
const record2 = program.command("record").description("Record system/mic audio via the Recappi Mini sidecar").option("--title <title>", "recording title", parseStringOption("--title")).option("--live", "show live captions while recording").option("--no-system-audio", "record microphone only").option("--no-microphone", "record system audio only").option(
|
|
24262
24858
|
"--translation-language <lang>",
|
|
24263
24859
|
"live caption translation language",
|