recappi 0.1.18 → 0.1.20
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 +270 -13
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -20,17 +20,18 @@ function recordingCaptureMappingFromSelection(selection = DEFAULT_RECORDING_SELE
|
|
|
20
20
|
source,
|
|
21
21
|
includeSystemAudio: false,
|
|
22
22
|
includeMicrophone: true,
|
|
23
|
+
...selection.microphoneDeviceId ? { microphoneDeviceId: selection.microphoneDeviceId } : {},
|
|
23
24
|
sourceLabel: source.label,
|
|
24
25
|
micEnabled: true
|
|
25
26
|
};
|
|
26
27
|
}
|
|
28
|
+
const microphoneDeviceId = selection.includeMicrophone && selection.microphoneDeviceId ? selection.microphoneDeviceId : void 0;
|
|
27
29
|
return {
|
|
28
30
|
source,
|
|
29
|
-
// Current helper support is system-wide capture. App-specific targeting is
|
|
30
|
-
// represented in the core model, then falls back to system capture until the
|
|
31
|
-
// Darwin adapter exposes a target app option.
|
|
32
31
|
includeSystemAudio: true,
|
|
33
32
|
includeMicrophone: selection.includeMicrophone,
|
|
33
|
+
...source.kind === "app" && source.bundleId ? { targetBundleId: source.bundleId } : {},
|
|
34
|
+
...microphoneDeviceId ? { microphoneDeviceId } : {},
|
|
34
35
|
sourceLabel: source.label,
|
|
35
36
|
micEnabled: selection.includeMicrophone
|
|
36
37
|
};
|
|
@@ -1465,21 +1466,30 @@ function RecordSetupView({
|
|
|
1465
1466
|
const size = useTerminalSize();
|
|
1466
1467
|
const [srcIdx, setSrcIdx] = useState5(0);
|
|
1467
1468
|
const [includeMic, setIncludeMic] = useState5(true);
|
|
1469
|
+
const [micIdx, setMicIdx] = useState5(
|
|
1470
|
+
() => Math.max(0, model.microphones?.findIndex((device) => device.isDefault) ?? 0)
|
|
1471
|
+
);
|
|
1468
1472
|
const [sceneIdx, setSceneIdx] = useState5(0);
|
|
1469
1473
|
const sources = model.sources;
|
|
1474
|
+
const microphones = model.microphones ?? [];
|
|
1470
1475
|
const selected = sources[Math.min(srcIdx, Math.max(0, sources.length - 1))];
|
|
1476
|
+
const selectedMic = microphones[Math.min(micIdx, Math.max(0, microphones.length - 1))];
|
|
1471
1477
|
const wide = size.columns >= 100;
|
|
1472
1478
|
const hasAppSource = sources.some((source) => source.kind === "app");
|
|
1473
1479
|
const hasMultipleSources = sources.length > 1;
|
|
1480
|
+
const hasMultipleMicrophones = microphones.length > 1;
|
|
1474
1481
|
useInput5((input, key) => {
|
|
1475
1482
|
if (key.upArrow || input === "k") setSrcIdx((i) => Math.max(0, i - 1));
|
|
1476
1483
|
else if (key.downArrow || input === "j") setSrcIdx((i) => Math.min(sources.length - 1, i + 1));
|
|
1477
1484
|
else if (input === " ") setIncludeMic((m) => !m);
|
|
1485
|
+
else if (input === "m" && includeMic && hasMultipleMicrophones)
|
|
1486
|
+
setMicIdx((i) => (i + 1) % microphones.length);
|
|
1478
1487
|
else if (input === "s" && model.scenes.length > 1) setSceneIdx((i) => (i + 1) % model.scenes.length);
|
|
1479
1488
|
else if (key.return && selected) {
|
|
1480
1489
|
onStart({
|
|
1481
1490
|
sourceId: selected.id,
|
|
1482
1491
|
includeMicrophone: includeMic,
|
|
1492
|
+
...includeMic && selectedMic ? { microphoneDeviceId: selectedMic.id } : {},
|
|
1483
1493
|
sceneId: model.scenes[sceneIdx]?.id
|
|
1484
1494
|
});
|
|
1485
1495
|
} else if (key.escape) onCancel();
|
|
@@ -1503,6 +1513,7 @@ function RecordSetupView({
|
|
|
1503
1513
|
const shortcuts = [
|
|
1504
1514
|
hasMultipleSources ? "\u2191\u2193 source" : void 0,
|
|
1505
1515
|
"space mic",
|
|
1516
|
+
includeMic && hasMultipleMicrophones ? "m mic device" : void 0,
|
|
1506
1517
|
model.scenes.length > 1 ? "s scene" : void 0,
|
|
1507
1518
|
"\u23CE start recording",
|
|
1508
1519
|
"esc cancel"
|
|
@@ -1511,7 +1522,7 @@ function RecordSetupView({
|
|
|
1511
1522
|
/* @__PURE__ */ jsx16(Text14, { bold: true, color: "magenta", children: "New recording" }),
|
|
1512
1523
|
/* @__PURE__ */ jsxs13(Box14, { marginTop: 1, flexDirection: wide ? "row" : "column", children: [
|
|
1513
1524
|
/* @__PURE__ */ jsx16(Box14, { flexGrow: 1, flexDirection: "column", children: sourceList }),
|
|
1514
|
-
|
|
1525
|
+
/* @__PURE__ */ jsx16(Box14, { marginLeft: wide ? 4 : 0, marginTop: wide ? 0 : 1, children: capturePlan })
|
|
1515
1526
|
] }),
|
|
1516
1527
|
/* @__PURE__ */ jsxs13(Box14, { marginTop: 1, flexDirection: "column", children: [
|
|
1517
1528
|
/* @__PURE__ */ jsxs13(Text14, { children: [
|
|
@@ -1519,6 +1530,12 @@ function RecordSetupView({
|
|
|
1519
1530
|
/* @__PURE__ */ jsx16(Text14, { color: includeMic ? "green" : "gray", children: includeMic ? "[x] include mic" : "[ ] include mic" }),
|
|
1520
1531
|
/* @__PURE__ */ jsx16(Text14, { dimColor: true, children: " (space)" })
|
|
1521
1532
|
] }),
|
|
1533
|
+
selectedMic ? /* @__PURE__ */ jsxs13(Text14, { dimColor: !includeMic, children: [
|
|
1534
|
+
/* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "Mic device " }),
|
|
1535
|
+
/* @__PURE__ */ jsx16(Text14, { children: selectedMic.label }),
|
|
1536
|
+
selectedMic.isDefault ? /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: " \xB7 default" }) : null,
|
|
1537
|
+
includeMic && hasMultipleMicrophones ? /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: " (m to change)" }) : null
|
|
1538
|
+
] }) : null,
|
|
1522
1539
|
model.scenes.length > 0 ? /* @__PURE__ */ jsxs13(Text14, { children: [
|
|
1523
1540
|
/* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "Scene " }),
|
|
1524
1541
|
/* @__PURE__ */ jsx16(Text14, { children: model.scenes[sceneIdx]?.label ?? "Default" }),
|
|
@@ -1551,6 +1568,8 @@ function waveform(samples, width) {
|
|
|
1551
1568
|
}
|
|
1552
1569
|
function RecordingHeroScreen({
|
|
1553
1570
|
telemetry,
|
|
1571
|
+
artifact,
|
|
1572
|
+
canTranscribe = false,
|
|
1554
1573
|
canPause = false,
|
|
1555
1574
|
now = () => Date.now()
|
|
1556
1575
|
}) {
|
|
@@ -1568,6 +1587,7 @@ function RecordingHeroScreen({
|
|
|
1568
1587
|
const elapsed = telemetry.startedAtMs != null ? formatClockMs(Math.max(0, tick - telemetry.startedAtMs)) : "00:00";
|
|
1569
1588
|
const innerWidth = Math.max(10, size.columns - 4);
|
|
1570
1589
|
if (telemetry.status === "stopped") {
|
|
1590
|
+
const handoff = stoppedHandoffCopy(artifact, canTranscribe);
|
|
1571
1591
|
const meta3 = [
|
|
1572
1592
|
telemetry.durationMs != null ? formatClockMs(telemetry.durationMs) : null,
|
|
1573
1593
|
formatBytes2(telemetry.sizeBytes) || null
|
|
@@ -1579,7 +1599,10 @@ function RecordingHeroScreen({
|
|
|
1579
1599
|
meta3 ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: meta3 }) : null,
|
|
1580
1600
|
telemetry.savedPath ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, wrap: "truncate-middle", children: telemetry.savedPath }) : null
|
|
1581
1601
|
] }),
|
|
1582
|
-
/* @__PURE__ */
|
|
1602
|
+
/* @__PURE__ */ jsxs14(Box15, { marginTop: 1, children: [
|
|
1603
|
+
/* @__PURE__ */ jsx17(Text15, { color: handoff.tone === "red" ? "red" : handoff.tone === "green" ? "green" : void 0, dimColor: handoff.tone === "dim", children: handoff.text }),
|
|
1604
|
+
artifact?.error ? /* @__PURE__ */ jsx17(Text15, { color: "red", wrap: "truncate-end", children: artifact.error }) : null
|
|
1605
|
+
] })
|
|
1583
1606
|
] });
|
|
1584
1607
|
}
|
|
1585
1608
|
if (telemetry.status === "error") {
|
|
@@ -1612,6 +1635,27 @@ function RecordingHeroScreen({
|
|
|
1612
1635
|
] }) })
|
|
1613
1636
|
] });
|
|
1614
1637
|
}
|
|
1638
|
+
function stoppedHandoffCopy(artifact, canTranscribe) {
|
|
1639
|
+
if (artifact?.uploadStatus === "uploading") {
|
|
1640
|
+
return { text: "Uploading\u2026", tone: "normal" };
|
|
1641
|
+
}
|
|
1642
|
+
if (artifact?.transcriptionStatus === "processing") {
|
|
1643
|
+
return { text: "Transcribing\u2026", tone: "normal" };
|
|
1644
|
+
}
|
|
1645
|
+
if (artifact?.transcriptionStatus === "queued") {
|
|
1646
|
+
return { text: "Transcription queued \xB7 \u23CE open recording \xB7 n not now", tone: "green" };
|
|
1647
|
+
}
|
|
1648
|
+
if (artifact?.transcriptionStatus === "ready") {
|
|
1649
|
+
return { text: "Transcription ready \xB7 \u23CE open recording \xB7 n not now", tone: "green" };
|
|
1650
|
+
}
|
|
1651
|
+
if (artifact?.uploadStatus === "failed" || artifact?.transcriptionStatus === "failed") {
|
|
1652
|
+
return { text: "Transcription failed \xB7 \u23CE retry \xB7 n not now", tone: "red" };
|
|
1653
|
+
}
|
|
1654
|
+
if (!canTranscribe || !artifact?.audioPath) {
|
|
1655
|
+
return { text: "Saved locally \xB7 n back", tone: "dim" };
|
|
1656
|
+
}
|
|
1657
|
+
return { text: "Transcribe now? \u23CE yes \xB7 n not now", tone: "normal" };
|
|
1658
|
+
}
|
|
1615
1659
|
var BLOCKS;
|
|
1616
1660
|
var init_RecordingHeroScreen = __esm({
|
|
1617
1661
|
"src/tui/RecordingHeroScreen.tsx"() {
|
|
@@ -1703,7 +1747,9 @@ function AppShell({
|
|
|
1703
1747
|
fetchAccountStatus,
|
|
1704
1748
|
recordingAudio,
|
|
1705
1749
|
listDownloadedRecordingIds,
|
|
1750
|
+
fetchRecordSetup,
|
|
1706
1751
|
startLiveRecord,
|
|
1752
|
+
transcribeRecordingArtifact,
|
|
1707
1753
|
initialView = "overview",
|
|
1708
1754
|
openUrl: openUrl2,
|
|
1709
1755
|
copyText: copyText2,
|
|
@@ -1733,8 +1779,13 @@ function AppShell({
|
|
|
1733
1779
|
const [audioCache, setAudioCache] = useState7(() => /* @__PURE__ */ new Map());
|
|
1734
1780
|
const [downloadedIds, setDownloadedIds] = useState7(() => /* @__PURE__ */ new Set());
|
|
1735
1781
|
const [liveRecord, setLiveRecord] = useState7(void 0);
|
|
1736
|
-
const
|
|
1782
|
+
const [recordSetupInputs, setRecordSetupInputs] = useState7({
|
|
1737
1783
|
sources: DEFAULT_RECORDING_SOURCES,
|
|
1784
|
+
microphones: []
|
|
1785
|
+
});
|
|
1786
|
+
const recordSetupModel = {
|
|
1787
|
+
sources: recordSetupInputs.sources.length > 0 ? recordSetupInputs.sources : DEFAULT_RECORDING_SOURCES,
|
|
1788
|
+
microphones: recordSetupInputs.microphones ?? [],
|
|
1738
1789
|
scenes: DEFAULT_RECORDING_SCENES
|
|
1739
1790
|
};
|
|
1740
1791
|
const refreshDownloadedIds = useCallback(async () => {
|
|
@@ -1750,7 +1801,7 @@ function AppShell({
|
|
|
1750
1801
|
const screen = stack[stack.length - 1];
|
|
1751
1802
|
const beginLiveRecord = useCallback(
|
|
1752
1803
|
(selection = DEFAULT_RECORDING_SELECTION) => {
|
|
1753
|
-
const capture = recordingCaptureMappingFromSelection(selection,
|
|
1804
|
+
const capture = recordingCaptureMappingFromSelection(selection, recordSetupModel.sources);
|
|
1754
1805
|
const telemetry = {
|
|
1755
1806
|
status: "starting",
|
|
1756
1807
|
startedAtMs: now(),
|
|
@@ -1771,7 +1822,7 @@ function AppShell({
|
|
|
1771
1822
|
return;
|
|
1772
1823
|
}
|
|
1773
1824
|
setLiveRecord({ kind: "starting", selection, telemetry });
|
|
1774
|
-
startLiveRecord(selection).then((session) => {
|
|
1825
|
+
startLiveRecord(selection, recordSetupModel.sources).then((session) => {
|
|
1775
1826
|
setLiveRecord((current) => {
|
|
1776
1827
|
if (current?.kind !== "starting") return current;
|
|
1777
1828
|
return {
|
|
@@ -1785,7 +1836,7 @@ function AppShell({
|
|
|
1785
1836
|
setLiveRecord(recordErrorState(error51, selection));
|
|
1786
1837
|
});
|
|
1787
1838
|
},
|
|
1788
|
-
[now, startLiveRecord]
|
|
1839
|
+
[now, recordSetupModel.sources, startLiveRecord]
|
|
1789
1840
|
);
|
|
1790
1841
|
const stopLiveRecord = useCallback(async () => {
|
|
1791
1842
|
const current = liveRecord;
|
|
@@ -1884,6 +1935,63 @@ function AppShell({
|
|
|
1884
1935
|
setAccountStatus("error");
|
|
1885
1936
|
}
|
|
1886
1937
|
}, [fetchJobs, fetchRecordings, fetchDashboardStats, fetchAccountStatus]);
|
|
1938
|
+
const transcribeStoppedRecording = useCallback(async () => {
|
|
1939
|
+
const current = liveRecord;
|
|
1940
|
+
if (current?.kind !== "stopped") return;
|
|
1941
|
+
const artifact = current.artifact;
|
|
1942
|
+
if (artifact?.recordingId && artifact.uploadStatus === "uploaded") {
|
|
1943
|
+
await refresh({ resetRecordings: true });
|
|
1944
|
+
setStack([{ kind: "overview" }, { kind: "recordingDetail", recordingId: artifact.recordingId }]);
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1947
|
+
if (!artifact?.audioPath) {
|
|
1948
|
+
setNotice("No local audio file is available to transcribe.");
|
|
1949
|
+
return;
|
|
1950
|
+
}
|
|
1951
|
+
if (!transcribeRecordingArtifact) {
|
|
1952
|
+
setNotice("Transcription is not available in this CLI session.");
|
|
1953
|
+
return;
|
|
1954
|
+
}
|
|
1955
|
+
setLiveRecord({
|
|
1956
|
+
...current,
|
|
1957
|
+
artifact: {
|
|
1958
|
+
...artifact,
|
|
1959
|
+
uploadStatus: "uploading",
|
|
1960
|
+
transcriptionStatus: "not_started",
|
|
1961
|
+
error: void 0
|
|
1962
|
+
}
|
|
1963
|
+
});
|
|
1964
|
+
try {
|
|
1965
|
+
const uploaded = await transcribeRecordingArtifact(artifact);
|
|
1966
|
+
const transcriptionStatus = uploaded.transcriptId != null || uploaded.status === "succeeded" || uploaded.status === "ready" ? "ready" : uploaded.status === "running" ? "processing" : uploaded.jobId ? "queued" : "not_started";
|
|
1967
|
+
setLiveRecord({
|
|
1968
|
+
...current,
|
|
1969
|
+
artifact: {
|
|
1970
|
+
...artifact,
|
|
1971
|
+
recordingId: uploaded.recordingId,
|
|
1972
|
+
...uploaded.jobId ? { jobId: uploaded.jobId } : {},
|
|
1973
|
+
...uploaded.transcriptId ? { transcriptId: uploaded.transcriptId } : {},
|
|
1974
|
+
uploadStatus: "uploaded",
|
|
1975
|
+
transcriptionStatus
|
|
1976
|
+
}
|
|
1977
|
+
});
|
|
1978
|
+
setNotice(
|
|
1979
|
+
transcriptionStatus === "ready" ? "Transcription ready." : uploaded.jobId ? "Transcription queued." : "Uploaded to Recappi Cloud."
|
|
1980
|
+
);
|
|
1981
|
+
await refresh({ resetRecordings: true });
|
|
1982
|
+
} catch (error51) {
|
|
1983
|
+
setLiveRecord({
|
|
1984
|
+
...current,
|
|
1985
|
+
artifact: {
|
|
1986
|
+
...artifact,
|
|
1987
|
+
uploadStatus: "failed",
|
|
1988
|
+
transcriptionStatus: "failed",
|
|
1989
|
+
error: error51 instanceof Error ? error51.message : String(error51)
|
|
1990
|
+
}
|
|
1991
|
+
});
|
|
1992
|
+
setNotice("Transcription failed. Press enter to retry.");
|
|
1993
|
+
}
|
|
1994
|
+
}, [liveRecord, refresh, transcribeRecordingArtifact]);
|
|
1887
1995
|
const loadMoreRecordings = useCallback(async () => {
|
|
1888
1996
|
if (!fetchRecordings || !recordingsNextCursor || loadingMoreRecordings) return;
|
|
1889
1997
|
setLoadingMoreRecordings(true);
|
|
@@ -2029,7 +2137,7 @@ function AppShell({
|
|
|
2029
2137
|
return;
|
|
2030
2138
|
}
|
|
2031
2139
|
if (liveRecord?.kind === "stopped" && key.return) {
|
|
2032
|
-
|
|
2140
|
+
void transcribeStoppedRecording();
|
|
2033
2141
|
return;
|
|
2034
2142
|
}
|
|
2035
2143
|
if (input === "q" || key.escape || key.leftArrow || input === "n") void stopLiveRecord();
|
|
@@ -2042,6 +2150,16 @@ function AppShell({
|
|
|
2042
2150
|
if (input === "3") return goTab("account");
|
|
2043
2151
|
if (input === "n") {
|
|
2044
2152
|
setStack((st) => [...st, { kind: "recordSetup" }]);
|
|
2153
|
+
if (fetchRecordSetup) {
|
|
2154
|
+
fetchRecordSetup().then((model) => {
|
|
2155
|
+
setRecordSetupInputs({
|
|
2156
|
+
sources: model.sources.length > 0 ? model.sources : DEFAULT_RECORDING_SOURCES,
|
|
2157
|
+
microphones: model.microphones ?? []
|
|
2158
|
+
});
|
|
2159
|
+
}).catch(() => {
|
|
2160
|
+
setRecordSetupInputs({ sources: DEFAULT_RECORDING_SOURCES, microphones: [] });
|
|
2161
|
+
});
|
|
2162
|
+
}
|
|
2045
2163
|
return;
|
|
2046
2164
|
}
|
|
2047
2165
|
if (input === "r") return void refresh({ resetRecordings: true });
|
|
@@ -2126,7 +2244,15 @@ function AppShell({
|
|
|
2126
2244
|
return /* @__PURE__ */ jsx18(LiveCaptionsScreen, { source: liveRecord.session.source, now });
|
|
2127
2245
|
}
|
|
2128
2246
|
if (liveRecord?.kind === "live" || liveRecord?.kind === "starting" || liveRecord?.kind === "stopping" || liveRecord?.kind === "stopped") {
|
|
2129
|
-
return /* @__PURE__ */ jsx18(Detail, { notice, children: /* @__PURE__ */ jsx18(
|
|
2247
|
+
return /* @__PURE__ */ jsx18(Detail, { notice, children: /* @__PURE__ */ jsx18(
|
|
2248
|
+
RecordingHeroScreen,
|
|
2249
|
+
{
|
|
2250
|
+
telemetry: liveRecord.telemetry,
|
|
2251
|
+
artifact: liveRecord.kind === "stopped" ? liveRecord.artifact : void 0,
|
|
2252
|
+
canTranscribe: Boolean(transcribeRecordingArtifact),
|
|
2253
|
+
now
|
|
2254
|
+
}
|
|
2255
|
+
) });
|
|
2130
2256
|
}
|
|
2131
2257
|
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", height: size.rows, paddingX: 1, children: [
|
|
2132
2258
|
/* @__PURE__ */ jsx18(Box16, { flexGrow: 1, flexDirection: "column", paddingX: 1, paddingTop: 1, children: liveRecord?.kind === "error" ? (() => {
|
|
@@ -2284,7 +2410,9 @@ async function runDashboard(deps) {
|
|
|
2284
2410
|
fetchAccountStatus: deps.fetchAccountStatus,
|
|
2285
2411
|
recordingAudio: deps.recordingAudio,
|
|
2286
2412
|
listDownloadedRecordingIds: deps.listDownloadedRecordingIds,
|
|
2413
|
+
fetchRecordSetup: deps.fetchRecordSetup,
|
|
2287
2414
|
startLiveRecord: deps.startLiveRecord,
|
|
2415
|
+
transcribeRecordingArtifact: deps.transcribeRecordingArtifact,
|
|
2288
2416
|
initialView: deps.initialView ?? "overview",
|
|
2289
2417
|
openUrl,
|
|
2290
2418
|
copyText
|
|
@@ -16955,11 +17083,31 @@ var sidecarInfoSchema = external_exports.object({
|
|
|
16955
17083
|
var sidecarRecordingOptionsSchema = external_exports.object({
|
|
16956
17084
|
includeSystemAudio: external_exports.boolean().default(true),
|
|
16957
17085
|
includeMicrophone: external_exports.boolean().default(true),
|
|
17086
|
+
targetBundleId: external_exports.string().optional(),
|
|
17087
|
+
microphoneDeviceId: external_exports.string().optional(),
|
|
16958
17088
|
liveCaptions: external_exports.boolean().default(false),
|
|
16959
17089
|
translationLanguage: external_exports.string().optional(),
|
|
16960
17090
|
transcriptionLanguage: external_exports.string().optional(),
|
|
16961
17091
|
title: external_exports.string().optional()
|
|
16962
17092
|
});
|
|
17093
|
+
var sidecarRecordingSourceSchema = external_exports.object({
|
|
17094
|
+
id: external_exports.string(),
|
|
17095
|
+
kind: external_exports.enum(["system", "app"]),
|
|
17096
|
+
label: external_exports.string(),
|
|
17097
|
+
appName: external_exports.string().optional(),
|
|
17098
|
+
bundleId: external_exports.string().optional()
|
|
17099
|
+
});
|
|
17100
|
+
var sidecarRecordingSourcesListResultSchema = external_exports.object({
|
|
17101
|
+
sources: external_exports.array(sidecarRecordingSourceSchema)
|
|
17102
|
+
});
|
|
17103
|
+
var sidecarMicrophoneDeviceSchema = external_exports.object({
|
|
17104
|
+
id: external_exports.string(),
|
|
17105
|
+
label: external_exports.string(),
|
|
17106
|
+
isDefault: external_exports.boolean().optional()
|
|
17107
|
+
});
|
|
17108
|
+
var sidecarMicrophonesListResultSchema = external_exports.object({
|
|
17109
|
+
microphones: external_exports.array(sidecarMicrophoneDeviceSchema)
|
|
17110
|
+
});
|
|
16963
17111
|
var sidecarPermissionNameSchema = external_exports.enum(["screen_recording", "microphone"]);
|
|
16964
17112
|
var sidecarPermissionStatusSchema = external_exports.enum(["granted", "denied", "unknown"]);
|
|
16965
17113
|
var sidecarPermissionItemSchema = external_exports.object({
|
|
@@ -17038,6 +17186,18 @@ var sidecarRequestSchema = external_exports.discriminatedUnion("method", [
|
|
|
17038
17186
|
method: external_exports.literal("recappi.handshake"),
|
|
17039
17187
|
params: sidecarHandshakeParamsSchema
|
|
17040
17188
|
}),
|
|
17189
|
+
external_exports.object({
|
|
17190
|
+
jsonrpc: external_exports.literal("2.0"),
|
|
17191
|
+
id: sidecarJsonRpcIdSchema,
|
|
17192
|
+
method: external_exports.literal("recappi.recording.sources.list"),
|
|
17193
|
+
params: external_exports.object({}).optional()
|
|
17194
|
+
}),
|
|
17195
|
+
external_exports.object({
|
|
17196
|
+
jsonrpc: external_exports.literal("2.0"),
|
|
17197
|
+
id: sidecarJsonRpcIdSchema,
|
|
17198
|
+
method: external_exports.literal("recappi.recording.microphones.list"),
|
|
17199
|
+
params: external_exports.object({}).optional()
|
|
17200
|
+
}),
|
|
17041
17201
|
external_exports.object({
|
|
17042
17202
|
jsonrpc: external_exports.literal("2.0"),
|
|
17043
17203
|
id: sidecarJsonRpcIdSchema,
|
|
@@ -19899,6 +20059,20 @@ var MiniSidecarClient = class {
|
|
|
19899
20059
|
sidecarHandshakeResultSchema
|
|
19900
20060
|
);
|
|
19901
20061
|
}
|
|
20062
|
+
listRecordingSources() {
|
|
20063
|
+
return this.request(
|
|
20064
|
+
"recappi.recording.sources.list",
|
|
20065
|
+
{},
|
|
20066
|
+
sidecarRecordingSourcesListResultSchema
|
|
20067
|
+
);
|
|
20068
|
+
}
|
|
20069
|
+
listMicrophones() {
|
|
20070
|
+
return this.request(
|
|
20071
|
+
"recappi.recording.microphones.list",
|
|
20072
|
+
{},
|
|
20073
|
+
sidecarMicrophonesListResultSchema
|
|
20074
|
+
);
|
|
20075
|
+
}
|
|
19902
20076
|
startRecording(params) {
|
|
19903
20077
|
return this.request(
|
|
19904
20078
|
"recappi.recording.start",
|
|
@@ -20119,6 +20293,8 @@ async function startLiveRecordSession(opts, selection = {
|
|
|
20119
20293
|
...opts,
|
|
20120
20294
|
includeSystemAudio: capture.includeSystemAudio,
|
|
20121
20295
|
includeMicrophone: capture.includeMicrophone,
|
|
20296
|
+
targetBundleId: capture.targetBundleId,
|
|
20297
|
+
microphoneDeviceId: capture.microphoneDeviceId,
|
|
20122
20298
|
live: false
|
|
20123
20299
|
});
|
|
20124
20300
|
return {
|
|
@@ -20127,6 +20303,32 @@ async function startLiveRecordSession(opts, selection = {
|
|
|
20127
20303
|
stop: session.stop
|
|
20128
20304
|
};
|
|
20129
20305
|
}
|
|
20306
|
+
async function listRecordInputs(opts) {
|
|
20307
|
+
const command = resolveSidecarCommand(opts);
|
|
20308
|
+
const sidecarArgs = opts.sidecarArgs ?? [];
|
|
20309
|
+
const spawnSidecar = opts.runtime?.spawnSidecar ?? spawnMiniSidecar;
|
|
20310
|
+
const sidecar = spawnSidecar({ command, args: sidecarArgs, env: opts.env });
|
|
20311
|
+
try {
|
|
20312
|
+
await sidecar.client.handshake(
|
|
20313
|
+
defaultSidecarHandshakeParams({
|
|
20314
|
+
client: { name: "recappi-cli", version: opts.cliVersion },
|
|
20315
|
+
capabilities: ["recording.capture"]
|
|
20316
|
+
})
|
|
20317
|
+
);
|
|
20318
|
+
const [sourceResult, microphoneResult] = await Promise.all([
|
|
20319
|
+
sidecar.client.listRecordingSources(),
|
|
20320
|
+
sidecar.client.listMicrophones()
|
|
20321
|
+
]);
|
|
20322
|
+
const sources = normalizeSidecarSources(sourceResult.sources);
|
|
20323
|
+
const microphones = normalizeSidecarMicrophones(microphoneResult.microphones);
|
|
20324
|
+
return {
|
|
20325
|
+
sources: sources.length > 0 ? sources : DEFAULT_RECORDING_SOURCES,
|
|
20326
|
+
microphones
|
|
20327
|
+
};
|
|
20328
|
+
} finally {
|
|
20329
|
+
sidecar.kill();
|
|
20330
|
+
}
|
|
20331
|
+
}
|
|
20130
20332
|
async function startRecordSession(opts) {
|
|
20131
20333
|
const command = resolveSidecarCommand(opts);
|
|
20132
20334
|
const sidecarArgs = opts.sidecarArgs ?? [];
|
|
@@ -20178,6 +20380,8 @@ async function startRecordSession(opts) {
|
|
|
20178
20380
|
const recordingOptions = {
|
|
20179
20381
|
includeSystemAudio: opts.includeSystemAudio ?? true,
|
|
20180
20382
|
includeMicrophone: opts.includeMicrophone ?? true,
|
|
20383
|
+
...opts.targetBundleId ? { targetBundleId: opts.targetBundleId } : {},
|
|
20384
|
+
...opts.microphoneDeviceId ? { microphoneDeviceId: opts.microphoneDeviceId } : {},
|
|
20181
20385
|
liveCaptions: opts.live === true,
|
|
20182
20386
|
...opts.translationLanguage ? { translationLanguage: opts.translationLanguage } : {},
|
|
20183
20387
|
...opts.transcriptionLanguage ? { transcriptionLanguage: opts.transcriptionLanguage } : {},
|
|
@@ -20229,6 +20433,31 @@ async function startRecordSession(opts) {
|
|
|
20229
20433
|
throw error51;
|
|
20230
20434
|
}
|
|
20231
20435
|
}
|
|
20436
|
+
function normalizeSidecarSources(sources) {
|
|
20437
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20438
|
+
const out = [];
|
|
20439
|
+
for (const source of sources) {
|
|
20440
|
+
const id = source.kind === "app" && source.bundleId ? `app:${source.bundleId}` : source.id || source.kind;
|
|
20441
|
+
if (!id || seen.has(id)) continue;
|
|
20442
|
+
seen.add(id);
|
|
20443
|
+
out.push({
|
|
20444
|
+
id,
|
|
20445
|
+
kind: source.kind,
|
|
20446
|
+
label: source.label,
|
|
20447
|
+
...source.appName ? { appName: source.appName } : {},
|
|
20448
|
+
...source.bundleId ? { bundleId: source.bundleId } : {},
|
|
20449
|
+
canIncludeMicrophone: true
|
|
20450
|
+
});
|
|
20451
|
+
}
|
|
20452
|
+
return out;
|
|
20453
|
+
}
|
|
20454
|
+
function normalizeSidecarMicrophones(microphones) {
|
|
20455
|
+
return microphones.filter((device) => device.id && device.label).map((device) => ({
|
|
20456
|
+
id: device.id,
|
|
20457
|
+
label: device.label,
|
|
20458
|
+
...device.isDefault === true ? { isDefault: true } : {}
|
|
20459
|
+
}));
|
|
20460
|
+
}
|
|
20232
20461
|
function assertRecordingPermissions(permissions) {
|
|
20233
20462
|
const blocked = permissions.find((permission) => permission.status !== "granted");
|
|
20234
20463
|
if (!blocked) return;
|
|
@@ -20449,7 +20678,12 @@ async function runCli(deps = {}) {
|
|
|
20449
20678
|
recordingAudio,
|
|
20450
20679
|
listDownloadedRecordingIds: () => recordingAudio.listDownloadedRecordingIds(),
|
|
20451
20680
|
listDownloads: () => recordingAudio.listDownloads(),
|
|
20452
|
-
|
|
20681
|
+
fetchRecordSetup: async () => listRecordInputs({
|
|
20682
|
+
cliVersion: CLI_VERSION,
|
|
20683
|
+
env: deps.env,
|
|
20684
|
+
runtime: deps.recordRuntime
|
|
20685
|
+
}),
|
|
20686
|
+
startLiveRecord: async (selection, sources) => {
|
|
20453
20687
|
const liveStatus = await client.authStatus();
|
|
20454
20688
|
if (!liveStatus.loggedIn || !liveStatus.userId) {
|
|
20455
20689
|
throw cliError("auth.not_logged_in", "Sign in before starting a sidecar recording.", {
|
|
@@ -20468,9 +20702,32 @@ async function runCli(deps = {}) {
|
|
|
20468
20702
|
homeDir: deps.homeDir,
|
|
20469
20703
|
runtime: deps.recordRuntime
|
|
20470
20704
|
},
|
|
20471
|
-
selection
|
|
20705
|
+
selection,
|
|
20706
|
+
sources
|
|
20472
20707
|
);
|
|
20473
20708
|
},
|
|
20709
|
+
transcribeRecordingArtifact: async (artifact) => {
|
|
20710
|
+
if (!artifact.audioPath) {
|
|
20711
|
+
throw cliError("input.not_found", "No local audio file is available to transcribe.");
|
|
20712
|
+
}
|
|
20713
|
+
const data = await client.uploadPathBatch({
|
|
20714
|
+
inputPath: artifact.audioPath,
|
|
20715
|
+
transcribe: true,
|
|
20716
|
+
wait: false
|
|
20717
|
+
});
|
|
20718
|
+
if (data.failures.length > 0) {
|
|
20719
|
+
const failure = data.failures[0];
|
|
20720
|
+
throw cliError(failure.error.code, failure.error.message, {
|
|
20721
|
+
hint: failure.error.hint,
|
|
20722
|
+
retryable: failure.error.retryable
|
|
20723
|
+
});
|
|
20724
|
+
}
|
|
20725
|
+
const success2 = data.successes[0];
|
|
20726
|
+
if (!success2) {
|
|
20727
|
+
throw cliError("input.unsupported_audio", "No supported local audio file was uploaded.");
|
|
20728
|
+
}
|
|
20729
|
+
return success2;
|
|
20730
|
+
},
|
|
20474
20731
|
initialView: parsed.initialView
|
|
20475
20732
|
});
|
|
20476
20733
|
return 0;
|