recappi 0.1.76 → 0.1.78
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 +529 -195
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -84,7 +84,7 @@ function isLiveCaptionSidecarError(code) {
|
|
|
84
84
|
}
|
|
85
85
|
function recordingArtifactFromRecordData(data) {
|
|
86
86
|
const artifact = data.artifacts.find((item) => item.kind === "recording_session") ?? data.artifacts[0];
|
|
87
|
-
const metadata =
|
|
87
|
+
const metadata = isRecord6(artifact?.metadata) ? artifact.metadata : {};
|
|
88
88
|
const audioPath = typeof metadata.audioPath === "string" ? metadata.audioPath : typeof artifact?.localPath === "string" ? artifact.localPath : void 0;
|
|
89
89
|
const durationMs = typeof metadata.durationMs === "number" && Number.isFinite(metadata.durationMs) ? metadata.durationMs : void 0;
|
|
90
90
|
const sizeBytes = typeof metadata.sizeBytes === "number" && Number.isFinite(metadata.sizeBytes) ? metadata.sizeBytes : void 0;
|
|
@@ -104,7 +104,7 @@ function artifactTelemetryPatch(artifact) {
|
|
|
104
104
|
sizeBytes: artifact.sizeBytes
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
|
-
function
|
|
107
|
+
function isRecord6(value) {
|
|
108
108
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
109
109
|
}
|
|
110
110
|
var DEFAULT_RECORDING_SOURCES, DEFAULT_RECORDING_SCENES, DEFAULT_RECORDING_SELECTION;
|
|
@@ -977,7 +977,8 @@ function OverviewView({
|
|
|
977
977
|
peekItem,
|
|
978
978
|
peekSummary,
|
|
979
979
|
showPeek = false,
|
|
980
|
-
peekWidth = 0
|
|
980
|
+
peekWidth = 0,
|
|
981
|
+
revalidating = false
|
|
981
982
|
}) {
|
|
982
983
|
const jobCounts = countJobs(jobs);
|
|
983
984
|
const running = stats?.jobs.running ?? jobCounts.running;
|
|
@@ -998,7 +999,8 @@ function OverviewView({
|
|
|
998
999
|
/* @__PURE__ */ jsx12(Text10, { dimColor: true, children: " \xB7 " }),
|
|
999
1000
|
/* @__PURE__ */ jsx12(Text10, { color: "yellow", children: `${queued} queued` })
|
|
1000
1001
|
] }) : null,
|
|
1001
|
-
stats?.recordings.totalDurationMs != null ? /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: ` \xB7 ${formatClockMs(stats.recordings.totalDurationMs)} transcribed` }) : null
|
|
1002
|
+
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: ` \xB7 ${"\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F"[spinnerFrame % 10]} syncing` }) : null
|
|
1002
1004
|
] }),
|
|
1003
1005
|
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", alignItems: "flex-start", children: [
|
|
1004
1006
|
/* @__PURE__ */ jsx12(Box10, { flexGrow: 1, flexDirection: "column", children: /* @__PURE__ */ jsx12(
|
|
@@ -1877,20 +1879,20 @@ function recordErrorCopy(code, message) {
|
|
|
1877
1879
|
}
|
|
1878
1880
|
function recordErrorState(error51, selection) {
|
|
1879
1881
|
if (error51 instanceof Error) {
|
|
1880
|
-
const descriptor =
|
|
1882
|
+
const descriptor = isRecord9(error51) && isRecord9(error51.descriptor) ? error51.descriptor : void 0;
|
|
1881
1883
|
return {
|
|
1882
1884
|
kind: "error",
|
|
1883
1885
|
message: error51.message,
|
|
1884
1886
|
code: typeof descriptor?.code === "string" ? descriptor.code : "code" in error51 && typeof error51.code === "string" ? error51.code : void 0,
|
|
1885
|
-
data:
|
|
1887
|
+
data: isRecord9(error51) ? error51.data : void 0,
|
|
1886
1888
|
...selection ? { selection } : {}
|
|
1887
1889
|
};
|
|
1888
1890
|
}
|
|
1889
1891
|
return { kind: "error", message: String(error51), ...selection ? { selection } : {} };
|
|
1890
1892
|
}
|
|
1891
1893
|
function transcribeHandoffErrorCopy(error51) {
|
|
1892
|
-
const descriptor = error51 instanceof Error &&
|
|
1893
|
-
const code = typeof descriptor?.code === "string" ? descriptor.code :
|
|
1894
|
+
const descriptor = error51 instanceof Error && isRecord9(error51) && isRecord9(error51.descriptor) ? error51.descriptor : void 0;
|
|
1895
|
+
const code = typeof descriptor?.code === "string" ? descriptor.code : isRecord9(error51) && typeof error51.code === "string" ? error51.code : void 0;
|
|
1894
1896
|
switch (code) {
|
|
1895
1897
|
case "auth.not_logged_in":
|
|
1896
1898
|
return "Sign in to Recappi before transcribing this recording.";
|
|
@@ -1964,8 +1966,8 @@ function recordFrameTitle(recordings, recordingId) {
|
|
|
1964
1966
|
return recording ? recordingTitle2(recording) : "New recording";
|
|
1965
1967
|
}
|
|
1966
1968
|
function permissionItemsFromRecordError(data) {
|
|
1967
|
-
const sidecarError =
|
|
1968
|
-
const sidecarData =
|
|
1969
|
+
const sidecarError = isRecord9(data) ? data : void 0;
|
|
1970
|
+
const sidecarData = isRecord9(sidecarError?.data) ? sidecarError.data : void 0;
|
|
1969
1971
|
const permission = typeof sidecarData?.permission === "string" ? sidecarData.permission : "";
|
|
1970
1972
|
const hint = typeof sidecarData?.recovery === "string" ? sidecarData.recovery : void 0;
|
|
1971
1973
|
const requiresProcessRestart = sidecarData?.requiresProcessRestart === true || sidecarData?.requiresProcessRestart === "true";
|
|
@@ -1980,21 +1982,22 @@ function permissionItemsFromRecordError(data) {
|
|
|
1980
1982
|
];
|
|
1981
1983
|
}
|
|
1982
1984
|
function settingsUrlFromRecordError(data) {
|
|
1983
|
-
const sidecarError =
|
|
1984
|
-
const sidecarData =
|
|
1985
|
+
const sidecarError = isRecord9(data) ? data : void 0;
|
|
1986
|
+
const sidecarData = isRecord9(sidecarError?.data) ? sidecarError.data : void 0;
|
|
1985
1987
|
const permission = typeof sidecarData?.permission === "string" ? sidecarData.permission : "";
|
|
1986
1988
|
if (permission === "microphone") {
|
|
1987
1989
|
return "x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone";
|
|
1988
1990
|
}
|
|
1989
1991
|
return "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
|
|
1990
1992
|
}
|
|
1991
|
-
function
|
|
1993
|
+
function isRecord9(value) {
|
|
1992
1994
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1993
1995
|
}
|
|
1994
1996
|
function AppShell({
|
|
1995
1997
|
fetchJobs,
|
|
1996
1998
|
fetchTranscript,
|
|
1997
1999
|
fetchRecordings,
|
|
2000
|
+
fetchCachedRecordings,
|
|
1998
2001
|
fetchDashboardStats,
|
|
1999
2002
|
fetchAccountStatus,
|
|
2000
2003
|
recordingAudio,
|
|
@@ -2028,6 +2031,7 @@ function AppShell({
|
|
|
2028
2031
|
const [selected, setSelected] = useState8(0);
|
|
2029
2032
|
const [spinnerFrame, setSpinnerFrame] = useState8(0);
|
|
2030
2033
|
const [loadingMoreRecordings, setLoadingMoreRecordings] = useState8(false);
|
|
2034
|
+
const [revalidatingRecordings, setRevalidatingRecordings] = useState8(false);
|
|
2031
2035
|
const [loaded, setLoaded] = useState8(false);
|
|
2032
2036
|
const [loadError, setLoadError] = useState8(void 0);
|
|
2033
2037
|
const [notice, setNotice] = useState8(void 0);
|
|
@@ -2067,6 +2071,12 @@ function AppShell({
|
|
|
2067
2071
|
void refreshDownloadedIds();
|
|
2068
2072
|
}, [refreshDownloadedIds]);
|
|
2069
2073
|
const screen = stack[stack.length - 1];
|
|
2074
|
+
const recordingsRef = useRef3(recordings);
|
|
2075
|
+
recordingsRef.current = recordings;
|
|
2076
|
+
const selectedRef = useRef3(selected);
|
|
2077
|
+
selectedRef.current = selected;
|
|
2078
|
+
const screenRef = useRef3(screen);
|
|
2079
|
+
screenRef.current = screen;
|
|
2070
2080
|
useEffect4(() => {
|
|
2071
2081
|
if (screen.kind !== "recordSetup" || !startRecordSetupPreview) return;
|
|
2072
2082
|
let cancelled = false;
|
|
@@ -2245,6 +2255,21 @@ function AppShell({
|
|
|
2245
2255
|
}, [peekTranscriptId, fetchTranscript]);
|
|
2246
2256
|
const peekSummary = peekTranscriptId ? summaryCache.get(peekTranscriptId) : void 0;
|
|
2247
2257
|
const refresh = useCallback(async ({ resetRecordings = false } = {}) => {
|
|
2258
|
+
let showingCachedRecordings = false;
|
|
2259
|
+
if (resetRecordings && fetchCachedRecordings) {
|
|
2260
|
+
try {
|
|
2261
|
+
const cached2 = await fetchCachedRecordings({ limit: RECORDINGS_PAGE_SIZE });
|
|
2262
|
+
if (cached2.items.length > 0) {
|
|
2263
|
+
showingCachedRecordings = true;
|
|
2264
|
+
setRecordings(cached2.items);
|
|
2265
|
+
setRecordingsNextCursor(cached2.nextCursor ?? null);
|
|
2266
|
+
setRecordingsTotalCount(cached2.totalCount);
|
|
2267
|
+
setLoaded(true);
|
|
2268
|
+
setRevalidatingRecordings(Boolean(fetchRecordings));
|
|
2269
|
+
}
|
|
2270
|
+
} catch {
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2248
2273
|
const [jobsR, recR, statsR, accountR] = await Promise.allSettled([
|
|
2249
2274
|
fetchJobs(),
|
|
2250
2275
|
resetRecordings && fetchRecordings ? fetchRecordings({ limit: RECORDINGS_PAGE_SIZE }) : Promise.resolve(void 0),
|
|
@@ -2259,9 +2284,15 @@ function AppShell({
|
|
|
2259
2284
|
setLoadError(jobsR.reason instanceof Error ? jobsR.reason.message : String(jobsR.reason));
|
|
2260
2285
|
}
|
|
2261
2286
|
if (recR.status === "fulfilled" && recR.value) {
|
|
2262
|
-
|
|
2287
|
+
const prevSelectedId = screenRef.current.kind === "overview" ? recordingsRef.current[selectedRef.current]?.recordingId : void 0;
|
|
2288
|
+
const freshItems = recR.value.items;
|
|
2289
|
+
setRecordings(freshItems);
|
|
2263
2290
|
setRecordingsNextCursor(recR.value.nextCursor ?? null);
|
|
2264
2291
|
setRecordingsTotalCount(recR.value.totalCount);
|
|
2292
|
+
if (prevSelectedId) {
|
|
2293
|
+
const idx = freshItems.findIndex((r) => r.recordingId === prevSelectedId);
|
|
2294
|
+
setSelected((s) => idx >= 0 ? idx : Math.min(s, Math.max(0, freshItems.length - 1)));
|
|
2295
|
+
}
|
|
2265
2296
|
}
|
|
2266
2297
|
if (statsR.status === "fulfilled" && statsR.value) setStats(statsR.value);
|
|
2267
2298
|
if (accountR.status === "fulfilled") {
|
|
@@ -2270,7 +2301,8 @@ function AppShell({
|
|
|
2270
2301
|
setAccountStatus("error");
|
|
2271
2302
|
}
|
|
2272
2303
|
setLoaded(true);
|
|
2273
|
-
|
|
2304
|
+
if (showingCachedRecordings) setRevalidatingRecordings(false);
|
|
2305
|
+
}, [fetchJobs, fetchRecordings, fetchCachedRecordings, fetchDashboardStats, fetchAccountStatus]);
|
|
2274
2306
|
const transcribeStoppedRecording = useCallback(async () => {
|
|
2275
2307
|
const current = liveRecord;
|
|
2276
2308
|
if (current?.kind !== "stopped") return;
|
|
@@ -2933,7 +2965,8 @@ function AppShell({
|
|
|
2933
2965
|
peekItem: recordings[selected],
|
|
2934
2966
|
peekSummary,
|
|
2935
2967
|
showPeek,
|
|
2936
|
-
peekWidth
|
|
2968
|
+
peekWidth,
|
|
2969
|
+
revalidating: revalidatingRecordings
|
|
2937
2970
|
}
|
|
2938
2971
|
);
|
|
2939
2972
|
} else if (screen.kind === "account") {
|
|
@@ -3051,6 +3084,7 @@ async function runDashboard(deps) {
|
|
|
3051
3084
|
fetchJobs: deps.fetchJobs,
|
|
3052
3085
|
fetchTranscript: deps.fetchTranscript,
|
|
3053
3086
|
fetchRecordings: deps.fetchRecordings,
|
|
3087
|
+
fetchCachedRecordings: deps.fetchCachedRecordings,
|
|
3054
3088
|
fetchDashboardStats: deps.fetchDashboardStats,
|
|
3055
3089
|
fetchAccountStatus: deps.fetchAccountStatus,
|
|
3056
3090
|
recordingAudio: deps.recordingAudio,
|
|
@@ -3859,10 +3893,10 @@ function mergeDefs(...defs) {
|
|
|
3859
3893
|
function cloneDef(schema) {
|
|
3860
3894
|
return mergeDefs(schema._zod.def);
|
|
3861
3895
|
}
|
|
3862
|
-
function getElementAtPath(obj,
|
|
3863
|
-
if (!
|
|
3896
|
+
function getElementAtPath(obj, path8) {
|
|
3897
|
+
if (!path8)
|
|
3864
3898
|
return obj;
|
|
3865
|
-
return
|
|
3899
|
+
return path8.reduce((acc, key) => acc?.[key], obj);
|
|
3866
3900
|
}
|
|
3867
3901
|
function promiseAllObject(promisesObj) {
|
|
3868
3902
|
const keys = Object.keys(promisesObj);
|
|
@@ -4271,11 +4305,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
4271
4305
|
}
|
|
4272
4306
|
return false;
|
|
4273
4307
|
}
|
|
4274
|
-
function prefixIssues(
|
|
4308
|
+
function prefixIssues(path8, issues) {
|
|
4275
4309
|
return issues.map((iss) => {
|
|
4276
4310
|
var _a3;
|
|
4277
4311
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
4278
|
-
iss.path.unshift(
|
|
4312
|
+
iss.path.unshift(path8);
|
|
4279
4313
|
return iss;
|
|
4280
4314
|
});
|
|
4281
4315
|
}
|
|
@@ -4422,16 +4456,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
4422
4456
|
}
|
|
4423
4457
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
4424
4458
|
const fieldErrors = { _errors: [] };
|
|
4425
|
-
const processError = (error52,
|
|
4459
|
+
const processError = (error52, path8 = []) => {
|
|
4426
4460
|
for (const issue2 of error52.issues) {
|
|
4427
4461
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
4428
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
4462
|
+
issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
|
|
4429
4463
|
} else if (issue2.code === "invalid_key") {
|
|
4430
|
-
processError({ issues: issue2.issues }, [...
|
|
4464
|
+
processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
|
|
4431
4465
|
} else if (issue2.code === "invalid_element") {
|
|
4432
|
-
processError({ issues: issue2.issues }, [...
|
|
4466
|
+
processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
|
|
4433
4467
|
} else {
|
|
4434
|
-
const fullpath = [...
|
|
4468
|
+
const fullpath = [...path8, ...issue2.path];
|
|
4435
4469
|
if (fullpath.length === 0) {
|
|
4436
4470
|
fieldErrors._errors.push(mapper(issue2));
|
|
4437
4471
|
} else {
|
|
@@ -4458,17 +4492,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
4458
4492
|
}
|
|
4459
4493
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
4460
4494
|
const result = { errors: [] };
|
|
4461
|
-
const processError = (error52,
|
|
4495
|
+
const processError = (error52, path8 = []) => {
|
|
4462
4496
|
var _a3, _b;
|
|
4463
4497
|
for (const issue2 of error52.issues) {
|
|
4464
4498
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
4465
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
4499
|
+
issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
|
|
4466
4500
|
} else if (issue2.code === "invalid_key") {
|
|
4467
|
-
processError({ issues: issue2.issues }, [...
|
|
4501
|
+
processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
|
|
4468
4502
|
} else if (issue2.code === "invalid_element") {
|
|
4469
|
-
processError({ issues: issue2.issues }, [...
|
|
4503
|
+
processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
|
|
4470
4504
|
} else {
|
|
4471
|
-
const fullpath = [...
|
|
4505
|
+
const fullpath = [...path8, ...issue2.path];
|
|
4472
4506
|
if (fullpath.length === 0) {
|
|
4473
4507
|
result.errors.push(mapper(issue2));
|
|
4474
4508
|
continue;
|
|
@@ -4500,8 +4534,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
4500
4534
|
}
|
|
4501
4535
|
function toDotPath(_path) {
|
|
4502
4536
|
const segs = [];
|
|
4503
|
-
const
|
|
4504
|
-
for (const seg of
|
|
4537
|
+
const path8 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
4538
|
+
for (const seg of path8) {
|
|
4505
4539
|
if (typeof seg === "number")
|
|
4506
4540
|
segs.push(`[${seg}]`);
|
|
4507
4541
|
else if (typeof seg === "symbol")
|
|
@@ -17193,13 +17227,13 @@ function resolveRef(ref, ctx) {
|
|
|
17193
17227
|
if (!ref.startsWith("#")) {
|
|
17194
17228
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
17195
17229
|
}
|
|
17196
|
-
const
|
|
17197
|
-
if (
|
|
17230
|
+
const path8 = ref.slice(1).split("/").filter(Boolean);
|
|
17231
|
+
if (path8.length === 0) {
|
|
17198
17232
|
return ctx.rootSchema;
|
|
17199
17233
|
}
|
|
17200
17234
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
17201
|
-
if (
|
|
17202
|
-
const key =
|
|
17235
|
+
if (path8[0] === defsKey) {
|
|
17236
|
+
const key = path8[1];
|
|
17203
17237
|
if (!key || !ctx.defs[key]) {
|
|
17204
17238
|
throw new Error(`Reference not found: ${ref}`);
|
|
17205
17239
|
}
|
|
@@ -18884,6 +18918,9 @@ function defaultStorePath(homeDir = os3.homedir(), env = process.env) {
|
|
|
18884
18918
|
const dataHome = env.XDG_DATA_HOME?.trim() || path3.join(homeDir, ".local", "share");
|
|
18885
18919
|
return path3.join(dataHome, "recappi", "cli-state.sqlite");
|
|
18886
18920
|
}
|
|
18921
|
+
function normalizeAccountStamp(input) {
|
|
18922
|
+
return parseAccountPartition(input, "lenient");
|
|
18923
|
+
}
|
|
18887
18924
|
function requireAccountPartition(input) {
|
|
18888
18925
|
const account = parseAccountPartition(input, "strict");
|
|
18889
18926
|
if (!account) {
|
|
@@ -18900,6 +18937,30 @@ function requireAccountPartition(input) {
|
|
|
18900
18937
|
function openCliStore(opts = {}) {
|
|
18901
18938
|
return new CliLocalStore(opts);
|
|
18902
18939
|
}
|
|
18940
|
+
function isLocalStoreUnavailableError(error51) {
|
|
18941
|
+
return typeof error51 === "object" && error51 !== null && error51.localStoreUnavailable === true;
|
|
18942
|
+
}
|
|
18943
|
+
var warnedLocalStoreUnavailable = false;
|
|
18944
|
+
function tryOpenCliStore(opts = {}) {
|
|
18945
|
+
const { onUnavailable, open = openCliStore, ...storeOpts } = opts;
|
|
18946
|
+
try {
|
|
18947
|
+
return open(storeOpts);
|
|
18948
|
+
} catch (error51) {
|
|
18949
|
+
if (isLocalStoreUnavailableError(error51)) {
|
|
18950
|
+
if (!warnedLocalStoreUnavailable) {
|
|
18951
|
+
warnedLocalStoreUnavailable = true;
|
|
18952
|
+
(onUnavailable ?? defaultWarnLocalStoreUnavailable)();
|
|
18953
|
+
}
|
|
18954
|
+
return null;
|
|
18955
|
+
}
|
|
18956
|
+
throw error51;
|
|
18957
|
+
}
|
|
18958
|
+
}
|
|
18959
|
+
function defaultWarnLocalStoreUnavailable() {
|
|
18960
|
+
process.stderr.write(
|
|
18961
|
+
"Local cache unavailable (needs Node.js 22+ for node:sqlite). Cloud commands work normally.\n"
|
|
18962
|
+
);
|
|
18963
|
+
}
|
|
18903
18964
|
var CliLocalStore = class {
|
|
18904
18965
|
db;
|
|
18905
18966
|
now;
|
|
@@ -19195,9 +19256,11 @@ function suppressNodeSqliteWarning(run) {
|
|
|
19195
19256
|
return run();
|
|
19196
19257
|
} catch (error51) {
|
|
19197
19258
|
if (isMissingNodeSqlite(error51)) {
|
|
19198
|
-
|
|
19259
|
+
const err = cliError("internal.unexpected", "Local SQLite store requires Node.js 22 or newer.", {
|
|
19199
19260
|
hint: "Upgrade Node.js, then retry. Cloud-only commands that do not touch local state can still run."
|
|
19200
19261
|
});
|
|
19262
|
+
err.localStoreUnavailable = true;
|
|
19263
|
+
throw err;
|
|
19201
19264
|
}
|
|
19202
19265
|
throw error51;
|
|
19203
19266
|
} finally {
|
|
@@ -19524,21 +19587,23 @@ var RecappiApiClient = class {
|
|
|
19524
19587
|
let accountScopedArtifacts = 0;
|
|
19525
19588
|
let unattributedArtifacts = 0;
|
|
19526
19589
|
if (status.loggedIn && status.userId) {
|
|
19527
|
-
const store =
|
|
19590
|
+
const store = tryOpenCliStore({
|
|
19528
19591
|
dbPath: storePath,
|
|
19529
19592
|
env: this.env,
|
|
19530
19593
|
homeDir: this.homeDir
|
|
19531
19594
|
});
|
|
19532
|
-
|
|
19533
|
-
|
|
19534
|
-
|
|
19535
|
-
|
|
19536
|
-
|
|
19537
|
-
|
|
19538
|
-
|
|
19539
|
-
|
|
19540
|
-
|
|
19541
|
-
|
|
19595
|
+
if (store) {
|
|
19596
|
+
try {
|
|
19597
|
+
const account = requireAccountPartition({
|
|
19598
|
+
backendOrigin: this.auth.origin,
|
|
19599
|
+
userId: status.userId
|
|
19600
|
+
});
|
|
19601
|
+
store.recordAccountSeen(account, status.email);
|
|
19602
|
+
accountScopedArtifacts = store.listLocalArtifactsForAccount(account).length;
|
|
19603
|
+
unattributedArtifacts = store.listUnattributedLocalArtifacts().length;
|
|
19604
|
+
} finally {
|
|
19605
|
+
store.close();
|
|
19606
|
+
}
|
|
19542
19607
|
}
|
|
19543
19608
|
}
|
|
19544
19609
|
const billing = status.loggedIn ? await this.billingStatus() : void 0;
|
|
@@ -19791,12 +19856,12 @@ var RecappiApiClient = class {
|
|
|
19791
19856
|
} : {}
|
|
19792
19857
|
};
|
|
19793
19858
|
}
|
|
19794
|
-
async getJson(
|
|
19795
|
-
const response = await this.request("GET",
|
|
19859
|
+
async getJson(path8) {
|
|
19860
|
+
const response = await this.request("GET", path8);
|
|
19796
19861
|
return await parseJson(response);
|
|
19797
19862
|
}
|
|
19798
|
-
async postJson(
|
|
19799
|
-
const response = await this.request("POST",
|
|
19863
|
+
async postJson(path8, body) {
|
|
19864
|
+
const response = await this.request("POST", path8, JSON.stringify(body), {
|
|
19800
19865
|
headers: { "content-type": "application/json" }
|
|
19801
19866
|
});
|
|
19802
19867
|
return await parseJson(response);
|
|
@@ -20187,8 +20252,11 @@ import { promises as fs4 } from "fs";
|
|
|
20187
20252
|
import path5 from "path";
|
|
20188
20253
|
function createRecordingAudioRuntime(client, deps = {}) {
|
|
20189
20254
|
const downloadRecordingAudioFile = async (recordingId, opts) => {
|
|
20190
|
-
const cached2 =
|
|
20191
|
-
if (cached2)
|
|
20255
|
+
const cached2 = await findReusableDownload(recordingId, deps);
|
|
20256
|
+
if (cached2) {
|
|
20257
|
+
if (!opts?.directory) return cached2;
|
|
20258
|
+
return materializeCachedDownload(recordingId, cached2, opts, deps);
|
|
20259
|
+
}
|
|
20192
20260
|
const directory = opts?.directory ?? (deps.account ? defaultDownloadDirectory(deps) : void 0);
|
|
20193
20261
|
const download = await client.downloadRecordingAudio(recordingId, {
|
|
20194
20262
|
...opts,
|
|
@@ -20216,6 +20284,38 @@ function createRecordingAudioRuntime(client, deps = {}) {
|
|
|
20216
20284
|
)
|
|
20217
20285
|
};
|
|
20218
20286
|
}
|
|
20287
|
+
async function materializeCachedDownload(recordingId, cached2, opts, deps) {
|
|
20288
|
+
if (!opts.directory) return cached2;
|
|
20289
|
+
await fs4.mkdir(opts.directory, { recursive: true });
|
|
20290
|
+
const contentType = cached2.contentType ?? contentTypeForAudioPath(cached2.localPath);
|
|
20291
|
+
const targetPath = path5.join(
|
|
20292
|
+
opts.directory,
|
|
20293
|
+
recordingAudioFileName(recordingId, opts.title, contentType, opts.filenameStem)
|
|
20294
|
+
);
|
|
20295
|
+
if (path5.resolve(cached2.localPath) !== path5.resolve(targetPath)) {
|
|
20296
|
+
await fs4.copyFile(cached2.localPath, targetPath);
|
|
20297
|
+
}
|
|
20298
|
+
const contentLength = cached2.contentLength ?? (await fs4.stat(targetPath)).size;
|
|
20299
|
+
const artifact = await rememberDownload(
|
|
20300
|
+
{
|
|
20301
|
+
recordingId,
|
|
20302
|
+
localPath: targetPath,
|
|
20303
|
+
contentType,
|
|
20304
|
+
contentLength,
|
|
20305
|
+
origin: cached2.origin ?? ""
|
|
20306
|
+
},
|
|
20307
|
+
deps
|
|
20308
|
+
);
|
|
20309
|
+
return {
|
|
20310
|
+
recordingId,
|
|
20311
|
+
localPath: targetPath,
|
|
20312
|
+
reused: true,
|
|
20313
|
+
...artifact ? { artifactId: artifact.id } : {},
|
|
20314
|
+
contentType,
|
|
20315
|
+
contentLength,
|
|
20316
|
+
...cached2.origin ? { origin: cached2.origin } : {}
|
|
20317
|
+
};
|
|
20318
|
+
}
|
|
20219
20319
|
function openPath(localPath, deps = {}) {
|
|
20220
20320
|
return runMacOpen([localPath], deps);
|
|
20221
20321
|
}
|
|
@@ -20232,7 +20332,7 @@ async function findReusableDownload(recordingId, deps) {
|
|
|
20232
20332
|
if (!artifact || !await isReadableFile(artifact.localPath)) return null;
|
|
20233
20333
|
const opened = store.markLocalArtifactOpened(artifact.id);
|
|
20234
20334
|
return artifactToDownload(opened, recordingId);
|
|
20235
|
-
});
|
|
20335
|
+
}, null);
|
|
20236
20336
|
}
|
|
20237
20337
|
async function rememberDownload(download, deps) {
|
|
20238
20338
|
return withStore(deps, (store, account) => {
|
|
@@ -20246,16 +20346,17 @@ async function rememberDownload(download, deps) {
|
|
|
20246
20346
|
resource: "recording_audio",
|
|
20247
20347
|
contentType: download.contentType,
|
|
20248
20348
|
...download.contentLength !== void 0 ? { contentLength: download.contentLength } : {},
|
|
20249
|
-
origin: download.origin
|
|
20349
|
+
...download.origin ? { origin: download.origin } : {}
|
|
20250
20350
|
}
|
|
20251
20351
|
});
|
|
20252
20352
|
return store.markLocalArtifactOpened(artifact.id);
|
|
20253
|
-
});
|
|
20353
|
+
}, null);
|
|
20254
20354
|
}
|
|
20255
20355
|
async function listExistingDownloads(deps) {
|
|
20256
20356
|
const artifacts = await withStore(
|
|
20257
20357
|
deps,
|
|
20258
|
-
(store, account) => account ? store.listLocalArtifactsForAccount(account, { kind: "download" }) : []
|
|
20358
|
+
(store, account) => account ? store.listLocalArtifactsForAccount(account, { kind: "download" }) : [],
|
|
20359
|
+
[]
|
|
20259
20360
|
);
|
|
20260
20361
|
const existing = [];
|
|
20261
20362
|
for (const artifact of artifacts) {
|
|
@@ -20263,8 +20364,9 @@ async function listExistingDownloads(deps) {
|
|
|
20263
20364
|
}
|
|
20264
20365
|
return existing;
|
|
20265
20366
|
}
|
|
20266
|
-
async function withStore(deps, run) {
|
|
20267
|
-
const store = deps.store ??
|
|
20367
|
+
async function withStore(deps, run, fallback) {
|
|
20368
|
+
const store = deps.store ?? tryOpenCliStore({ homeDir: deps.homeDir, env: deps.env });
|
|
20369
|
+
if (!store) return fallback;
|
|
20268
20370
|
try {
|
|
20269
20371
|
return await run(store, deps.account ?? null);
|
|
20270
20372
|
} finally {
|
|
@@ -20274,6 +20376,27 @@ async function withStore(deps, run) {
|
|
|
20274
20376
|
function defaultDownloadDirectory(deps) {
|
|
20275
20377
|
return path5.join(path5.dirname(defaultStorePath(deps.homeDir, deps.env)), "downloads");
|
|
20276
20378
|
}
|
|
20379
|
+
function contentTypeForAudioPath(localPath) {
|
|
20380
|
+
switch (path5.extname(localPath).toLowerCase()) {
|
|
20381
|
+
case ".mp3":
|
|
20382
|
+
return "audio/mpeg";
|
|
20383
|
+
case ".m4a":
|
|
20384
|
+
case ".mp4":
|
|
20385
|
+
return "audio/m4a";
|
|
20386
|
+
case ".ogg":
|
|
20387
|
+
return "audio/ogg";
|
|
20388
|
+
case ".flac":
|
|
20389
|
+
return "audio/flac";
|
|
20390
|
+
case ".webm":
|
|
20391
|
+
return "audio/webm";
|
|
20392
|
+
case ".aiff":
|
|
20393
|
+
case ".aif":
|
|
20394
|
+
return "audio/aiff";
|
|
20395
|
+
case ".wav":
|
|
20396
|
+
default:
|
|
20397
|
+
return "audio/wav";
|
|
20398
|
+
}
|
|
20399
|
+
}
|
|
20277
20400
|
async function isReadableFile(localPath) {
|
|
20278
20401
|
try {
|
|
20279
20402
|
return (await fs4.stat(localPath)).isFile();
|
|
@@ -20553,41 +20676,171 @@ ${lines.join("\n")}
|
|
|
20553
20676
|
}
|
|
20554
20677
|
|
|
20555
20678
|
// src/export.ts
|
|
20556
|
-
import { promises as
|
|
20679
|
+
import { promises as fs6 } from "fs";
|
|
20557
20680
|
import os5 from "os";
|
|
20681
|
+
import path7 from "path";
|
|
20682
|
+
|
|
20683
|
+
// src/recordingSessionCache.ts
|
|
20684
|
+
import { promises as fs5 } from "fs";
|
|
20558
20685
|
import path6 from "path";
|
|
20686
|
+
function accountFromStatus(status) {
|
|
20687
|
+
if (!status.loggedIn || !status.userId) return null;
|
|
20688
|
+
return normalizeAccountStamp({ backendOrigin: status.origin, userId: status.userId });
|
|
20689
|
+
}
|
|
20690
|
+
async function findIndexedRecordingSessionDir(recordingId, deps) {
|
|
20691
|
+
return withSessionStore(deps, async (store, account) => {
|
|
20692
|
+
if (!account) return void 0;
|
|
20693
|
+
const artifact = store.findLocalArtifactForAccount(account, {
|
|
20694
|
+
kind: "recording_session",
|
|
20695
|
+
remoteId: recordingId
|
|
20696
|
+
});
|
|
20697
|
+
if (!artifact) return void 0;
|
|
20698
|
+
if (!await isReadableDirectory(artifact.localPath)) return void 0;
|
|
20699
|
+
const manifest = await readJsonRecord(path6.join(artifact.localPath, "remote-session.json"));
|
|
20700
|
+
if (manifest?.recordingId && manifest.recordingId !== recordingId) return void 0;
|
|
20701
|
+
return artifact.localPath;
|
|
20702
|
+
}, void 0);
|
|
20703
|
+
}
|
|
20704
|
+
async function rememberRecordingSession(input, deps) {
|
|
20705
|
+
return withSessionStore(deps, (store, account) => {
|
|
20706
|
+
if (!account) return null;
|
|
20707
|
+
return store.upsertLocalArtifact({
|
|
20708
|
+
kind: "recording_session",
|
|
20709
|
+
account,
|
|
20710
|
+
remoteId: input.recording.recordingId,
|
|
20711
|
+
localPath: input.sessionDir,
|
|
20712
|
+
metadata: stripUndefined({
|
|
20713
|
+
resource: "recording_session",
|
|
20714
|
+
source: input.source,
|
|
20715
|
+
sessionDir: input.sessionDir,
|
|
20716
|
+
recording: input.recording,
|
|
20717
|
+
files: stripUndefined({ ...input.files }),
|
|
20718
|
+
transcriptId: input.transcriptId,
|
|
20719
|
+
summaryStatus: input.summaryStatus,
|
|
20720
|
+
uploadFilename: input.uploadFilename,
|
|
20721
|
+
origin: input.recording.origin,
|
|
20722
|
+
indexedAt: (input.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
20723
|
+
})
|
|
20724
|
+
});
|
|
20725
|
+
}, null);
|
|
20726
|
+
}
|
|
20727
|
+
async function listCachedRecordingSessions(deps) {
|
|
20728
|
+
const limit = deps.limit ?? 50;
|
|
20729
|
+
const items = await withSessionStore(deps, async (store, account) => {
|
|
20730
|
+
if (!account) return [];
|
|
20731
|
+
const artifacts = store.listLocalArtifactsForAccount(account, { kind: "recording_session" });
|
|
20732
|
+
const recordings = [];
|
|
20733
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20734
|
+
for (const artifact of artifacts) {
|
|
20735
|
+
if (recordings.length >= limit) break;
|
|
20736
|
+
const recording = await recordingFromArtifact(artifact);
|
|
20737
|
+
if (!recording || seen.has(recording.recordingId)) continue;
|
|
20738
|
+
if (!await isReadableDirectory(artifact.localPath)) continue;
|
|
20739
|
+
seen.add(recording.recordingId);
|
|
20740
|
+
recordings.push(recording);
|
|
20741
|
+
}
|
|
20742
|
+
return recordings.sort((a, b) => b.createdAt - a.createdAt || b.updatedAt - a.updatedAt);
|
|
20743
|
+
}, []);
|
|
20744
|
+
return recordingListDataSchema.parse({
|
|
20745
|
+
items,
|
|
20746
|
+
limit,
|
|
20747
|
+
nextCursor: null,
|
|
20748
|
+
totalCount: items.length,
|
|
20749
|
+
origin: deps.origin
|
|
20750
|
+
});
|
|
20751
|
+
}
|
|
20752
|
+
async function recordingFromArtifact(artifact) {
|
|
20753
|
+
const metadata = isRecord4(artifact.metadata) ? artifact.metadata : {};
|
|
20754
|
+
const cached2 = recordingDataSchema.safeParse(metadata.recording);
|
|
20755
|
+
if (cached2.success) return cached2.data;
|
|
20756
|
+
const fromFile = await readJsonRecord(path6.join(artifact.localPath, "recording.json"));
|
|
20757
|
+
const parsed = recordingDataSchema.safeParse(fromFile);
|
|
20758
|
+
return parsed.success ? parsed.data : null;
|
|
20759
|
+
}
|
|
20760
|
+
async function withSessionStore(deps, run, fallback) {
|
|
20761
|
+
const store = deps.store ?? tryOpenCliStore({ homeDir: deps.homeDir, env: deps.env });
|
|
20762
|
+
if (!store) return fallback;
|
|
20763
|
+
try {
|
|
20764
|
+
return await run(store, deps.account ?? null);
|
|
20765
|
+
} finally {
|
|
20766
|
+
if (!deps.store) store.close();
|
|
20767
|
+
}
|
|
20768
|
+
}
|
|
20769
|
+
async function readJsonRecord(filePath) {
|
|
20770
|
+
try {
|
|
20771
|
+
const parsed = JSON.parse(await fs5.readFile(filePath, "utf8"));
|
|
20772
|
+
return isRecord4(parsed) ? parsed : void 0;
|
|
20773
|
+
} catch {
|
|
20774
|
+
return void 0;
|
|
20775
|
+
}
|
|
20776
|
+
}
|
|
20777
|
+
async function isReadableDirectory(localPath) {
|
|
20778
|
+
try {
|
|
20779
|
+
return (await fs5.stat(localPath)).isDirectory();
|
|
20780
|
+
} catch {
|
|
20781
|
+
return false;
|
|
20782
|
+
}
|
|
20783
|
+
}
|
|
20784
|
+
function stripUndefined(value) {
|
|
20785
|
+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
|
|
20786
|
+
}
|
|
20787
|
+
function isRecord4(value) {
|
|
20788
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20789
|
+
}
|
|
20790
|
+
|
|
20791
|
+
// src/export.ts
|
|
20559
20792
|
async function syncRecordingText(opts) {
|
|
20560
20793
|
const context = await loadRecordingBundleContext(opts);
|
|
20561
|
-
const
|
|
20794
|
+
const account = accountFromStatus(context.subscription);
|
|
20795
|
+
const sessionDir = await resolveRecordingSessionDir(context.recording, opts, account);
|
|
20562
20796
|
return writeRecordingTextFiles(context, sessionDir, {
|
|
20563
|
-
now: opts.now
|
|
20797
|
+
now: opts.now,
|
|
20798
|
+
store: opts.store,
|
|
20799
|
+
env: opts.env,
|
|
20800
|
+
homeDir: opts.homeDir,
|
|
20801
|
+
account,
|
|
20802
|
+
source: "text_sync"
|
|
20564
20803
|
});
|
|
20565
20804
|
}
|
|
20566
20805
|
async function syncRecordingAudio(opts) {
|
|
20567
20806
|
const context = await loadRecordingBundleContext(opts);
|
|
20568
|
-
const
|
|
20807
|
+
const account = accountFromStatus(context.subscription);
|
|
20808
|
+
const sessionDir = await resolveRecordingSessionDir(context.recording, opts, account);
|
|
20569
20809
|
const audio = await downloadRecordingAudioToDir(context.recording, opts.recordingAudio, sessionDir);
|
|
20570
20810
|
const text = await writeRecordingTextFiles(context, sessionDir, {
|
|
20571
20811
|
now: opts.now,
|
|
20572
|
-
uploadFilename:
|
|
20812
|
+
uploadFilename: path7.basename(audio.localPath),
|
|
20813
|
+
store: opts.store,
|
|
20814
|
+
env: opts.env,
|
|
20815
|
+
homeDir: opts.homeDir,
|
|
20816
|
+
account,
|
|
20817
|
+
source: "audio_sync",
|
|
20818
|
+
audioPath: audio.localPath
|
|
20573
20819
|
});
|
|
20574
20820
|
return { ...text, audioPath: audio.localPath, audio: audioMetadata(audio) };
|
|
20575
20821
|
}
|
|
20576
20822
|
async function exportRecording(opts) {
|
|
20577
20823
|
const context = await loadRecordingBundleContext(opts);
|
|
20578
|
-
const
|
|
20824
|
+
const account = accountFromStatus(context.subscription);
|
|
20825
|
+
const exportDir = await resolveRecordingSessionDir(context.recording, opts, account);
|
|
20579
20826
|
const audio = await downloadRecordingAudioToDir(context.recording, opts.recordingAudio, exportDir);
|
|
20580
20827
|
const textFiles = await writeRecordingTextFiles(context, exportDir, {
|
|
20581
20828
|
now: opts.now,
|
|
20582
|
-
uploadFilename:
|
|
20829
|
+
uploadFilename: path7.basename(audio.localPath),
|
|
20830
|
+
store: opts.store,
|
|
20831
|
+
env: opts.env,
|
|
20832
|
+
homeDir: opts.homeDir,
|
|
20833
|
+
account,
|
|
20834
|
+
source: "export",
|
|
20835
|
+
audioPath: audio.localPath
|
|
20583
20836
|
});
|
|
20584
20837
|
const { recording, subscription, transcript } = context;
|
|
20585
|
-
const subscriptionPath =
|
|
20586
|
-
const subscriptionJsonPath =
|
|
20587
|
-
await
|
|
20838
|
+
const subscriptionPath = path7.join(exportDir, "subscription.md");
|
|
20839
|
+
const subscriptionJsonPath = path7.join(exportDir, "subscription.json");
|
|
20840
|
+
await fs6.writeFile(subscriptionPath, renderSubscriptionMarkdown(subscription), "utf8");
|
|
20588
20841
|
await writeJson(subscriptionJsonPath, subscription);
|
|
20589
|
-
const textPath =
|
|
20590
|
-
const manifestPath =
|
|
20842
|
+
const textPath = path7.join(exportDir, "handoff.md");
|
|
20843
|
+
const manifestPath = path7.join(exportDir, "manifest.json");
|
|
20591
20844
|
const data = recordingExportDataSchema.parse({
|
|
20592
20845
|
origin: recording.origin,
|
|
20593
20846
|
recordingId: recording.recordingId,
|
|
@@ -20609,7 +20862,7 @@ async function exportRecording(opts) {
|
|
|
20609
20862
|
...textFiles.summaryStatus ? { summaryStatus: textFiles.summaryStatus } : {},
|
|
20610
20863
|
audio: audioMetadata(audio)
|
|
20611
20864
|
});
|
|
20612
|
-
await
|
|
20865
|
+
await fs6.writeFile(
|
|
20613
20866
|
textPath,
|
|
20614
20867
|
renderHandoffMarkdown(recording, subscription, audio, data, transcript),
|
|
20615
20868
|
"utf8"
|
|
@@ -20619,6 +20872,33 @@ async function exportRecording(opts) {
|
|
|
20619
20872
|
command: "recordings export",
|
|
20620
20873
|
data
|
|
20621
20874
|
});
|
|
20875
|
+
await rememberRecordingSession(
|
|
20876
|
+
{
|
|
20877
|
+
recording,
|
|
20878
|
+
sessionDir: exportDir,
|
|
20879
|
+
files: {
|
|
20880
|
+
remoteManifestPath: data.remoteManifestPath,
|
|
20881
|
+
sessionMetadataPath: data.sessionMetadataPath,
|
|
20882
|
+
recordingJsonPath: data.recordingJsonPath,
|
|
20883
|
+
...data.transcriptPath ? { transcriptPath: data.transcriptPath } : {},
|
|
20884
|
+
...data.transcriptJsonPath ? { transcriptJsonPath: data.transcriptJsonPath } : {},
|
|
20885
|
+
...data.summaryPath ? { summaryPath: data.summaryPath } : {},
|
|
20886
|
+
...data.summaryJsonPath ? { summaryJsonPath: data.summaryJsonPath } : {},
|
|
20887
|
+
...data.actionItemsPath ? { actionItemsPath: data.actionItemsPath } : {},
|
|
20888
|
+
audioPath: data.audioPath,
|
|
20889
|
+
subscriptionPath: data.subscriptionPath,
|
|
20890
|
+
subscriptionJsonPath: data.subscriptionJsonPath,
|
|
20891
|
+
textPath: data.textPath,
|
|
20892
|
+
manifestPath: data.manifestPath
|
|
20893
|
+
},
|
|
20894
|
+
...data.transcriptId !== void 0 ? { transcriptId: data.transcriptId } : {},
|
|
20895
|
+
...data.summaryStatus ? { summaryStatus: data.summaryStatus } : {},
|
|
20896
|
+
uploadFilename: path7.basename(audio.localPath),
|
|
20897
|
+
source: "export",
|
|
20898
|
+
now: opts.now
|
|
20899
|
+
},
|
|
20900
|
+
{ account, store: opts.store, env: opts.env, homeDir: opts.homeDir }
|
|
20901
|
+
);
|
|
20622
20902
|
return data;
|
|
20623
20903
|
}
|
|
20624
20904
|
async function loadRecordingBundleContext(opts) {
|
|
@@ -20641,11 +20921,11 @@ async function downloadRecordingAudioToDir(recording, recordingAudio, directory)
|
|
|
20641
20921
|
}
|
|
20642
20922
|
async function writeRecordingTextFiles(context, sessionDir, opts) {
|
|
20643
20923
|
const { recording, transcript, subscription } = context;
|
|
20644
|
-
await
|
|
20645
|
-
const recordingJsonPath =
|
|
20646
|
-
const sessionMetadataPath =
|
|
20647
|
-
const remoteManifestPath =
|
|
20648
|
-
const existingManifest = await
|
|
20924
|
+
await fs6.mkdir(sessionDir, { recursive: true });
|
|
20925
|
+
const recordingJsonPath = path7.join(sessionDir, "recording.json");
|
|
20926
|
+
const sessionMetadataPath = path7.join(sessionDir, "session-metadata.json");
|
|
20927
|
+
const remoteManifestPath = path7.join(sessionDir, "remote-session.json");
|
|
20928
|
+
const existingManifest = await readJsonRecord2(remoteManifestPath);
|
|
20649
20929
|
const uploadFilename = opts.uploadFilename ?? (typeof existingManifest?.uploadFilename === "string" ? existingManifest.uploadFilename : void 0);
|
|
20650
20930
|
await writeJson(recordingJsonPath, recording);
|
|
20651
20931
|
await writeJson(sessionMetadataPath, renderSessionMetadata(recording));
|
|
@@ -20660,19 +20940,23 @@ async function writeRecordingTextFiles(context, sessionDir, opts) {
|
|
|
20660
20940
|
let actionItemsPath;
|
|
20661
20941
|
let summaryStatus;
|
|
20662
20942
|
if (transcript) {
|
|
20663
|
-
transcriptPath =
|
|
20664
|
-
transcriptJsonPath =
|
|
20665
|
-
|
|
20666
|
-
summaryJsonPath = path6.join(sessionDir, "summary.json");
|
|
20667
|
-
actionItemsPath = path6.join(sessionDir, "action-items.md");
|
|
20943
|
+
transcriptPath = path7.join(sessionDir, "transcript.md");
|
|
20944
|
+
transcriptJsonPath = path7.join(sessionDir, "transcript.json");
|
|
20945
|
+
summaryJsonPath = path7.join(sessionDir, "summary.json");
|
|
20668
20946
|
summaryStatus = transcript.summary.status;
|
|
20669
|
-
await
|
|
20947
|
+
await fs6.writeFile(transcriptPath, renderTranscriptMarkdown(transcript), "utf8");
|
|
20670
20948
|
await writeJson(transcriptJsonPath, transcript);
|
|
20671
20949
|
await writeJson(summaryJsonPath, transcript.summary);
|
|
20672
|
-
|
|
20673
|
-
|
|
20950
|
+
const summaryFilePath = path7.join(sessionDir, "summary.md");
|
|
20951
|
+
const summaryMarkdown = renderSummaryMarkdown(transcript);
|
|
20952
|
+
await writeOptionalText(summaryFilePath, summaryMarkdown);
|
|
20953
|
+
if (summaryMarkdown) summaryPath = summaryFilePath;
|
|
20954
|
+
const actionItemsFilePath = path7.join(sessionDir, "action-items.md");
|
|
20955
|
+
const actionItemsMarkdown = renderActionItemsMarkdown(transcript.summary);
|
|
20956
|
+
await writeOptionalText(actionItemsFilePath, actionItemsMarkdown);
|
|
20957
|
+
if (actionItemsMarkdown) actionItemsPath = actionItemsFilePath;
|
|
20674
20958
|
}
|
|
20675
|
-
|
|
20959
|
+
const data = {
|
|
20676
20960
|
recordingId: recording.recordingId,
|
|
20677
20961
|
sessionDir,
|
|
20678
20962
|
remoteManifestPath,
|
|
@@ -20686,10 +20970,56 @@ async function writeRecordingTextFiles(context, sessionDir, opts) {
|
|
|
20686
20970
|
...actionItemsPath ? { actionItemsPath } : {},
|
|
20687
20971
|
...summaryStatus ? { summaryStatus } : {}
|
|
20688
20972
|
};
|
|
20973
|
+
await rememberRecordingSession(
|
|
20974
|
+
{
|
|
20975
|
+
recording,
|
|
20976
|
+
sessionDir,
|
|
20977
|
+
files: {
|
|
20978
|
+
remoteManifestPath,
|
|
20979
|
+
sessionMetadataPath,
|
|
20980
|
+
recordingJsonPath,
|
|
20981
|
+
...transcriptPath ? { transcriptPath } : {},
|
|
20982
|
+
...transcriptJsonPath ? { transcriptJsonPath } : {},
|
|
20983
|
+
...summaryPath ? { summaryPath } : {},
|
|
20984
|
+
...summaryJsonPath ? { summaryJsonPath } : {},
|
|
20985
|
+
...actionItemsPath ? { actionItemsPath } : {},
|
|
20986
|
+
...opts.audioPath ? { audioPath: opts.audioPath } : {}
|
|
20987
|
+
},
|
|
20988
|
+
...context.transcriptId !== void 0 ? { transcriptId: context.transcriptId } : {},
|
|
20989
|
+
...summaryStatus ? { summaryStatus } : {},
|
|
20990
|
+
...uploadFilename ? { uploadFilename } : {},
|
|
20991
|
+
source: opts.source,
|
|
20992
|
+
now: opts.now
|
|
20993
|
+
},
|
|
20994
|
+
{ account: opts.account ?? null, store: opts.store, env: opts.env, homeDir: opts.homeDir }
|
|
20995
|
+
);
|
|
20996
|
+
return data;
|
|
20689
20997
|
}
|
|
20690
|
-
async function resolveRecordingSessionDir(recording, opts) {
|
|
20691
|
-
if (opts.directory) return
|
|
20998
|
+
async function resolveRecordingSessionDir(recording, opts, account) {
|
|
20999
|
+
if (opts.directory) return path7.resolve(opts.directory);
|
|
21000
|
+
const indexed = await findIndexedRecordingSessionDir(recording.recordingId, {
|
|
21001
|
+
account,
|
|
21002
|
+
store: opts.store,
|
|
21003
|
+
homeDir: opts.homeDir,
|
|
21004
|
+
env: opts.env
|
|
21005
|
+
});
|
|
21006
|
+
if (indexed) return indexed;
|
|
20692
21007
|
const existing = await findExistingRecordingSessionDir(recording.recordingId, opts.homeDir, opts.env);
|
|
21008
|
+
if (existing) {
|
|
21009
|
+
await rememberRecordingSession(
|
|
21010
|
+
{
|
|
21011
|
+
recording,
|
|
21012
|
+
sessionDir: existing,
|
|
21013
|
+
files: {
|
|
21014
|
+
remoteManifestPath: path7.join(existing, "remote-session.json"),
|
|
21015
|
+
sessionMetadataPath: path7.join(existing, "session-metadata.json"),
|
|
21016
|
+
recordingJsonPath: path7.join(existing, "recording.json")
|
|
21017
|
+
},
|
|
21018
|
+
source: "manifest_scan"
|
|
21019
|
+
},
|
|
21020
|
+
{ account, store: opts.store, env: opts.env, homeDir: opts.homeDir }
|
|
21021
|
+
);
|
|
21022
|
+
}
|
|
20693
21023
|
if (existing) return existing;
|
|
20694
21024
|
return createRecordingSessionDir(recording, opts.homeDir, opts.env);
|
|
20695
21025
|
}
|
|
@@ -20697,39 +21027,39 @@ async function findExistingRecordingSessionDir(recordingId, homeDir, env) {
|
|
|
20697
21027
|
const base = recordingSessionBaseDirectory(homeDir, env);
|
|
20698
21028
|
let entries;
|
|
20699
21029
|
try {
|
|
20700
|
-
entries = await
|
|
21030
|
+
entries = await fs6.readdir(base, { withFileTypes: true });
|
|
20701
21031
|
} catch {
|
|
20702
21032
|
return void 0;
|
|
20703
21033
|
}
|
|
20704
21034
|
for (const entry of entries) {
|
|
20705
21035
|
if (!entry.isDirectory()) continue;
|
|
20706
|
-
const dir =
|
|
20707
|
-
const manifest = await
|
|
21036
|
+
const dir = path7.join(base, entry.name);
|
|
21037
|
+
const manifest = await readJsonRecord2(path7.join(dir, "remote-session.json"));
|
|
20708
21038
|
if (manifest?.recordingId === recordingId) return dir;
|
|
20709
21039
|
}
|
|
20710
21040
|
return void 0;
|
|
20711
21041
|
}
|
|
20712
21042
|
async function createRecordingSessionDir(recording, homeDir, env) {
|
|
20713
21043
|
const base = recordingSessionBaseDirectory(homeDir, env);
|
|
20714
|
-
await
|
|
21044
|
+
await fs6.mkdir(base, { recursive: true });
|
|
20715
21045
|
const stem = formatSessionDirectoryDate(new Date(recording.createdAt));
|
|
20716
|
-
let candidate =
|
|
21046
|
+
let candidate = path7.join(base, stem);
|
|
20717
21047
|
let suffix = 2;
|
|
20718
21048
|
while (await pathExists(candidate)) {
|
|
20719
|
-
candidate =
|
|
21049
|
+
candidate = path7.join(base, `${stem}-cloud-${suffix}`);
|
|
20720
21050
|
suffix += 1;
|
|
20721
21051
|
}
|
|
20722
|
-
await
|
|
21052
|
+
await fs6.mkdir(candidate, { recursive: true });
|
|
20723
21053
|
return candidate;
|
|
20724
21054
|
}
|
|
20725
21055
|
function recordingSessionBaseDirectory(homeDir = os5.homedir(), env = process.env) {
|
|
20726
21056
|
const explicit = env.RECAPPI_LOCAL_SESSIONS_DIR?.trim();
|
|
20727
21057
|
if (explicit) return explicit;
|
|
20728
|
-
return
|
|
21058
|
+
return path7.join(homeDir, "Documents", "Recappi Mini");
|
|
20729
21059
|
}
|
|
20730
|
-
async function
|
|
21060
|
+
async function readJsonRecord2(filePath) {
|
|
20731
21061
|
try {
|
|
20732
|
-
const parsed = JSON.parse(await
|
|
21062
|
+
const parsed = JSON.parse(await fs6.readFile(filePath, "utf8"));
|
|
20733
21063
|
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : void 0;
|
|
20734
21064
|
} catch {
|
|
20735
21065
|
return void 0;
|
|
@@ -20737,7 +21067,7 @@ async function readJsonRecord(filePath) {
|
|
|
20737
21067
|
}
|
|
20738
21068
|
async function pathExists(filePath) {
|
|
20739
21069
|
try {
|
|
20740
|
-
await
|
|
21070
|
+
await fs6.access(filePath);
|
|
20741
21071
|
return true;
|
|
20742
21072
|
} catch {
|
|
20743
21073
|
return false;
|
|
@@ -20753,15 +21083,15 @@ function formatSessionDirectoryDate(date5) {
|
|
|
20753
21083
|
return `${year}-${month}-${day}_${hour}${minute}${second}`;
|
|
20754
21084
|
}
|
|
20755
21085
|
async function writeJson(filePath, value) {
|
|
20756
|
-
await
|
|
21086
|
+
await fs6.writeFile(filePath, `${JSON.stringify(value, null, 2)}
|
|
20757
21087
|
`, "utf8");
|
|
20758
21088
|
}
|
|
20759
21089
|
async function writeOptionalText(filePath, value) {
|
|
20760
21090
|
if (value && value.trim()) {
|
|
20761
|
-
await
|
|
21091
|
+
await fs6.writeFile(filePath, value, "utf8");
|
|
20762
21092
|
return;
|
|
20763
21093
|
}
|
|
20764
|
-
await
|
|
21094
|
+
await fs6.rm(filePath, { force: true });
|
|
20765
21095
|
}
|
|
20766
21096
|
function renderHandoffMarkdown(recording, subscription, audio, data, transcript) {
|
|
20767
21097
|
const lines = [];
|
|
@@ -20797,9 +21127,12 @@ function renderHandoffMarkdown(recording, subscription, audio, data, transcript)
|
|
|
20797
21127
|
appendSubscriptionLines(lines, subscription);
|
|
20798
21128
|
lines.push("");
|
|
20799
21129
|
if (transcript) {
|
|
20800
|
-
|
|
20801
|
-
|
|
20802
|
-
|
|
21130
|
+
const summaryMarkdown = renderSummaryMarkdown(transcript);
|
|
21131
|
+
if (summaryMarkdown) {
|
|
21132
|
+
lines.push(summaryMarkdown.trimEnd());
|
|
21133
|
+
lines.push("");
|
|
21134
|
+
}
|
|
21135
|
+
lines.push(renderTimestampedTranscriptMarkdown(transcript).trimEnd());
|
|
20803
21136
|
lines.push("");
|
|
20804
21137
|
}
|
|
20805
21138
|
return `${lines.join("\n").trimEnd()}
|
|
@@ -20813,7 +21146,7 @@ function renderSubscriptionMarkdown(subscription) {
|
|
|
20813
21146
|
}
|
|
20814
21147
|
function renderSessionMetadata(recording) {
|
|
20815
21148
|
const sourceTitle = recording.title ?? recording.summaryTitle ?? "Recappi Cloud";
|
|
20816
|
-
return
|
|
21149
|
+
return stripUndefined2({
|
|
20817
21150
|
summaryTitle: recording.summaryTitle ?? recording.title ?? void 0,
|
|
20818
21151
|
sourceTitle,
|
|
20819
21152
|
sourceAppName: void 0,
|
|
@@ -20825,7 +21158,7 @@ function renderSessionMetadata(recording) {
|
|
|
20825
21158
|
});
|
|
20826
21159
|
}
|
|
20827
21160
|
function renderRemoteSessionManifest(recording, uploadFilename, transcript, subscription, now) {
|
|
20828
|
-
return
|
|
21161
|
+
return stripUndefined2({
|
|
20829
21162
|
recordingId: recording.recordingId,
|
|
20830
21163
|
jobId: transcript?.jobId,
|
|
20831
21164
|
transcriptId: transcript?.transcriptId ?? recording.activeTranscriptId ?? void 0,
|
|
@@ -20839,7 +21172,7 @@ function renderRemoteSessionManifest(recording, uploadFilename, transcript, subs
|
|
|
20839
21172
|
accountBackendOrigin: subscription.origin
|
|
20840
21173
|
});
|
|
20841
21174
|
}
|
|
20842
|
-
function
|
|
21175
|
+
function stripUndefined2(value) {
|
|
20843
21176
|
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
|
|
20844
21177
|
}
|
|
20845
21178
|
function appendSubscriptionLines(lines, subscription) {
|
|
@@ -20862,6 +21195,12 @@ function appendSubscriptionLines(lines, subscription) {
|
|
|
20862
21195
|
function renderTranscriptMarkdown(transcript) {
|
|
20863
21196
|
return `# Transcript
|
|
20864
21197
|
|
|
21198
|
+
${transcript.text}
|
|
21199
|
+
`;
|
|
21200
|
+
}
|
|
21201
|
+
function renderTimestampedTranscriptMarkdown(transcript) {
|
|
21202
|
+
return `# Transcript
|
|
21203
|
+
|
|
20865
21204
|
${renderTranscriptLines(transcript)}
|
|
20866
21205
|
`;
|
|
20867
21206
|
}
|
|
@@ -20873,15 +21212,10 @@ function renderTranscriptLines(transcript) {
|
|
|
20873
21212
|
if (lines.length > 0) return lines.join("\n");
|
|
20874
21213
|
return transcript.text;
|
|
20875
21214
|
}
|
|
20876
|
-
function renderSummaryMarkdown(
|
|
21215
|
+
function renderSummaryMarkdown(transcript) {
|
|
20877
21216
|
const summary = transcript.summary;
|
|
20878
21217
|
const lines = [];
|
|
20879
|
-
lines.push(
|
|
20880
|
-
lines.push("");
|
|
20881
|
-
lines.push(`- recordingId: ${recording.recordingId}`);
|
|
20882
|
-
lines.push(`- transcriptId: ${transcript.transcriptId}`);
|
|
20883
|
-
lines.push(`- summaryStatus: ${summary.status}`);
|
|
20884
|
-
if (summary.error) lines.push(`- error: ${summary.error}`);
|
|
21218
|
+
lines.push("# Summary");
|
|
20885
21219
|
lines.push("");
|
|
20886
21220
|
if (summary.tldr) {
|
|
20887
21221
|
lines.push("## TL;DR", "");
|
|
@@ -20891,8 +21225,8 @@ function renderSummaryMarkdown(recording, transcript) {
|
|
|
20891
21225
|
appendStringList(lines, "Topics", summary.topics);
|
|
20892
21226
|
appendStringList(lines, "Decisions", summary.decisions);
|
|
20893
21227
|
appendActionItems(lines, summary.actionItems);
|
|
20894
|
-
|
|
20895
|
-
|
|
21228
|
+
appendNotableQuotes(lines, summary.quotes);
|
|
21229
|
+
if (lines.length === 2) return null;
|
|
20896
21230
|
if (lines[lines.length - 1] !== "") lines.push("");
|
|
20897
21231
|
return lines.join("\n");
|
|
20898
21232
|
}
|
|
@@ -20900,7 +21234,7 @@ function renderActionItemsMarkdown(summary) {
|
|
|
20900
21234
|
if (!summary.actionItems || summary.actionItems.length === 0) return null;
|
|
20901
21235
|
const lines = ["# Action Items", ""];
|
|
20902
21236
|
for (const item of summary.actionItems) {
|
|
20903
|
-
lines.push(
|
|
21237
|
+
lines.push(actionItemLine(item));
|
|
20904
21238
|
}
|
|
20905
21239
|
lines.push("");
|
|
20906
21240
|
return lines.join("\n");
|
|
@@ -20915,27 +21249,20 @@ function appendActionItems(lines, values) {
|
|
|
20915
21249
|
if (!values || values.length === 0) return;
|
|
20916
21250
|
lines.push("## Action Items", "");
|
|
20917
21251
|
for (const item of values) {
|
|
20918
|
-
lines.push(
|
|
21252
|
+
lines.push(actionItemLine(item));
|
|
20919
21253
|
}
|
|
20920
21254
|
lines.push("");
|
|
20921
21255
|
}
|
|
20922
|
-
function
|
|
21256
|
+
function appendNotableQuotes(lines, values) {
|
|
20923
21257
|
if (!values || values.length === 0) return;
|
|
20924
|
-
lines.push("##
|
|
21258
|
+
lines.push("## Notable Quotes", "");
|
|
20925
21259
|
for (const item of values) {
|
|
20926
|
-
lines.push(
|
|
20927
|
-
`- ${formatTimestamp(item.startMs)}-${formatTimestamp(item.endMs)}: ${item.title} - ${item.summary}`
|
|
20928
|
-
);
|
|
21260
|
+
lines.push(`> ${item.speaker ? `${item.speaker}: ` : ""}${item.text}`);
|
|
20929
21261
|
}
|
|
20930
21262
|
lines.push("");
|
|
20931
21263
|
}
|
|
20932
|
-
function
|
|
20933
|
-
|
|
20934
|
-
lines.push("## Quotes", "");
|
|
20935
|
-
for (const item of values) {
|
|
20936
|
-
lines.push(`- ${item.speaker ? `${item.speaker}: ` : ""}"${item.text}"`);
|
|
20937
|
-
}
|
|
20938
|
-
lines.push("");
|
|
21264
|
+
function actionItemLine(item) {
|
|
21265
|
+
return `- ${item.who ? `${item.who}: ` : ""}${item.what}`;
|
|
20939
21266
|
}
|
|
20940
21267
|
function audioMetadata(audio) {
|
|
20941
21268
|
return {
|
|
@@ -21331,20 +21658,20 @@ function renderEnvelope(envelope, opts) {
|
|
|
21331
21658
|
`);
|
|
21332
21659
|
}
|
|
21333
21660
|
function renderHumanSuccess(command, data, opts) {
|
|
21334
|
-
if (command === "auth login" &&
|
|
21661
|
+
if (command === "auth login" && isRecord5(data)) {
|
|
21335
21662
|
opts.stdout(`Signed in${typeof data.email === "string" ? ` as ${data.email}` : ""}
|
|
21336
21663
|
`);
|
|
21337
21664
|
return;
|
|
21338
21665
|
}
|
|
21339
|
-
if (command === "auth logout" &&
|
|
21666
|
+
if (command === "auth logout" && isRecord5(data)) {
|
|
21340
21667
|
opts.stdout(data.cleared ? "Signed out of Recappi CLI\n" : "No Recappi CLI session to clear\n");
|
|
21341
21668
|
return;
|
|
21342
21669
|
}
|
|
21343
|
-
if (command === "auth import-macos" &&
|
|
21670
|
+
if (command === "auth import-macos" && isRecord5(data)) {
|
|
21344
21671
|
opts.stdout("Imported the Recappi Mini app session into Recappi CLI\n");
|
|
21345
21672
|
return;
|
|
21346
21673
|
}
|
|
21347
|
-
if (command === "auth status" &&
|
|
21674
|
+
if (command === "auth status" && isRecord5(data)) {
|
|
21348
21675
|
if (data.loggedIn) {
|
|
21349
21676
|
opts.stdout(`Signed in${typeof data.email === "string" ? ` as ${data.email}` : ""}
|
|
21350
21677
|
`);
|
|
@@ -21353,7 +21680,7 @@ function renderHumanSuccess(command, data, opts) {
|
|
|
21353
21680
|
opts.stdout("Not logged in\n");
|
|
21354
21681
|
return;
|
|
21355
21682
|
}
|
|
21356
|
-
if (command === "account status" &&
|
|
21683
|
+
if (command === "account status" && isRecord5(data)) {
|
|
21357
21684
|
if (!data.loggedIn) {
|
|
21358
21685
|
opts.stdout("Not logged in\n");
|
|
21359
21686
|
return;
|
|
@@ -21364,7 +21691,7 @@ function renderHumanSuccess(command, data, opts) {
|
|
|
21364
21691
|
`);
|
|
21365
21692
|
if (typeof data.userId === "string") opts.stdout(` userId: ${data.userId}
|
|
21366
21693
|
`);
|
|
21367
|
-
const billing =
|
|
21694
|
+
const billing = isRecord5(data.billing) ? data.billing : {};
|
|
21368
21695
|
if (typeof billing.tier === "string") opts.stdout(` plan: ${billing.tier}
|
|
21369
21696
|
`);
|
|
21370
21697
|
if (typeof billing.minutesUsed === "number") {
|
|
@@ -21377,7 +21704,7 @@ function renderHumanSuccess(command, data, opts) {
|
|
|
21377
21704
|
opts.stdout(` storage: ${formatBytes2(billing.storageBytes)} / ${cap}
|
|
21378
21705
|
`);
|
|
21379
21706
|
}
|
|
21380
|
-
const localStore =
|
|
21707
|
+
const localStore = isRecord5(data.localStore) ? data.localStore : {};
|
|
21381
21708
|
if (typeof localStore.path === "string") opts.stdout(` localStore: ${localStore.path}
|
|
21382
21709
|
`);
|
|
21383
21710
|
opts.stdout(
|
|
@@ -21386,17 +21713,17 @@ function renderHumanSuccess(command, data, opts) {
|
|
|
21386
21713
|
);
|
|
21387
21714
|
return;
|
|
21388
21715
|
}
|
|
21389
|
-
if (command === "version" &&
|
|
21716
|
+
if (command === "version" && isRecord5(data) && typeof data.version === "string") {
|
|
21390
21717
|
opts.stdout(`${data.version}
|
|
21391
21718
|
`);
|
|
21392
21719
|
return;
|
|
21393
21720
|
}
|
|
21394
|
-
if (command === "doctor" &&
|
|
21721
|
+
if (command === "doctor" && isRecord5(data) && Array.isArray(data.checks)) {
|
|
21395
21722
|
const status = typeof data.status === "string" ? data.status : "unknown";
|
|
21396
21723
|
opts.stdout(`Doctor: ${status}
|
|
21397
21724
|
`);
|
|
21398
21725
|
for (const check2 of data.checks) {
|
|
21399
|
-
if (!
|
|
21726
|
+
if (!isRecord5(check2)) continue;
|
|
21400
21727
|
const checkStatus = typeof check2.status === "string" ? check2.status : "unknown";
|
|
21401
21728
|
const name = typeof check2.name === "string" ? check2.name : "check";
|
|
21402
21729
|
const message = typeof check2.message === "string" ? ` \u2014 ${check2.message}` : "";
|
|
@@ -21407,14 +21734,14 @@ function renderHumanSuccess(command, data, opts) {
|
|
|
21407
21734
|
}
|
|
21408
21735
|
return;
|
|
21409
21736
|
}
|
|
21410
|
-
if (command === "transcript get" &&
|
|
21737
|
+
if (command === "transcript get" && isRecord5(data)) {
|
|
21411
21738
|
renderTranscriptHuman(data, opts);
|
|
21412
21739
|
return;
|
|
21413
21740
|
}
|
|
21414
|
-
if (command === "recordings list" &&
|
|
21741
|
+
if (command === "recordings list" && isRecord5(data) && Array.isArray(data.items)) {
|
|
21415
21742
|
opts.stdout("Recordings:\n");
|
|
21416
21743
|
for (const item of data.items) {
|
|
21417
|
-
if (!
|
|
21744
|
+
if (!isRecord5(item)) continue;
|
|
21418
21745
|
opts.stdout(` ${recordingLabel(item)}
|
|
21419
21746
|
`);
|
|
21420
21747
|
}
|
|
@@ -21426,7 +21753,7 @@ Next cursor: ${data.nextCursor}
|
|
|
21426
21753
|
}
|
|
21427
21754
|
return;
|
|
21428
21755
|
}
|
|
21429
|
-
if (command === "recordings get" &&
|
|
21756
|
+
if (command === "recordings get" && isRecord5(data)) {
|
|
21430
21757
|
opts.stdout(`${recordingTitle(data)}
|
|
21431
21758
|
`);
|
|
21432
21759
|
opts.stdout(` recordingId: ${String(data.recordingId)}
|
|
@@ -21448,7 +21775,7 @@ Next:
|
|
|
21448
21775
|
}
|
|
21449
21776
|
return;
|
|
21450
21777
|
}
|
|
21451
|
-
if (command === "recordings export" &&
|
|
21778
|
+
if (command === "recordings export" && isRecord5(data)) {
|
|
21452
21779
|
opts.stdout("Recording export ready\n");
|
|
21453
21780
|
printStringField(opts, data, "recordingId");
|
|
21454
21781
|
printStringField(opts, data, "exportDir");
|
|
@@ -21470,7 +21797,7 @@ Next:
|
|
|
21470
21797
|
}
|
|
21471
21798
|
return;
|
|
21472
21799
|
}
|
|
21473
|
-
if (command === "recordings retranscribe" &&
|
|
21800
|
+
if (command === "recordings retranscribe" && isRecord5(data)) {
|
|
21474
21801
|
opts.stdout("Transcription started\n");
|
|
21475
21802
|
if (typeof data.recordingId === "string") opts.stdout(` recordingId: ${data.recordingId}
|
|
21476
21803
|
`);
|
|
@@ -21493,7 +21820,7 @@ Next:
|
|
|
21493
21820
|
}
|
|
21494
21821
|
return;
|
|
21495
21822
|
}
|
|
21496
|
-
if (command === "recordings resummarize" &&
|
|
21823
|
+
if (command === "recordings resummarize" && isRecord5(data)) {
|
|
21497
21824
|
opts.stdout("Summary queued\n");
|
|
21498
21825
|
if (typeof data.recordingId === "string") opts.stdout(` recordingId: ${data.recordingId}
|
|
21499
21826
|
`);
|
|
@@ -21511,9 +21838,9 @@ Next:
|
|
|
21511
21838
|
}
|
|
21512
21839
|
return;
|
|
21513
21840
|
}
|
|
21514
|
-
if (command === "dashboard stats" &&
|
|
21515
|
-
const recordings =
|
|
21516
|
-
const jobs =
|
|
21841
|
+
if (command === "dashboard stats" && isRecord5(data)) {
|
|
21842
|
+
const recordings = isRecord5(data.recordings) ? data.recordings : {};
|
|
21843
|
+
const jobs = isRecord5(data.jobs) ? data.jobs : {};
|
|
21517
21844
|
opts.stdout(
|
|
21518
21845
|
`Recordings: ${numberText(recordings.total)} total, ${numberText(recordings.ready)} ready
|
|
21519
21846
|
`
|
|
@@ -21556,7 +21883,7 @@ Next:
|
|
|
21556
21883
|
}
|
|
21557
21884
|
return;
|
|
21558
21885
|
}
|
|
21559
|
-
if (command === "record" &&
|
|
21886
|
+
if (command === "record" && isRecord5(data)) {
|
|
21560
21887
|
opts.stdout("Recording complete\n");
|
|
21561
21888
|
if (typeof data.recordingId === "string") opts.stdout(` recordingId: ${data.recordingId}
|
|
21562
21889
|
`);
|
|
@@ -21575,14 +21902,14 @@ Next:
|
|
|
21575
21902
|
if (Array.isArray(data.artifacts) && data.artifacts.length > 0) {
|
|
21576
21903
|
opts.stdout(" artifacts:\n");
|
|
21577
21904
|
for (const artifact of data.artifacts) {
|
|
21578
|
-
if (!
|
|
21905
|
+
if (!isRecord5(artifact)) continue;
|
|
21579
21906
|
const kind = typeof artifact.kind === "string" ? artifact.kind : "artifact";
|
|
21580
21907
|
const localPath = typeof artifact.localPath === "string" ? artifact.localPath : "";
|
|
21581
21908
|
opts.stdout(` - ${kind}: ${localPath}
|
|
21582
21909
|
`);
|
|
21583
21910
|
}
|
|
21584
21911
|
}
|
|
21585
|
-
const cloudHandoffError =
|
|
21912
|
+
const cloudHandoffError = isRecord5(data.cloudHandoffError) ? data.cloudHandoffError : void 0;
|
|
21586
21913
|
if (cloudHandoffError && typeof cloudHandoffError.message === "string") {
|
|
21587
21914
|
opts.stderr(`Cloud handoff failed: ${cloudHandoffError.message}
|
|
21588
21915
|
`);
|
|
@@ -21607,7 +21934,7 @@ Next:
|
|
|
21607
21934
|
}
|
|
21608
21935
|
return;
|
|
21609
21936
|
}
|
|
21610
|
-
if (command === "audio" &&
|
|
21937
|
+
if (command === "audio" && isRecord5(data)) {
|
|
21611
21938
|
const action = typeof data.action === "string" ? data.action : "download";
|
|
21612
21939
|
opts.stdout(
|
|
21613
21940
|
action === "open" ? "Audio opened\n" : action === "reveal" ? "Audio revealed\n" : "Audio ready\n"
|
|
@@ -21622,7 +21949,7 @@ Next:
|
|
|
21622
21949
|
}
|
|
21623
21950
|
return;
|
|
21624
21951
|
}
|
|
21625
|
-
if ((command === "jobs wait" || command === "upload") &&
|
|
21952
|
+
if ((command === "jobs wait" || command === "upload") && isRecord5(data)) {
|
|
21626
21953
|
if (typeof data.transcriptId === "string") {
|
|
21627
21954
|
opts.stdout("Transcript ready\n");
|
|
21628
21955
|
if (typeof data.recordingId === "string" && typeof data.origin === "string") {
|
|
@@ -21647,10 +21974,10 @@ Next:
|
|
|
21647
21974
|
}
|
|
21648
21975
|
return;
|
|
21649
21976
|
}
|
|
21650
|
-
if (command === "schema" &&
|
|
21977
|
+
if (command === "schema" && isRecord5(data) && Array.isArray(data.commands)) {
|
|
21651
21978
|
opts.stdout("Commands:\n");
|
|
21652
21979
|
for (const entry of data.commands) {
|
|
21653
|
-
if (!
|
|
21980
|
+
if (!isRecord5(entry) || typeof entry.name !== "string") continue;
|
|
21654
21981
|
const summary = typeof entry.summary === "string" ? ` \u2014 ${entry.summary}` : "";
|
|
21655
21982
|
opts.stdout(` ${entry.name}${summary}
|
|
21656
21983
|
`);
|
|
@@ -21660,9 +21987,9 @@ Next:
|
|
|
21660
21987
|
}
|
|
21661
21988
|
if (Array.isArray(entry.examples) && entry.examples.length > 0) {
|
|
21662
21989
|
const first = entry.examples.find(
|
|
21663
|
-
(example) =>
|
|
21990
|
+
(example) => isRecord5(example) && typeof example.command === "string"
|
|
21664
21991
|
);
|
|
21665
|
-
if (
|
|
21992
|
+
if (isRecord5(first) && typeof first.command === "string") {
|
|
21666
21993
|
opts.stdout(` example: ${first.command}
|
|
21667
21994
|
`);
|
|
21668
21995
|
}
|
|
@@ -21794,7 +22121,7 @@ function renderTranscriptHuman(data, opts) {
|
|
|
21794
22121
|
const segments = Array.isArray(data.segments) ? data.segments : [];
|
|
21795
22122
|
let printedBody = false;
|
|
21796
22123
|
for (const segment of segments) {
|
|
21797
|
-
if (!
|
|
22124
|
+
if (!isRecord5(segment) || typeof segment.text !== "string") continue;
|
|
21798
22125
|
const clock = typeof segment.startMs === "number" ? `[${formatClock(segment.startMs / 1e3)}] ` : "";
|
|
21799
22126
|
const speaker = typeof segment.speaker === "string" ? `${segment.speaker}: ` : "";
|
|
21800
22127
|
opts.stdout(`${clock}${speaker}${segment.text}
|
|
@@ -21806,7 +22133,7 @@ function renderTranscriptHuman(data, opts) {
|
|
|
21806
22133
|
`);
|
|
21807
22134
|
printedBody = true;
|
|
21808
22135
|
}
|
|
21809
|
-
const summary =
|
|
22136
|
+
const summary = isRecord5(data.summary) ? data.summary : void 0;
|
|
21810
22137
|
if (!summary || summary.status !== "succeeded") return;
|
|
21811
22138
|
if (typeof summary.tldr === "string" && summary.tldr.length > 0) {
|
|
21812
22139
|
opts.stdout(`
|
|
@@ -21824,7 +22151,7 @@ Summary:
|
|
|
21824
22151
|
if (Array.isArray(summary.actionItems) && summary.actionItems.length > 0) {
|
|
21825
22152
|
opts.stdout("\nAction items:\n");
|
|
21826
22153
|
for (const item of summary.actionItems) {
|
|
21827
|
-
if (!
|
|
22154
|
+
if (!isRecord5(item) || typeof item.what !== "string") continue;
|
|
21828
22155
|
const who = typeof item.who === "string" ? `${item.who}: ` : "";
|
|
21829
22156
|
opts.stdout(` - ${who}${item.what}
|
|
21830
22157
|
`);
|
|
@@ -21899,7 +22226,7 @@ function applyFields(command, data, fields, compact) {
|
|
|
21899
22226
|
};
|
|
21900
22227
|
return compact ? compactData(filtered2) : filtered2;
|
|
21901
22228
|
}
|
|
21902
|
-
if (!
|
|
22229
|
+
if (!isRecord5(data)) return data;
|
|
21903
22230
|
const allowed = new Set(Object.keys(data));
|
|
21904
22231
|
assertKnownFields(fields, allowed);
|
|
21905
22232
|
const filtered = pickFields(data, fields);
|
|
@@ -21924,7 +22251,7 @@ function compactData(value) {
|
|
|
21924
22251
|
if (Array.isArray(value)) {
|
|
21925
22252
|
return value.map(compactData).filter((item) => item !== void 0);
|
|
21926
22253
|
}
|
|
21927
|
-
if (
|
|
22254
|
+
if (isRecord5(value)) {
|
|
21928
22255
|
const out = {};
|
|
21929
22256
|
for (const [key, child] of Object.entries(value)) {
|
|
21930
22257
|
const compacted = compactData(child);
|
|
@@ -21942,16 +22269,16 @@ function stableStringify(value, compact) {
|
|
|
21942
22269
|
}
|
|
21943
22270
|
function sortKeys(value) {
|
|
21944
22271
|
if (Array.isArray(value)) return value.map(sortKeys);
|
|
21945
|
-
if (!
|
|
22272
|
+
if (!isRecord5(value)) return value;
|
|
21946
22273
|
return Object.fromEntries(
|
|
21947
22274
|
Object.keys(value).sort().map((key) => [key, sortKeys(value[key])])
|
|
21948
22275
|
);
|
|
21949
22276
|
}
|
|
21950
|
-
function
|
|
22277
|
+
function isRecord5(value) {
|
|
21951
22278
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21952
22279
|
}
|
|
21953
22280
|
function isUploadBatch(value) {
|
|
21954
|
-
return
|
|
22281
|
+
return isRecord5(value) && Array.isArray(value.successes) && Array.isArray(value.failures);
|
|
21955
22282
|
}
|
|
21956
22283
|
|
|
21957
22284
|
// src/schema.ts
|
|
@@ -22006,11 +22333,11 @@ function buildSchemaDocument(program) {
|
|
|
22006
22333
|
event: toJsonSchema(operationEventSchema)
|
|
22007
22334
|
};
|
|
22008
22335
|
}
|
|
22009
|
-
function walkCommands(command,
|
|
22336
|
+
function walkCommands(command, path8, out) {
|
|
22010
22337
|
for (const sub of subcommandsOf(command)) {
|
|
22011
22338
|
const name = sub.name();
|
|
22012
22339
|
if (name === "help") continue;
|
|
22013
|
-
const fullPath = [...
|
|
22340
|
+
const fullPath = [...path8, name];
|
|
22014
22341
|
const children = subcommandsOf(sub).filter((child) => child.name() !== "help");
|
|
22015
22342
|
if (children.length === 0) {
|
|
22016
22343
|
out.push(leafCommandDoc(sub, fullPath.join(" ")));
|
|
@@ -22288,7 +22615,7 @@ var MiniSidecarClient = class {
|
|
|
22288
22615
|
}
|
|
22289
22616
|
};
|
|
22290
22617
|
function sidecarErrorToCliError(error51) {
|
|
22291
|
-
const data =
|
|
22618
|
+
const data = isRecord7(error51.data) ? error51.data : void 0;
|
|
22292
22619
|
const maybeCode = typeof data?.cliCode === "string" ? cliErrorCodeSchema.safeParse(data.cliCode) : void 0;
|
|
22293
22620
|
const hint = typeof data?.recovery === "string" ? data.recovery : void 0;
|
|
22294
22621
|
const retryable = typeof data?.retryable === "boolean" ? data.retryable : error51.code >= -32099 && error51.code <= -32e3;
|
|
@@ -22305,7 +22632,7 @@ function sidecarErrorToCliError(error51) {
|
|
|
22305
22632
|
retryable
|
|
22306
22633
|
});
|
|
22307
22634
|
}
|
|
22308
|
-
function
|
|
22635
|
+
function isRecord7(value) {
|
|
22309
22636
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22310
22637
|
}
|
|
22311
22638
|
function spawnMiniSidecar(opts) {
|
|
@@ -22412,8 +22739,8 @@ function requestLaunchServicesSidecarShutdown(input) {
|
|
|
22412
22739
|
} catch {
|
|
22413
22740
|
}
|
|
22414
22741
|
}
|
|
22415
|
-
function createFifo(
|
|
22416
|
-
const result = spawnSync("mkfifo", [
|
|
22742
|
+
function createFifo(path8) {
|
|
22743
|
+
const result = spawnSync("mkfifo", [path8], { encoding: "utf8" });
|
|
22417
22744
|
if (result.status !== 0) {
|
|
22418
22745
|
throw cliError("record.helper_unavailable", "Recappi recording helper could not start.", {
|
|
22419
22746
|
hint: result.stderr || "Could not create the local recorder pipes. Try again."
|
|
@@ -22949,14 +23276,14 @@ async function startRecordSessionOnce(opts) {
|
|
|
22949
23276
|
}
|
|
22950
23277
|
}
|
|
22951
23278
|
function isPermissionRestartRequiredError(error51, permissionName) {
|
|
22952
|
-
const root =
|
|
22953
|
-
const descriptor =
|
|
23279
|
+
const root = isRecord8(error51) ? error51 : void 0;
|
|
23280
|
+
const descriptor = isRecord8(root?.descriptor) ? root.descriptor : void 0;
|
|
22954
23281
|
if (descriptor?.code !== "record.permission_required") return false;
|
|
22955
|
-
const sidecarError =
|
|
22956
|
-
const sidecarData =
|
|
23282
|
+
const sidecarError = isRecord8(root?.data) ? root.data : void 0;
|
|
23283
|
+
const sidecarData = isRecord8(sidecarError?.data) ? sidecarError.data : void 0;
|
|
22957
23284
|
return sidecarData?.permission === permissionName && (sidecarData.requiresProcessRestart === true || sidecarData.requiresProcessRestart === "true");
|
|
22958
23285
|
}
|
|
22959
|
-
function
|
|
23286
|
+
function isRecord8(value) {
|
|
22960
23287
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22961
23288
|
}
|
|
22962
23289
|
function normalizeSidecarSources(sources) {
|
|
@@ -23037,9 +23364,9 @@ function resolveSidecarCommand(opts) {
|
|
|
23037
23364
|
hint: `No bundled helper is registered for ${platform}. Set ${SIDECAR_COMMAND_ENV} to a compatible helper when one is available.`
|
|
23038
23365
|
});
|
|
23039
23366
|
}
|
|
23040
|
-
function ensureBundledHelperExecutable(
|
|
23041
|
-
if (process.platform === "darwin" &&
|
|
23042
|
-
const stableApp = ensureStableDarwinHelperApp(
|
|
23367
|
+
function ensureBundledHelperExecutable(path8, opts = {}) {
|
|
23368
|
+
if (process.platform === "darwin" && path8.endsWith(".app")) {
|
|
23369
|
+
const stableApp = ensureStableDarwinHelperApp(path8, opts);
|
|
23043
23370
|
const executable = darwinAppExecutablePath(stableApp);
|
|
23044
23371
|
if (!existsSync(executable)) {
|
|
23045
23372
|
throw cliError("record.helper_unavailable", "Recappi recording helper is not available.", {
|
|
@@ -23049,19 +23376,19 @@ function ensureBundledHelperExecutable(path7, opts = {}) {
|
|
|
23049
23376
|
ensureExecutableMode(executable);
|
|
23050
23377
|
return stableApp;
|
|
23051
23378
|
}
|
|
23052
|
-
if (process.platform === "win32") return
|
|
23053
|
-
ensureExecutableMode(
|
|
23054
|
-
return
|
|
23379
|
+
if (process.platform === "win32") return path8;
|
|
23380
|
+
ensureExecutableMode(path8);
|
|
23381
|
+
return path8;
|
|
23055
23382
|
}
|
|
23056
|
-
function ensureExecutableMode(
|
|
23057
|
-
const mode = statSync(
|
|
23383
|
+
function ensureExecutableMode(path8) {
|
|
23384
|
+
const mode = statSync(path8).mode;
|
|
23058
23385
|
if ((mode & 73) !== 0) return;
|
|
23059
23386
|
try {
|
|
23060
|
-
chmodSync(
|
|
23387
|
+
chmodSync(path8, mode | 493);
|
|
23061
23388
|
} catch (error51) {
|
|
23062
23389
|
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
23063
23390
|
throw cliError("record.helper_unavailable", "Recappi recording helper is not executable.", {
|
|
23064
|
-
hint: `Could not make bundled helper executable at ${
|
|
23391
|
+
hint: `Could not make bundled helper executable at ${path8}: ${message}. Reinstall recappi, or set ${SIDECAR_COMMAND_ENV} to a compatible helper.`
|
|
23065
23392
|
});
|
|
23066
23393
|
}
|
|
23067
23394
|
}
|
|
@@ -23106,14 +23433,14 @@ function helperSourceSignature(sourceApp) {
|
|
|
23106
23433
|
codeSignature: existsSync(signaturePath) ? fileDigest(signaturePath) : null
|
|
23107
23434
|
});
|
|
23108
23435
|
}
|
|
23109
|
-
function fileDigest(
|
|
23436
|
+
function fileDigest(path8) {
|
|
23110
23437
|
const hash2 = createHash("sha256");
|
|
23111
|
-
hash2.update(readFileSync2(
|
|
23438
|
+
hash2.update(readFileSync2(path8));
|
|
23112
23439
|
return hash2.digest("hex");
|
|
23113
23440
|
}
|
|
23114
|
-
function readTextIfExists(
|
|
23441
|
+
function readTextIfExists(path8) {
|
|
23115
23442
|
try {
|
|
23116
|
-
return readFileSync2(
|
|
23443
|
+
return readFileSync2(path8, "utf8");
|
|
23117
23444
|
} catch {
|
|
23118
23445
|
return null;
|
|
23119
23446
|
}
|
|
@@ -23407,6 +23734,13 @@ async function runCli(deps = {}) {
|
|
|
23407
23734
|
await runDashboard2({
|
|
23408
23735
|
fetchJobs: () => client.listJobs({ status: "active", limit: 20 }),
|
|
23409
23736
|
fetchRecordings: ({ cursor, limit = DASHBOARD_RECORDINGS_PAGE_SIZE } = {}) => client.listRecordings({ limit, cursor }),
|
|
23737
|
+
fetchCachedRecordings: account ? ({ limit = DASHBOARD_RECORDINGS_PAGE_SIZE } = {}) => listCachedRecordingSessions({
|
|
23738
|
+
account,
|
|
23739
|
+
origin: auth.origin,
|
|
23740
|
+
limit,
|
|
23741
|
+
env: deps.env,
|
|
23742
|
+
homeDir: deps.homeDir
|
|
23743
|
+
}) : void 0,
|
|
23410
23744
|
fetchDashboardStats: () => client.dashboardStats(),
|
|
23411
23745
|
fetchAccountStatus: () => client.accountStatus(),
|
|
23412
23746
|
fetchTranscript: (transcriptId) => client.getTranscript(transcriptId),
|