recappi 0.1.76 → 0.1.77

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 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 = isRecord5(artifact?.metadata) ? artifact.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 isRecord5(value) {
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 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 = isRecord8(error51) && isRecord8(error51.descriptor) ? error51.descriptor : void 0;
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: isRecord8(error51) ? error51.data : void 0,
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 && isRecord8(error51) && isRecord8(error51.descriptor) ? error51.descriptor : void 0;
1893
- const code = typeof descriptor?.code === "string" ? descriptor.code : isRecord8(error51) && typeof error51.code === "string" ? error51.code : void 0;
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 = isRecord8(data) ? data : void 0;
1968
- const sidecarData = isRecord8(sidecarError?.data) ? sidecarError.data : void 0;
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 = isRecord8(data) ? data : void 0;
1984
- const sidecarData = isRecord8(sidecarError?.data) ? sidecarError.data : void 0;
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 isRecord8(value) {
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);
@@ -2245,6 +2249,21 @@ function AppShell({
2245
2249
  }, [peekTranscriptId, fetchTranscript]);
2246
2250
  const peekSummary = peekTranscriptId ? summaryCache.get(peekTranscriptId) : void 0;
2247
2251
  const refresh = useCallback(async ({ resetRecordings = false } = {}) => {
2252
+ let showingCachedRecordings = false;
2253
+ if (resetRecordings && fetchCachedRecordings) {
2254
+ try {
2255
+ const cached2 = await fetchCachedRecordings({ limit: RECORDINGS_PAGE_SIZE });
2256
+ if (cached2.items.length > 0) {
2257
+ showingCachedRecordings = true;
2258
+ setRecordings(cached2.items);
2259
+ setRecordingsNextCursor(cached2.nextCursor ?? null);
2260
+ setRecordingsTotalCount(cached2.totalCount);
2261
+ setLoaded(true);
2262
+ setRevalidatingRecordings(Boolean(fetchRecordings));
2263
+ }
2264
+ } catch {
2265
+ }
2266
+ }
2248
2267
  const [jobsR, recR, statsR, accountR] = await Promise.allSettled([
2249
2268
  fetchJobs(),
2250
2269
  resetRecordings && fetchRecordings ? fetchRecordings({ limit: RECORDINGS_PAGE_SIZE }) : Promise.resolve(void 0),
@@ -2270,7 +2289,8 @@ function AppShell({
2270
2289
  setAccountStatus("error");
2271
2290
  }
2272
2291
  setLoaded(true);
2273
- }, [fetchJobs, fetchRecordings, fetchDashboardStats, fetchAccountStatus]);
2292
+ if (showingCachedRecordings) setRevalidatingRecordings(false);
2293
+ }, [fetchJobs, fetchRecordings, fetchCachedRecordings, fetchDashboardStats, fetchAccountStatus]);
2274
2294
  const transcribeStoppedRecording = useCallback(async () => {
2275
2295
  const current = liveRecord;
2276
2296
  if (current?.kind !== "stopped") return;
@@ -2933,7 +2953,8 @@ function AppShell({
2933
2953
  peekItem: recordings[selected],
2934
2954
  peekSummary,
2935
2955
  showPeek,
2936
- peekWidth
2956
+ peekWidth,
2957
+ revalidating: revalidatingRecordings
2937
2958
  }
2938
2959
  );
2939
2960
  } else if (screen.kind === "account") {
@@ -3051,6 +3072,7 @@ async function runDashboard(deps) {
3051
3072
  fetchJobs: deps.fetchJobs,
3052
3073
  fetchTranscript: deps.fetchTranscript,
3053
3074
  fetchRecordings: deps.fetchRecordings,
3075
+ fetchCachedRecordings: deps.fetchCachedRecordings,
3054
3076
  fetchDashboardStats: deps.fetchDashboardStats,
3055
3077
  fetchAccountStatus: deps.fetchAccountStatus,
3056
3078
  recordingAudio: deps.recordingAudio,
@@ -3859,10 +3881,10 @@ function mergeDefs(...defs) {
3859
3881
  function cloneDef(schema) {
3860
3882
  return mergeDefs(schema._zod.def);
3861
3883
  }
3862
- function getElementAtPath(obj, path7) {
3863
- if (!path7)
3884
+ function getElementAtPath(obj, path8) {
3885
+ if (!path8)
3864
3886
  return obj;
3865
- return path7.reduce((acc, key) => acc?.[key], obj);
3887
+ return path8.reduce((acc, key) => acc?.[key], obj);
3866
3888
  }
3867
3889
  function promiseAllObject(promisesObj) {
3868
3890
  const keys = Object.keys(promisesObj);
@@ -4271,11 +4293,11 @@ function explicitlyAborted(x, startIndex = 0) {
4271
4293
  }
4272
4294
  return false;
4273
4295
  }
4274
- function prefixIssues(path7, issues) {
4296
+ function prefixIssues(path8, issues) {
4275
4297
  return issues.map((iss) => {
4276
4298
  var _a3;
4277
4299
  (_a3 = iss).path ?? (_a3.path = []);
4278
- iss.path.unshift(path7);
4300
+ iss.path.unshift(path8);
4279
4301
  return iss;
4280
4302
  });
4281
4303
  }
@@ -4422,16 +4444,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
4422
4444
  }
4423
4445
  function formatError(error51, mapper = (issue2) => issue2.message) {
4424
4446
  const fieldErrors = { _errors: [] };
4425
- const processError = (error52, path7 = []) => {
4447
+ const processError = (error52, path8 = []) => {
4426
4448
  for (const issue2 of error52.issues) {
4427
4449
  if (issue2.code === "invalid_union" && issue2.errors.length) {
4428
- issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
4450
+ issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
4429
4451
  } else if (issue2.code === "invalid_key") {
4430
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
4452
+ processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
4431
4453
  } else if (issue2.code === "invalid_element") {
4432
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
4454
+ processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
4433
4455
  } else {
4434
- const fullpath = [...path7, ...issue2.path];
4456
+ const fullpath = [...path8, ...issue2.path];
4435
4457
  if (fullpath.length === 0) {
4436
4458
  fieldErrors._errors.push(mapper(issue2));
4437
4459
  } else {
@@ -4458,17 +4480,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
4458
4480
  }
4459
4481
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
4460
4482
  const result = { errors: [] };
4461
- const processError = (error52, path7 = []) => {
4483
+ const processError = (error52, path8 = []) => {
4462
4484
  var _a3, _b;
4463
4485
  for (const issue2 of error52.issues) {
4464
4486
  if (issue2.code === "invalid_union" && issue2.errors.length) {
4465
- issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
4487
+ issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
4466
4488
  } else if (issue2.code === "invalid_key") {
4467
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
4489
+ processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
4468
4490
  } else if (issue2.code === "invalid_element") {
4469
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
4491
+ processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
4470
4492
  } else {
4471
- const fullpath = [...path7, ...issue2.path];
4493
+ const fullpath = [...path8, ...issue2.path];
4472
4494
  if (fullpath.length === 0) {
4473
4495
  result.errors.push(mapper(issue2));
4474
4496
  continue;
@@ -4500,8 +4522,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
4500
4522
  }
4501
4523
  function toDotPath(_path) {
4502
4524
  const segs = [];
4503
- const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
4504
- for (const seg of path7) {
4525
+ const path8 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
4526
+ for (const seg of path8) {
4505
4527
  if (typeof seg === "number")
4506
4528
  segs.push(`[${seg}]`);
4507
4529
  else if (typeof seg === "symbol")
@@ -17193,13 +17215,13 @@ function resolveRef(ref, ctx) {
17193
17215
  if (!ref.startsWith("#")) {
17194
17216
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
17195
17217
  }
17196
- const path7 = ref.slice(1).split("/").filter(Boolean);
17197
- if (path7.length === 0) {
17218
+ const path8 = ref.slice(1).split("/").filter(Boolean);
17219
+ if (path8.length === 0) {
17198
17220
  return ctx.rootSchema;
17199
17221
  }
17200
17222
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
17201
- if (path7[0] === defsKey) {
17202
- const key = path7[1];
17223
+ if (path8[0] === defsKey) {
17224
+ const key = path8[1];
17203
17225
  if (!key || !ctx.defs[key]) {
17204
17226
  throw new Error(`Reference not found: ${ref}`);
17205
17227
  }
@@ -18884,6 +18906,9 @@ function defaultStorePath(homeDir = os3.homedir(), env = process.env) {
18884
18906
  const dataHome = env.XDG_DATA_HOME?.trim() || path3.join(homeDir, ".local", "share");
18885
18907
  return path3.join(dataHome, "recappi", "cli-state.sqlite");
18886
18908
  }
18909
+ function normalizeAccountStamp(input) {
18910
+ return parseAccountPartition(input, "lenient");
18911
+ }
18887
18912
  function requireAccountPartition(input) {
18888
18913
  const account = parseAccountPartition(input, "strict");
18889
18914
  if (!account) {
@@ -19791,12 +19816,12 @@ var RecappiApiClient = class {
19791
19816
  } : {}
19792
19817
  };
19793
19818
  }
19794
- async getJson(path7) {
19795
- const response = await this.request("GET", path7);
19819
+ async getJson(path8) {
19820
+ const response = await this.request("GET", path8);
19796
19821
  return await parseJson(response);
19797
19822
  }
19798
- async postJson(path7, body) {
19799
- const response = await this.request("POST", path7, JSON.stringify(body), {
19823
+ async postJson(path8, body) {
19824
+ const response = await this.request("POST", path8, JSON.stringify(body), {
19800
19825
  headers: { "content-type": "application/json" }
19801
19826
  });
19802
19827
  return await parseJson(response);
@@ -20187,8 +20212,11 @@ import { promises as fs4 } from "fs";
20187
20212
  import path5 from "path";
20188
20213
  function createRecordingAudioRuntime(client, deps = {}) {
20189
20214
  const downloadRecordingAudioFile = async (recordingId, opts) => {
20190
- const cached2 = opts?.directory ? null : await findReusableDownload(recordingId, deps);
20191
- if (cached2) return cached2;
20215
+ const cached2 = await findReusableDownload(recordingId, deps);
20216
+ if (cached2) {
20217
+ if (!opts?.directory) return cached2;
20218
+ return materializeCachedDownload(recordingId, cached2, opts, deps);
20219
+ }
20192
20220
  const directory = opts?.directory ?? (deps.account ? defaultDownloadDirectory(deps) : void 0);
20193
20221
  const download = await client.downloadRecordingAudio(recordingId, {
20194
20222
  ...opts,
@@ -20216,6 +20244,38 @@ function createRecordingAudioRuntime(client, deps = {}) {
20216
20244
  )
20217
20245
  };
20218
20246
  }
20247
+ async function materializeCachedDownload(recordingId, cached2, opts, deps) {
20248
+ if (!opts.directory) return cached2;
20249
+ await fs4.mkdir(opts.directory, { recursive: true });
20250
+ const contentType = cached2.contentType ?? contentTypeForAudioPath(cached2.localPath);
20251
+ const targetPath = path5.join(
20252
+ opts.directory,
20253
+ recordingAudioFileName(recordingId, opts.title, contentType, opts.filenameStem)
20254
+ );
20255
+ if (path5.resolve(cached2.localPath) !== path5.resolve(targetPath)) {
20256
+ await fs4.copyFile(cached2.localPath, targetPath);
20257
+ }
20258
+ const contentLength = cached2.contentLength ?? (await fs4.stat(targetPath)).size;
20259
+ const artifact = await rememberDownload(
20260
+ {
20261
+ recordingId,
20262
+ localPath: targetPath,
20263
+ contentType,
20264
+ contentLength,
20265
+ origin: cached2.origin ?? ""
20266
+ },
20267
+ deps
20268
+ );
20269
+ return {
20270
+ recordingId,
20271
+ localPath: targetPath,
20272
+ reused: true,
20273
+ ...artifact ? { artifactId: artifact.id } : {},
20274
+ contentType,
20275
+ contentLength,
20276
+ ...cached2.origin ? { origin: cached2.origin } : {}
20277
+ };
20278
+ }
20219
20279
  function openPath(localPath, deps = {}) {
20220
20280
  return runMacOpen([localPath], deps);
20221
20281
  }
@@ -20246,7 +20306,7 @@ async function rememberDownload(download, deps) {
20246
20306
  resource: "recording_audio",
20247
20307
  contentType: download.contentType,
20248
20308
  ...download.contentLength !== void 0 ? { contentLength: download.contentLength } : {},
20249
- origin: download.origin
20309
+ ...download.origin ? { origin: download.origin } : {}
20250
20310
  }
20251
20311
  });
20252
20312
  return store.markLocalArtifactOpened(artifact.id);
@@ -20274,6 +20334,27 @@ async function withStore(deps, run) {
20274
20334
  function defaultDownloadDirectory(deps) {
20275
20335
  return path5.join(path5.dirname(defaultStorePath(deps.homeDir, deps.env)), "downloads");
20276
20336
  }
20337
+ function contentTypeForAudioPath(localPath) {
20338
+ switch (path5.extname(localPath).toLowerCase()) {
20339
+ case ".mp3":
20340
+ return "audio/mpeg";
20341
+ case ".m4a":
20342
+ case ".mp4":
20343
+ return "audio/m4a";
20344
+ case ".ogg":
20345
+ return "audio/ogg";
20346
+ case ".flac":
20347
+ return "audio/flac";
20348
+ case ".webm":
20349
+ return "audio/webm";
20350
+ case ".aiff":
20351
+ case ".aif":
20352
+ return "audio/aiff";
20353
+ case ".wav":
20354
+ default:
20355
+ return "audio/wav";
20356
+ }
20357
+ }
20277
20358
  async function isReadableFile(localPath) {
20278
20359
  try {
20279
20360
  return (await fs4.stat(localPath)).isFile();
@@ -20553,41 +20634,170 @@ ${lines.join("\n")}
20553
20634
  }
20554
20635
 
20555
20636
  // src/export.ts
20556
- import { promises as fs5 } from "fs";
20637
+ import { promises as fs6 } from "fs";
20557
20638
  import os5 from "os";
20639
+ import path7 from "path";
20640
+
20641
+ // src/recordingSessionCache.ts
20642
+ import { promises as fs5 } from "fs";
20558
20643
  import path6 from "path";
20644
+ function accountFromStatus(status) {
20645
+ if (!status.loggedIn || !status.userId) return null;
20646
+ return normalizeAccountStamp({ backendOrigin: status.origin, userId: status.userId });
20647
+ }
20648
+ async function findIndexedRecordingSessionDir(recordingId, deps) {
20649
+ return withSessionStore(deps, async (store, account) => {
20650
+ if (!account) return void 0;
20651
+ const artifact = store.findLocalArtifactForAccount(account, {
20652
+ kind: "recording_session",
20653
+ remoteId: recordingId
20654
+ });
20655
+ if (!artifact) return void 0;
20656
+ if (!await isReadableDirectory(artifact.localPath)) return void 0;
20657
+ const manifest = await readJsonRecord(path6.join(artifact.localPath, "remote-session.json"));
20658
+ if (manifest?.recordingId && manifest.recordingId !== recordingId) return void 0;
20659
+ return artifact.localPath;
20660
+ });
20661
+ }
20662
+ async function rememberRecordingSession(input, deps) {
20663
+ return withSessionStore(deps, (store, account) => {
20664
+ if (!account) return null;
20665
+ return store.upsertLocalArtifact({
20666
+ kind: "recording_session",
20667
+ account,
20668
+ remoteId: input.recording.recordingId,
20669
+ localPath: input.sessionDir,
20670
+ metadata: stripUndefined({
20671
+ resource: "recording_session",
20672
+ source: input.source,
20673
+ sessionDir: input.sessionDir,
20674
+ recording: input.recording,
20675
+ files: stripUndefined({ ...input.files }),
20676
+ transcriptId: input.transcriptId,
20677
+ summaryStatus: input.summaryStatus,
20678
+ uploadFilename: input.uploadFilename,
20679
+ origin: input.recording.origin,
20680
+ indexedAt: (input.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
20681
+ })
20682
+ });
20683
+ });
20684
+ }
20685
+ async function listCachedRecordingSessions(deps) {
20686
+ const limit = deps.limit ?? 50;
20687
+ const items = await withSessionStore(deps, async (store, account) => {
20688
+ if (!account) return [];
20689
+ const artifacts = store.listLocalArtifactsForAccount(account, { kind: "recording_session" });
20690
+ const recordings = [];
20691
+ const seen = /* @__PURE__ */ new Set();
20692
+ for (const artifact of artifacts) {
20693
+ if (recordings.length >= limit) break;
20694
+ const recording = await recordingFromArtifact(artifact);
20695
+ if (!recording || seen.has(recording.recordingId)) continue;
20696
+ if (!await isReadableDirectory(artifact.localPath)) continue;
20697
+ seen.add(recording.recordingId);
20698
+ recordings.push(recording);
20699
+ }
20700
+ return recordings.sort((a, b) => b.createdAt - a.createdAt || b.updatedAt - a.updatedAt);
20701
+ });
20702
+ return recordingListDataSchema.parse({
20703
+ items,
20704
+ limit,
20705
+ nextCursor: null,
20706
+ totalCount: items.length,
20707
+ origin: deps.origin
20708
+ });
20709
+ }
20710
+ async function recordingFromArtifact(artifact) {
20711
+ const metadata = isRecord4(artifact.metadata) ? artifact.metadata : {};
20712
+ const cached2 = recordingDataSchema.safeParse(metadata.recording);
20713
+ if (cached2.success) return cached2.data;
20714
+ const fromFile = await readJsonRecord(path6.join(artifact.localPath, "recording.json"));
20715
+ const parsed = recordingDataSchema.safeParse(fromFile);
20716
+ return parsed.success ? parsed.data : null;
20717
+ }
20718
+ async function withSessionStore(deps, run) {
20719
+ const store = deps.store ?? openCliStore({ homeDir: deps.homeDir, env: deps.env });
20720
+ try {
20721
+ return await run(store, deps.account ?? null);
20722
+ } finally {
20723
+ if (!deps.store) store.close();
20724
+ }
20725
+ }
20726
+ async function readJsonRecord(filePath) {
20727
+ try {
20728
+ const parsed = JSON.parse(await fs5.readFile(filePath, "utf8"));
20729
+ return isRecord4(parsed) ? parsed : void 0;
20730
+ } catch {
20731
+ return void 0;
20732
+ }
20733
+ }
20734
+ async function isReadableDirectory(localPath) {
20735
+ try {
20736
+ return (await fs5.stat(localPath)).isDirectory();
20737
+ } catch {
20738
+ return false;
20739
+ }
20740
+ }
20741
+ function stripUndefined(value) {
20742
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
20743
+ }
20744
+ function isRecord4(value) {
20745
+ return typeof value === "object" && value !== null && !Array.isArray(value);
20746
+ }
20747
+
20748
+ // src/export.ts
20559
20749
  async function syncRecordingText(opts) {
20560
20750
  const context = await loadRecordingBundleContext(opts);
20561
- const sessionDir = await resolveRecordingSessionDir(context.recording, opts);
20751
+ const account = accountFromStatus(context.subscription);
20752
+ const sessionDir = await resolveRecordingSessionDir(context.recording, opts, account);
20562
20753
  return writeRecordingTextFiles(context, sessionDir, {
20563
- now: opts.now
20754
+ now: opts.now,
20755
+ store: opts.store,
20756
+ env: opts.env,
20757
+ homeDir: opts.homeDir,
20758
+ account,
20759
+ source: "text_sync"
20564
20760
  });
20565
20761
  }
20566
20762
  async function syncRecordingAudio(opts) {
20567
20763
  const context = await loadRecordingBundleContext(opts);
20568
- const sessionDir = await resolveRecordingSessionDir(context.recording, opts);
20764
+ const account = accountFromStatus(context.subscription);
20765
+ const sessionDir = await resolveRecordingSessionDir(context.recording, opts, account);
20569
20766
  const audio = await downloadRecordingAudioToDir(context.recording, opts.recordingAudio, sessionDir);
20570
20767
  const text = await writeRecordingTextFiles(context, sessionDir, {
20571
20768
  now: opts.now,
20572
- uploadFilename: path6.basename(audio.localPath)
20769
+ uploadFilename: path7.basename(audio.localPath),
20770
+ store: opts.store,
20771
+ env: opts.env,
20772
+ homeDir: opts.homeDir,
20773
+ account,
20774
+ source: "audio_sync",
20775
+ audioPath: audio.localPath
20573
20776
  });
20574
20777
  return { ...text, audioPath: audio.localPath, audio: audioMetadata(audio) };
20575
20778
  }
20576
20779
  async function exportRecording(opts) {
20577
20780
  const context = await loadRecordingBundleContext(opts);
20578
- const exportDir = await resolveRecordingSessionDir(context.recording, opts);
20781
+ const account = accountFromStatus(context.subscription);
20782
+ const exportDir = await resolveRecordingSessionDir(context.recording, opts, account);
20579
20783
  const audio = await downloadRecordingAudioToDir(context.recording, opts.recordingAudio, exportDir);
20580
20784
  const textFiles = await writeRecordingTextFiles(context, exportDir, {
20581
20785
  now: opts.now,
20582
- uploadFilename: path6.basename(audio.localPath)
20786
+ uploadFilename: path7.basename(audio.localPath),
20787
+ store: opts.store,
20788
+ env: opts.env,
20789
+ homeDir: opts.homeDir,
20790
+ account,
20791
+ source: "export",
20792
+ audioPath: audio.localPath
20583
20793
  });
20584
20794
  const { recording, subscription, transcript } = context;
20585
- const subscriptionPath = path6.join(exportDir, "subscription.md");
20586
- const subscriptionJsonPath = path6.join(exportDir, "subscription.json");
20587
- await fs5.writeFile(subscriptionPath, renderSubscriptionMarkdown(subscription), "utf8");
20795
+ const subscriptionPath = path7.join(exportDir, "subscription.md");
20796
+ const subscriptionJsonPath = path7.join(exportDir, "subscription.json");
20797
+ await fs6.writeFile(subscriptionPath, renderSubscriptionMarkdown(subscription), "utf8");
20588
20798
  await writeJson(subscriptionJsonPath, subscription);
20589
- const textPath = path6.join(exportDir, "handoff.md");
20590
- const manifestPath = path6.join(exportDir, "manifest.json");
20799
+ const textPath = path7.join(exportDir, "handoff.md");
20800
+ const manifestPath = path7.join(exportDir, "manifest.json");
20591
20801
  const data = recordingExportDataSchema.parse({
20592
20802
  origin: recording.origin,
20593
20803
  recordingId: recording.recordingId,
@@ -20609,7 +20819,7 @@ async function exportRecording(opts) {
20609
20819
  ...textFiles.summaryStatus ? { summaryStatus: textFiles.summaryStatus } : {},
20610
20820
  audio: audioMetadata(audio)
20611
20821
  });
20612
- await fs5.writeFile(
20822
+ await fs6.writeFile(
20613
20823
  textPath,
20614
20824
  renderHandoffMarkdown(recording, subscription, audio, data, transcript),
20615
20825
  "utf8"
@@ -20619,6 +20829,33 @@ async function exportRecording(opts) {
20619
20829
  command: "recordings export",
20620
20830
  data
20621
20831
  });
20832
+ await rememberRecordingSession(
20833
+ {
20834
+ recording,
20835
+ sessionDir: exportDir,
20836
+ files: {
20837
+ remoteManifestPath: data.remoteManifestPath,
20838
+ sessionMetadataPath: data.sessionMetadataPath,
20839
+ recordingJsonPath: data.recordingJsonPath,
20840
+ ...data.transcriptPath ? { transcriptPath: data.transcriptPath } : {},
20841
+ ...data.transcriptJsonPath ? { transcriptJsonPath: data.transcriptJsonPath } : {},
20842
+ ...data.summaryPath ? { summaryPath: data.summaryPath } : {},
20843
+ ...data.summaryJsonPath ? { summaryJsonPath: data.summaryJsonPath } : {},
20844
+ ...data.actionItemsPath ? { actionItemsPath: data.actionItemsPath } : {},
20845
+ audioPath: data.audioPath,
20846
+ subscriptionPath: data.subscriptionPath,
20847
+ subscriptionJsonPath: data.subscriptionJsonPath,
20848
+ textPath: data.textPath,
20849
+ manifestPath: data.manifestPath
20850
+ },
20851
+ ...data.transcriptId !== void 0 ? { transcriptId: data.transcriptId } : {},
20852
+ ...data.summaryStatus ? { summaryStatus: data.summaryStatus } : {},
20853
+ uploadFilename: path7.basename(audio.localPath),
20854
+ source: "export",
20855
+ now: opts.now
20856
+ },
20857
+ { account, store: opts.store, env: opts.env, homeDir: opts.homeDir }
20858
+ );
20622
20859
  return data;
20623
20860
  }
20624
20861
  async function loadRecordingBundleContext(opts) {
@@ -20641,11 +20878,11 @@ async function downloadRecordingAudioToDir(recording, recordingAudio, directory)
20641
20878
  }
20642
20879
  async function writeRecordingTextFiles(context, sessionDir, opts) {
20643
20880
  const { recording, transcript, subscription } = context;
20644
- await fs5.mkdir(sessionDir, { recursive: true });
20645
- const recordingJsonPath = path6.join(sessionDir, "recording.json");
20646
- const sessionMetadataPath = path6.join(sessionDir, "session-metadata.json");
20647
- const remoteManifestPath = path6.join(sessionDir, "remote-session.json");
20648
- const existingManifest = await readJsonRecord(remoteManifestPath);
20881
+ await fs6.mkdir(sessionDir, { recursive: true });
20882
+ const recordingJsonPath = path7.join(sessionDir, "recording.json");
20883
+ const sessionMetadataPath = path7.join(sessionDir, "session-metadata.json");
20884
+ const remoteManifestPath = path7.join(sessionDir, "remote-session.json");
20885
+ const existingManifest = await readJsonRecord2(remoteManifestPath);
20649
20886
  const uploadFilename = opts.uploadFilename ?? (typeof existingManifest?.uploadFilename === "string" ? existingManifest.uploadFilename : void 0);
20650
20887
  await writeJson(recordingJsonPath, recording);
20651
20888
  await writeJson(sessionMetadataPath, renderSessionMetadata(recording));
@@ -20660,19 +20897,23 @@ async function writeRecordingTextFiles(context, sessionDir, opts) {
20660
20897
  let actionItemsPath;
20661
20898
  let summaryStatus;
20662
20899
  if (transcript) {
20663
- transcriptPath = path6.join(sessionDir, "transcript.md");
20664
- transcriptJsonPath = path6.join(sessionDir, "transcript.json");
20665
- summaryPath = path6.join(sessionDir, "summary.md");
20666
- summaryJsonPath = path6.join(sessionDir, "summary.json");
20667
- actionItemsPath = path6.join(sessionDir, "action-items.md");
20900
+ transcriptPath = path7.join(sessionDir, "transcript.md");
20901
+ transcriptJsonPath = path7.join(sessionDir, "transcript.json");
20902
+ summaryJsonPath = path7.join(sessionDir, "summary.json");
20668
20903
  summaryStatus = transcript.summary.status;
20669
- await fs5.writeFile(transcriptPath, renderTranscriptMarkdown(transcript), "utf8");
20904
+ await fs6.writeFile(transcriptPath, renderTranscriptMarkdown(transcript), "utf8");
20670
20905
  await writeJson(transcriptJsonPath, transcript);
20671
20906
  await writeJson(summaryJsonPath, transcript.summary);
20672
- await fs5.writeFile(summaryPath, renderSummaryMarkdown(recording, transcript), "utf8");
20673
- await writeOptionalText(actionItemsPath, renderActionItemsMarkdown(transcript.summary));
20907
+ const summaryFilePath = path7.join(sessionDir, "summary.md");
20908
+ const summaryMarkdown = renderSummaryMarkdown(transcript);
20909
+ await writeOptionalText(summaryFilePath, summaryMarkdown);
20910
+ if (summaryMarkdown) summaryPath = summaryFilePath;
20911
+ const actionItemsFilePath = path7.join(sessionDir, "action-items.md");
20912
+ const actionItemsMarkdown = renderActionItemsMarkdown(transcript.summary);
20913
+ await writeOptionalText(actionItemsFilePath, actionItemsMarkdown);
20914
+ if (actionItemsMarkdown) actionItemsPath = actionItemsFilePath;
20674
20915
  }
20675
- return {
20916
+ const data = {
20676
20917
  recordingId: recording.recordingId,
20677
20918
  sessionDir,
20678
20919
  remoteManifestPath,
@@ -20686,10 +20927,56 @@ async function writeRecordingTextFiles(context, sessionDir, opts) {
20686
20927
  ...actionItemsPath ? { actionItemsPath } : {},
20687
20928
  ...summaryStatus ? { summaryStatus } : {}
20688
20929
  };
20930
+ await rememberRecordingSession(
20931
+ {
20932
+ recording,
20933
+ sessionDir,
20934
+ files: {
20935
+ remoteManifestPath,
20936
+ sessionMetadataPath,
20937
+ recordingJsonPath,
20938
+ ...transcriptPath ? { transcriptPath } : {},
20939
+ ...transcriptJsonPath ? { transcriptJsonPath } : {},
20940
+ ...summaryPath ? { summaryPath } : {},
20941
+ ...summaryJsonPath ? { summaryJsonPath } : {},
20942
+ ...actionItemsPath ? { actionItemsPath } : {},
20943
+ ...opts.audioPath ? { audioPath: opts.audioPath } : {}
20944
+ },
20945
+ ...context.transcriptId !== void 0 ? { transcriptId: context.transcriptId } : {},
20946
+ ...summaryStatus ? { summaryStatus } : {},
20947
+ ...uploadFilename ? { uploadFilename } : {},
20948
+ source: opts.source,
20949
+ now: opts.now
20950
+ },
20951
+ { account: opts.account ?? null, store: opts.store, env: opts.env, homeDir: opts.homeDir }
20952
+ );
20953
+ return data;
20689
20954
  }
20690
- async function resolveRecordingSessionDir(recording, opts) {
20691
- if (opts.directory) return path6.resolve(opts.directory);
20955
+ async function resolveRecordingSessionDir(recording, opts, account) {
20956
+ if (opts.directory) return path7.resolve(opts.directory);
20957
+ const indexed = await findIndexedRecordingSessionDir(recording.recordingId, {
20958
+ account,
20959
+ store: opts.store,
20960
+ homeDir: opts.homeDir,
20961
+ env: opts.env
20962
+ });
20963
+ if (indexed) return indexed;
20692
20964
  const existing = await findExistingRecordingSessionDir(recording.recordingId, opts.homeDir, opts.env);
20965
+ if (existing) {
20966
+ await rememberRecordingSession(
20967
+ {
20968
+ recording,
20969
+ sessionDir: existing,
20970
+ files: {
20971
+ remoteManifestPath: path7.join(existing, "remote-session.json"),
20972
+ sessionMetadataPath: path7.join(existing, "session-metadata.json"),
20973
+ recordingJsonPath: path7.join(existing, "recording.json")
20974
+ },
20975
+ source: "manifest_scan"
20976
+ },
20977
+ { account, store: opts.store, env: opts.env, homeDir: opts.homeDir }
20978
+ );
20979
+ }
20693
20980
  if (existing) return existing;
20694
20981
  return createRecordingSessionDir(recording, opts.homeDir, opts.env);
20695
20982
  }
@@ -20697,39 +20984,39 @@ async function findExistingRecordingSessionDir(recordingId, homeDir, env) {
20697
20984
  const base = recordingSessionBaseDirectory(homeDir, env);
20698
20985
  let entries;
20699
20986
  try {
20700
- entries = await fs5.readdir(base, { withFileTypes: true });
20987
+ entries = await fs6.readdir(base, { withFileTypes: true });
20701
20988
  } catch {
20702
20989
  return void 0;
20703
20990
  }
20704
20991
  for (const entry of entries) {
20705
20992
  if (!entry.isDirectory()) continue;
20706
- const dir = path6.join(base, entry.name);
20707
- const manifest = await readJsonRecord(path6.join(dir, "remote-session.json"));
20993
+ const dir = path7.join(base, entry.name);
20994
+ const manifest = await readJsonRecord2(path7.join(dir, "remote-session.json"));
20708
20995
  if (manifest?.recordingId === recordingId) return dir;
20709
20996
  }
20710
20997
  return void 0;
20711
20998
  }
20712
20999
  async function createRecordingSessionDir(recording, homeDir, env) {
20713
21000
  const base = recordingSessionBaseDirectory(homeDir, env);
20714
- await fs5.mkdir(base, { recursive: true });
21001
+ await fs6.mkdir(base, { recursive: true });
20715
21002
  const stem = formatSessionDirectoryDate(new Date(recording.createdAt));
20716
- let candidate = path6.join(base, stem);
21003
+ let candidate = path7.join(base, stem);
20717
21004
  let suffix = 2;
20718
21005
  while (await pathExists(candidate)) {
20719
- candidate = path6.join(base, `${stem}-cloud-${suffix}`);
21006
+ candidate = path7.join(base, `${stem}-cloud-${suffix}`);
20720
21007
  suffix += 1;
20721
21008
  }
20722
- await fs5.mkdir(candidate, { recursive: true });
21009
+ await fs6.mkdir(candidate, { recursive: true });
20723
21010
  return candidate;
20724
21011
  }
20725
21012
  function recordingSessionBaseDirectory(homeDir = os5.homedir(), env = process.env) {
20726
21013
  const explicit = env.RECAPPI_LOCAL_SESSIONS_DIR?.trim();
20727
21014
  if (explicit) return explicit;
20728
- return path6.join(homeDir, "Documents", "Recappi Mini");
21015
+ return path7.join(homeDir, "Documents", "Recappi Mini");
20729
21016
  }
20730
- async function readJsonRecord(filePath) {
21017
+ async function readJsonRecord2(filePath) {
20731
21018
  try {
20732
- const parsed = JSON.parse(await fs5.readFile(filePath, "utf8"));
21019
+ const parsed = JSON.parse(await fs6.readFile(filePath, "utf8"));
20733
21020
  return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : void 0;
20734
21021
  } catch {
20735
21022
  return void 0;
@@ -20737,7 +21024,7 @@ async function readJsonRecord(filePath) {
20737
21024
  }
20738
21025
  async function pathExists(filePath) {
20739
21026
  try {
20740
- await fs5.access(filePath);
21027
+ await fs6.access(filePath);
20741
21028
  return true;
20742
21029
  } catch {
20743
21030
  return false;
@@ -20753,15 +21040,15 @@ function formatSessionDirectoryDate(date5) {
20753
21040
  return `${year}-${month}-${day}_${hour}${minute}${second}`;
20754
21041
  }
20755
21042
  async function writeJson(filePath, value) {
20756
- await fs5.writeFile(filePath, `${JSON.stringify(value, null, 2)}
21043
+ await fs6.writeFile(filePath, `${JSON.stringify(value, null, 2)}
20757
21044
  `, "utf8");
20758
21045
  }
20759
21046
  async function writeOptionalText(filePath, value) {
20760
21047
  if (value && value.trim()) {
20761
- await fs5.writeFile(filePath, value, "utf8");
21048
+ await fs6.writeFile(filePath, value, "utf8");
20762
21049
  return;
20763
21050
  }
20764
- await fs5.rm(filePath, { force: true });
21051
+ await fs6.rm(filePath, { force: true });
20765
21052
  }
20766
21053
  function renderHandoffMarkdown(recording, subscription, audio, data, transcript) {
20767
21054
  const lines = [];
@@ -20797,9 +21084,12 @@ function renderHandoffMarkdown(recording, subscription, audio, data, transcript)
20797
21084
  appendSubscriptionLines(lines, subscription);
20798
21085
  lines.push("");
20799
21086
  if (transcript) {
20800
- lines.push(renderSummaryMarkdown(recording, transcript).trimEnd());
20801
- lines.push("");
20802
- lines.push(renderTranscriptMarkdown(transcript).trimEnd());
21087
+ const summaryMarkdown = renderSummaryMarkdown(transcript);
21088
+ if (summaryMarkdown) {
21089
+ lines.push(summaryMarkdown.trimEnd());
21090
+ lines.push("");
21091
+ }
21092
+ lines.push(renderTimestampedTranscriptMarkdown(transcript).trimEnd());
20803
21093
  lines.push("");
20804
21094
  }
20805
21095
  return `${lines.join("\n").trimEnd()}
@@ -20813,7 +21103,7 @@ function renderSubscriptionMarkdown(subscription) {
20813
21103
  }
20814
21104
  function renderSessionMetadata(recording) {
20815
21105
  const sourceTitle = recording.title ?? recording.summaryTitle ?? "Recappi Cloud";
20816
- return stripUndefined({
21106
+ return stripUndefined2({
20817
21107
  summaryTitle: recording.summaryTitle ?? recording.title ?? void 0,
20818
21108
  sourceTitle,
20819
21109
  sourceAppName: void 0,
@@ -20825,7 +21115,7 @@ function renderSessionMetadata(recording) {
20825
21115
  });
20826
21116
  }
20827
21117
  function renderRemoteSessionManifest(recording, uploadFilename, transcript, subscription, now) {
20828
- return stripUndefined({
21118
+ return stripUndefined2({
20829
21119
  recordingId: recording.recordingId,
20830
21120
  jobId: transcript?.jobId,
20831
21121
  transcriptId: transcript?.transcriptId ?? recording.activeTranscriptId ?? void 0,
@@ -20839,7 +21129,7 @@ function renderRemoteSessionManifest(recording, uploadFilename, transcript, subs
20839
21129
  accountBackendOrigin: subscription.origin
20840
21130
  });
20841
21131
  }
20842
- function stripUndefined(value) {
21132
+ function stripUndefined2(value) {
20843
21133
  return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
20844
21134
  }
20845
21135
  function appendSubscriptionLines(lines, subscription) {
@@ -20862,6 +21152,12 @@ function appendSubscriptionLines(lines, subscription) {
20862
21152
  function renderTranscriptMarkdown(transcript) {
20863
21153
  return `# Transcript
20864
21154
 
21155
+ ${transcript.text}
21156
+ `;
21157
+ }
21158
+ function renderTimestampedTranscriptMarkdown(transcript) {
21159
+ return `# Transcript
21160
+
20865
21161
  ${renderTranscriptLines(transcript)}
20866
21162
  `;
20867
21163
  }
@@ -20873,15 +21169,10 @@ function renderTranscriptLines(transcript) {
20873
21169
  if (lines.length > 0) return lines.join("\n");
20874
21170
  return transcript.text;
20875
21171
  }
20876
- function renderSummaryMarkdown(recording, transcript) {
21172
+ function renderSummaryMarkdown(transcript) {
20877
21173
  const summary = transcript.summary;
20878
21174
  const lines = [];
20879
- lines.push(`# ${summary.title ?? recording.title ?? recording.summaryTitle ?? "Summary"}`);
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}`);
21175
+ lines.push("# Summary");
20885
21176
  lines.push("");
20886
21177
  if (summary.tldr) {
20887
21178
  lines.push("## TL;DR", "");
@@ -20891,8 +21182,8 @@ function renderSummaryMarkdown(recording, transcript) {
20891
21182
  appendStringList(lines, "Topics", summary.topics);
20892
21183
  appendStringList(lines, "Decisions", summary.decisions);
20893
21184
  appendActionItems(lines, summary.actionItems);
20894
- appendTimeline(lines, summary.timeline);
20895
- appendQuotes(lines, summary.quotes);
21185
+ appendNotableQuotes(lines, summary.quotes);
21186
+ if (lines.length === 2) return null;
20896
21187
  if (lines[lines.length - 1] !== "") lines.push("");
20897
21188
  return lines.join("\n");
20898
21189
  }
@@ -20900,7 +21191,7 @@ function renderActionItemsMarkdown(summary) {
20900
21191
  if (!summary.actionItems || summary.actionItems.length === 0) return null;
20901
21192
  const lines = ["# Action Items", ""];
20902
21193
  for (const item of summary.actionItems) {
20903
- lines.push(`- ${item.who ? `${item.who} - ` : ""}${item.what}`);
21194
+ lines.push(actionItemLine(item));
20904
21195
  }
20905
21196
  lines.push("");
20906
21197
  return lines.join("\n");
@@ -20915,27 +21206,20 @@ function appendActionItems(lines, values) {
20915
21206
  if (!values || values.length === 0) return;
20916
21207
  lines.push("## Action Items", "");
20917
21208
  for (const item of values) {
20918
- lines.push(`- ${item.who ? `${item.who}: ` : ""}${item.what}`);
21209
+ lines.push(actionItemLine(item));
20919
21210
  }
20920
21211
  lines.push("");
20921
21212
  }
20922
- function appendTimeline(lines, values) {
21213
+ function appendNotableQuotes(lines, values) {
20923
21214
  if (!values || values.length === 0) return;
20924
- lines.push("## Timeline", "");
21215
+ lines.push("## Notable Quotes", "");
20925
21216
  for (const item of values) {
20926
- lines.push(
20927
- `- ${formatTimestamp(item.startMs)}-${formatTimestamp(item.endMs)}: ${item.title} - ${item.summary}`
20928
- );
21217
+ lines.push(`> ${item.speaker ? `${item.speaker}: ` : ""}${item.text}`);
20929
21218
  }
20930
21219
  lines.push("");
20931
21220
  }
20932
- function appendQuotes(lines, values) {
20933
- if (!values || values.length === 0) return;
20934
- lines.push("## Quotes", "");
20935
- for (const item of values) {
20936
- lines.push(`- ${item.speaker ? `${item.speaker}: ` : ""}"${item.text}"`);
20937
- }
20938
- lines.push("");
21221
+ function actionItemLine(item) {
21222
+ return `- ${item.who ? `${item.who}: ` : ""}${item.what}`;
20939
21223
  }
20940
21224
  function audioMetadata(audio) {
20941
21225
  return {
@@ -21331,20 +21615,20 @@ function renderEnvelope(envelope, opts) {
21331
21615
  `);
21332
21616
  }
21333
21617
  function renderHumanSuccess(command, data, opts) {
21334
- if (command === "auth login" && isRecord4(data)) {
21618
+ if (command === "auth login" && isRecord5(data)) {
21335
21619
  opts.stdout(`Signed in${typeof data.email === "string" ? ` as ${data.email}` : ""}
21336
21620
  `);
21337
21621
  return;
21338
21622
  }
21339
- if (command === "auth logout" && isRecord4(data)) {
21623
+ if (command === "auth logout" && isRecord5(data)) {
21340
21624
  opts.stdout(data.cleared ? "Signed out of Recappi CLI\n" : "No Recappi CLI session to clear\n");
21341
21625
  return;
21342
21626
  }
21343
- if (command === "auth import-macos" && isRecord4(data)) {
21627
+ if (command === "auth import-macos" && isRecord5(data)) {
21344
21628
  opts.stdout("Imported the Recappi Mini app session into Recappi CLI\n");
21345
21629
  return;
21346
21630
  }
21347
- if (command === "auth status" && isRecord4(data)) {
21631
+ if (command === "auth status" && isRecord5(data)) {
21348
21632
  if (data.loggedIn) {
21349
21633
  opts.stdout(`Signed in${typeof data.email === "string" ? ` as ${data.email}` : ""}
21350
21634
  `);
@@ -21353,7 +21637,7 @@ function renderHumanSuccess(command, data, opts) {
21353
21637
  opts.stdout("Not logged in\n");
21354
21638
  return;
21355
21639
  }
21356
- if (command === "account status" && isRecord4(data)) {
21640
+ if (command === "account status" && isRecord5(data)) {
21357
21641
  if (!data.loggedIn) {
21358
21642
  opts.stdout("Not logged in\n");
21359
21643
  return;
@@ -21364,7 +21648,7 @@ function renderHumanSuccess(command, data, opts) {
21364
21648
  `);
21365
21649
  if (typeof data.userId === "string") opts.stdout(` userId: ${data.userId}
21366
21650
  `);
21367
- const billing = isRecord4(data.billing) ? data.billing : {};
21651
+ const billing = isRecord5(data.billing) ? data.billing : {};
21368
21652
  if (typeof billing.tier === "string") opts.stdout(` plan: ${billing.tier}
21369
21653
  `);
21370
21654
  if (typeof billing.minutesUsed === "number") {
@@ -21377,7 +21661,7 @@ function renderHumanSuccess(command, data, opts) {
21377
21661
  opts.stdout(` storage: ${formatBytes2(billing.storageBytes)} / ${cap}
21378
21662
  `);
21379
21663
  }
21380
- const localStore = isRecord4(data.localStore) ? data.localStore : {};
21664
+ const localStore = isRecord5(data.localStore) ? data.localStore : {};
21381
21665
  if (typeof localStore.path === "string") opts.stdout(` localStore: ${localStore.path}
21382
21666
  `);
21383
21667
  opts.stdout(
@@ -21386,17 +21670,17 @@ function renderHumanSuccess(command, data, opts) {
21386
21670
  );
21387
21671
  return;
21388
21672
  }
21389
- if (command === "version" && isRecord4(data) && typeof data.version === "string") {
21673
+ if (command === "version" && isRecord5(data) && typeof data.version === "string") {
21390
21674
  opts.stdout(`${data.version}
21391
21675
  `);
21392
21676
  return;
21393
21677
  }
21394
- if (command === "doctor" && isRecord4(data) && Array.isArray(data.checks)) {
21678
+ if (command === "doctor" && isRecord5(data) && Array.isArray(data.checks)) {
21395
21679
  const status = typeof data.status === "string" ? data.status : "unknown";
21396
21680
  opts.stdout(`Doctor: ${status}
21397
21681
  `);
21398
21682
  for (const check2 of data.checks) {
21399
- if (!isRecord4(check2)) continue;
21683
+ if (!isRecord5(check2)) continue;
21400
21684
  const checkStatus = typeof check2.status === "string" ? check2.status : "unknown";
21401
21685
  const name = typeof check2.name === "string" ? check2.name : "check";
21402
21686
  const message = typeof check2.message === "string" ? ` \u2014 ${check2.message}` : "";
@@ -21407,14 +21691,14 @@ function renderHumanSuccess(command, data, opts) {
21407
21691
  }
21408
21692
  return;
21409
21693
  }
21410
- if (command === "transcript get" && isRecord4(data)) {
21694
+ if (command === "transcript get" && isRecord5(data)) {
21411
21695
  renderTranscriptHuman(data, opts);
21412
21696
  return;
21413
21697
  }
21414
- if (command === "recordings list" && isRecord4(data) && Array.isArray(data.items)) {
21698
+ if (command === "recordings list" && isRecord5(data) && Array.isArray(data.items)) {
21415
21699
  opts.stdout("Recordings:\n");
21416
21700
  for (const item of data.items) {
21417
- if (!isRecord4(item)) continue;
21701
+ if (!isRecord5(item)) continue;
21418
21702
  opts.stdout(` ${recordingLabel(item)}
21419
21703
  `);
21420
21704
  }
@@ -21426,7 +21710,7 @@ Next cursor: ${data.nextCursor}
21426
21710
  }
21427
21711
  return;
21428
21712
  }
21429
- if (command === "recordings get" && isRecord4(data)) {
21713
+ if (command === "recordings get" && isRecord5(data)) {
21430
21714
  opts.stdout(`${recordingTitle(data)}
21431
21715
  `);
21432
21716
  opts.stdout(` recordingId: ${String(data.recordingId)}
@@ -21448,7 +21732,7 @@ Next:
21448
21732
  }
21449
21733
  return;
21450
21734
  }
21451
- if (command === "recordings export" && isRecord4(data)) {
21735
+ if (command === "recordings export" && isRecord5(data)) {
21452
21736
  opts.stdout("Recording export ready\n");
21453
21737
  printStringField(opts, data, "recordingId");
21454
21738
  printStringField(opts, data, "exportDir");
@@ -21470,7 +21754,7 @@ Next:
21470
21754
  }
21471
21755
  return;
21472
21756
  }
21473
- if (command === "recordings retranscribe" && isRecord4(data)) {
21757
+ if (command === "recordings retranscribe" && isRecord5(data)) {
21474
21758
  opts.stdout("Transcription started\n");
21475
21759
  if (typeof data.recordingId === "string") opts.stdout(` recordingId: ${data.recordingId}
21476
21760
  `);
@@ -21493,7 +21777,7 @@ Next:
21493
21777
  }
21494
21778
  return;
21495
21779
  }
21496
- if (command === "recordings resummarize" && isRecord4(data)) {
21780
+ if (command === "recordings resummarize" && isRecord5(data)) {
21497
21781
  opts.stdout("Summary queued\n");
21498
21782
  if (typeof data.recordingId === "string") opts.stdout(` recordingId: ${data.recordingId}
21499
21783
  `);
@@ -21511,9 +21795,9 @@ Next:
21511
21795
  }
21512
21796
  return;
21513
21797
  }
21514
- if (command === "dashboard stats" && isRecord4(data)) {
21515
- const recordings = isRecord4(data.recordings) ? data.recordings : {};
21516
- const jobs = isRecord4(data.jobs) ? data.jobs : {};
21798
+ if (command === "dashboard stats" && isRecord5(data)) {
21799
+ const recordings = isRecord5(data.recordings) ? data.recordings : {};
21800
+ const jobs = isRecord5(data.jobs) ? data.jobs : {};
21517
21801
  opts.stdout(
21518
21802
  `Recordings: ${numberText(recordings.total)} total, ${numberText(recordings.ready)} ready
21519
21803
  `
@@ -21556,7 +21840,7 @@ Next:
21556
21840
  }
21557
21841
  return;
21558
21842
  }
21559
- if (command === "record" && isRecord4(data)) {
21843
+ if (command === "record" && isRecord5(data)) {
21560
21844
  opts.stdout("Recording complete\n");
21561
21845
  if (typeof data.recordingId === "string") opts.stdout(` recordingId: ${data.recordingId}
21562
21846
  `);
@@ -21575,14 +21859,14 @@ Next:
21575
21859
  if (Array.isArray(data.artifacts) && data.artifacts.length > 0) {
21576
21860
  opts.stdout(" artifacts:\n");
21577
21861
  for (const artifact of data.artifacts) {
21578
- if (!isRecord4(artifact)) continue;
21862
+ if (!isRecord5(artifact)) continue;
21579
21863
  const kind = typeof artifact.kind === "string" ? artifact.kind : "artifact";
21580
21864
  const localPath = typeof artifact.localPath === "string" ? artifact.localPath : "";
21581
21865
  opts.stdout(` - ${kind}: ${localPath}
21582
21866
  `);
21583
21867
  }
21584
21868
  }
21585
- const cloudHandoffError = isRecord4(data.cloudHandoffError) ? data.cloudHandoffError : void 0;
21869
+ const cloudHandoffError = isRecord5(data.cloudHandoffError) ? data.cloudHandoffError : void 0;
21586
21870
  if (cloudHandoffError && typeof cloudHandoffError.message === "string") {
21587
21871
  opts.stderr(`Cloud handoff failed: ${cloudHandoffError.message}
21588
21872
  `);
@@ -21607,7 +21891,7 @@ Next:
21607
21891
  }
21608
21892
  return;
21609
21893
  }
21610
- if (command === "audio" && isRecord4(data)) {
21894
+ if (command === "audio" && isRecord5(data)) {
21611
21895
  const action = typeof data.action === "string" ? data.action : "download";
21612
21896
  opts.stdout(
21613
21897
  action === "open" ? "Audio opened\n" : action === "reveal" ? "Audio revealed\n" : "Audio ready\n"
@@ -21622,7 +21906,7 @@ Next:
21622
21906
  }
21623
21907
  return;
21624
21908
  }
21625
- if ((command === "jobs wait" || command === "upload") && isRecord4(data)) {
21909
+ if ((command === "jobs wait" || command === "upload") && isRecord5(data)) {
21626
21910
  if (typeof data.transcriptId === "string") {
21627
21911
  opts.stdout("Transcript ready\n");
21628
21912
  if (typeof data.recordingId === "string" && typeof data.origin === "string") {
@@ -21647,10 +21931,10 @@ Next:
21647
21931
  }
21648
21932
  return;
21649
21933
  }
21650
- if (command === "schema" && isRecord4(data) && Array.isArray(data.commands)) {
21934
+ if (command === "schema" && isRecord5(data) && Array.isArray(data.commands)) {
21651
21935
  opts.stdout("Commands:\n");
21652
21936
  for (const entry of data.commands) {
21653
- if (!isRecord4(entry) || typeof entry.name !== "string") continue;
21937
+ if (!isRecord5(entry) || typeof entry.name !== "string") continue;
21654
21938
  const summary = typeof entry.summary === "string" ? ` \u2014 ${entry.summary}` : "";
21655
21939
  opts.stdout(` ${entry.name}${summary}
21656
21940
  `);
@@ -21660,9 +21944,9 @@ Next:
21660
21944
  }
21661
21945
  if (Array.isArray(entry.examples) && entry.examples.length > 0) {
21662
21946
  const first = entry.examples.find(
21663
- (example) => isRecord4(example) && typeof example.command === "string"
21947
+ (example) => isRecord5(example) && typeof example.command === "string"
21664
21948
  );
21665
- if (isRecord4(first) && typeof first.command === "string") {
21949
+ if (isRecord5(first) && typeof first.command === "string") {
21666
21950
  opts.stdout(` example: ${first.command}
21667
21951
  `);
21668
21952
  }
@@ -21794,7 +22078,7 @@ function renderTranscriptHuman(data, opts) {
21794
22078
  const segments = Array.isArray(data.segments) ? data.segments : [];
21795
22079
  let printedBody = false;
21796
22080
  for (const segment of segments) {
21797
- if (!isRecord4(segment) || typeof segment.text !== "string") continue;
22081
+ if (!isRecord5(segment) || typeof segment.text !== "string") continue;
21798
22082
  const clock = typeof segment.startMs === "number" ? `[${formatClock(segment.startMs / 1e3)}] ` : "";
21799
22083
  const speaker = typeof segment.speaker === "string" ? `${segment.speaker}: ` : "";
21800
22084
  opts.stdout(`${clock}${speaker}${segment.text}
@@ -21806,7 +22090,7 @@ function renderTranscriptHuman(data, opts) {
21806
22090
  `);
21807
22091
  printedBody = true;
21808
22092
  }
21809
- const summary = isRecord4(data.summary) ? data.summary : void 0;
22093
+ const summary = isRecord5(data.summary) ? data.summary : void 0;
21810
22094
  if (!summary || summary.status !== "succeeded") return;
21811
22095
  if (typeof summary.tldr === "string" && summary.tldr.length > 0) {
21812
22096
  opts.stdout(`
@@ -21824,7 +22108,7 @@ Summary:
21824
22108
  if (Array.isArray(summary.actionItems) && summary.actionItems.length > 0) {
21825
22109
  opts.stdout("\nAction items:\n");
21826
22110
  for (const item of summary.actionItems) {
21827
- if (!isRecord4(item) || typeof item.what !== "string") continue;
22111
+ if (!isRecord5(item) || typeof item.what !== "string") continue;
21828
22112
  const who = typeof item.who === "string" ? `${item.who}: ` : "";
21829
22113
  opts.stdout(` - ${who}${item.what}
21830
22114
  `);
@@ -21899,7 +22183,7 @@ function applyFields(command, data, fields, compact) {
21899
22183
  };
21900
22184
  return compact ? compactData(filtered2) : filtered2;
21901
22185
  }
21902
- if (!isRecord4(data)) return data;
22186
+ if (!isRecord5(data)) return data;
21903
22187
  const allowed = new Set(Object.keys(data));
21904
22188
  assertKnownFields(fields, allowed);
21905
22189
  const filtered = pickFields(data, fields);
@@ -21924,7 +22208,7 @@ function compactData(value) {
21924
22208
  if (Array.isArray(value)) {
21925
22209
  return value.map(compactData).filter((item) => item !== void 0);
21926
22210
  }
21927
- if (isRecord4(value)) {
22211
+ if (isRecord5(value)) {
21928
22212
  const out = {};
21929
22213
  for (const [key, child] of Object.entries(value)) {
21930
22214
  const compacted = compactData(child);
@@ -21942,16 +22226,16 @@ function stableStringify(value, compact) {
21942
22226
  }
21943
22227
  function sortKeys(value) {
21944
22228
  if (Array.isArray(value)) return value.map(sortKeys);
21945
- if (!isRecord4(value)) return value;
22229
+ if (!isRecord5(value)) return value;
21946
22230
  return Object.fromEntries(
21947
22231
  Object.keys(value).sort().map((key) => [key, sortKeys(value[key])])
21948
22232
  );
21949
22233
  }
21950
- function isRecord4(value) {
22234
+ function isRecord5(value) {
21951
22235
  return typeof value === "object" && value !== null && !Array.isArray(value);
21952
22236
  }
21953
22237
  function isUploadBatch(value) {
21954
- return isRecord4(value) && Array.isArray(value.successes) && Array.isArray(value.failures);
22238
+ return isRecord5(value) && Array.isArray(value.successes) && Array.isArray(value.failures);
21955
22239
  }
21956
22240
 
21957
22241
  // src/schema.ts
@@ -22006,11 +22290,11 @@ function buildSchemaDocument(program) {
22006
22290
  event: toJsonSchema(operationEventSchema)
22007
22291
  };
22008
22292
  }
22009
- function walkCommands(command, path7, out) {
22293
+ function walkCommands(command, path8, out) {
22010
22294
  for (const sub of subcommandsOf(command)) {
22011
22295
  const name = sub.name();
22012
22296
  if (name === "help") continue;
22013
- const fullPath = [...path7, name];
22297
+ const fullPath = [...path8, name];
22014
22298
  const children = subcommandsOf(sub).filter((child) => child.name() !== "help");
22015
22299
  if (children.length === 0) {
22016
22300
  out.push(leafCommandDoc(sub, fullPath.join(" ")));
@@ -22288,7 +22572,7 @@ var MiniSidecarClient = class {
22288
22572
  }
22289
22573
  };
22290
22574
  function sidecarErrorToCliError(error51) {
22291
- const data = isRecord6(error51.data) ? error51.data : void 0;
22575
+ const data = isRecord7(error51.data) ? error51.data : void 0;
22292
22576
  const maybeCode = typeof data?.cliCode === "string" ? cliErrorCodeSchema.safeParse(data.cliCode) : void 0;
22293
22577
  const hint = typeof data?.recovery === "string" ? data.recovery : void 0;
22294
22578
  const retryable = typeof data?.retryable === "boolean" ? data.retryable : error51.code >= -32099 && error51.code <= -32e3;
@@ -22305,7 +22589,7 @@ function sidecarErrorToCliError(error51) {
22305
22589
  retryable
22306
22590
  });
22307
22591
  }
22308
- function isRecord6(value) {
22592
+ function isRecord7(value) {
22309
22593
  return typeof value === "object" && value !== null && !Array.isArray(value);
22310
22594
  }
22311
22595
  function spawnMiniSidecar(opts) {
@@ -22412,8 +22696,8 @@ function requestLaunchServicesSidecarShutdown(input) {
22412
22696
  } catch {
22413
22697
  }
22414
22698
  }
22415
- function createFifo(path7) {
22416
- const result = spawnSync("mkfifo", [path7], { encoding: "utf8" });
22699
+ function createFifo(path8) {
22700
+ const result = spawnSync("mkfifo", [path8], { encoding: "utf8" });
22417
22701
  if (result.status !== 0) {
22418
22702
  throw cliError("record.helper_unavailable", "Recappi recording helper could not start.", {
22419
22703
  hint: result.stderr || "Could not create the local recorder pipes. Try again."
@@ -22949,14 +23233,14 @@ async function startRecordSessionOnce(opts) {
22949
23233
  }
22950
23234
  }
22951
23235
  function isPermissionRestartRequiredError(error51, permissionName) {
22952
- const root = isRecord7(error51) ? error51 : void 0;
22953
- const descriptor = isRecord7(root?.descriptor) ? root.descriptor : void 0;
23236
+ const root = isRecord8(error51) ? error51 : void 0;
23237
+ const descriptor = isRecord8(root?.descriptor) ? root.descriptor : void 0;
22954
23238
  if (descriptor?.code !== "record.permission_required") return false;
22955
- const sidecarError = isRecord7(root?.data) ? root.data : void 0;
22956
- const sidecarData = isRecord7(sidecarError?.data) ? sidecarError.data : void 0;
23239
+ const sidecarError = isRecord8(root?.data) ? root.data : void 0;
23240
+ const sidecarData = isRecord8(sidecarError?.data) ? sidecarError.data : void 0;
22957
23241
  return sidecarData?.permission === permissionName && (sidecarData.requiresProcessRestart === true || sidecarData.requiresProcessRestart === "true");
22958
23242
  }
22959
- function isRecord7(value) {
23243
+ function isRecord8(value) {
22960
23244
  return typeof value === "object" && value !== null && !Array.isArray(value);
22961
23245
  }
22962
23246
  function normalizeSidecarSources(sources) {
@@ -23037,9 +23321,9 @@ function resolveSidecarCommand(opts) {
23037
23321
  hint: `No bundled helper is registered for ${platform}. Set ${SIDECAR_COMMAND_ENV} to a compatible helper when one is available.`
23038
23322
  });
23039
23323
  }
23040
- function ensureBundledHelperExecutable(path7, opts = {}) {
23041
- if (process.platform === "darwin" && path7.endsWith(".app")) {
23042
- const stableApp = ensureStableDarwinHelperApp(path7, opts);
23324
+ function ensureBundledHelperExecutable(path8, opts = {}) {
23325
+ if (process.platform === "darwin" && path8.endsWith(".app")) {
23326
+ const stableApp = ensureStableDarwinHelperApp(path8, opts);
23043
23327
  const executable = darwinAppExecutablePath(stableApp);
23044
23328
  if (!existsSync(executable)) {
23045
23329
  throw cliError("record.helper_unavailable", "Recappi recording helper is not available.", {
@@ -23049,19 +23333,19 @@ function ensureBundledHelperExecutable(path7, opts = {}) {
23049
23333
  ensureExecutableMode(executable);
23050
23334
  return stableApp;
23051
23335
  }
23052
- if (process.platform === "win32") return path7;
23053
- ensureExecutableMode(path7);
23054
- return path7;
23336
+ if (process.platform === "win32") return path8;
23337
+ ensureExecutableMode(path8);
23338
+ return path8;
23055
23339
  }
23056
- function ensureExecutableMode(path7) {
23057
- const mode = statSync(path7).mode;
23340
+ function ensureExecutableMode(path8) {
23341
+ const mode = statSync(path8).mode;
23058
23342
  if ((mode & 73) !== 0) return;
23059
23343
  try {
23060
- chmodSync(path7, mode | 493);
23344
+ chmodSync(path8, mode | 493);
23061
23345
  } catch (error51) {
23062
23346
  const message = error51 instanceof Error ? error51.message : String(error51);
23063
23347
  throw cliError("record.helper_unavailable", "Recappi recording helper is not executable.", {
23064
- hint: `Could not make bundled helper executable at ${path7}: ${message}. Reinstall recappi, or set ${SIDECAR_COMMAND_ENV} to a compatible helper.`
23348
+ hint: `Could not make bundled helper executable at ${path8}: ${message}. Reinstall recappi, or set ${SIDECAR_COMMAND_ENV} to a compatible helper.`
23065
23349
  });
23066
23350
  }
23067
23351
  }
@@ -23106,14 +23390,14 @@ function helperSourceSignature(sourceApp) {
23106
23390
  codeSignature: existsSync(signaturePath) ? fileDigest(signaturePath) : null
23107
23391
  });
23108
23392
  }
23109
- function fileDigest(path7) {
23393
+ function fileDigest(path8) {
23110
23394
  const hash2 = createHash("sha256");
23111
- hash2.update(readFileSync2(path7));
23395
+ hash2.update(readFileSync2(path8));
23112
23396
  return hash2.digest("hex");
23113
23397
  }
23114
- function readTextIfExists(path7) {
23398
+ function readTextIfExists(path8) {
23115
23399
  try {
23116
- return readFileSync2(path7, "utf8");
23400
+ return readFileSync2(path8, "utf8");
23117
23401
  } catch {
23118
23402
  return null;
23119
23403
  }
@@ -23407,6 +23691,13 @@ async function runCli(deps = {}) {
23407
23691
  await runDashboard2({
23408
23692
  fetchJobs: () => client.listJobs({ status: "active", limit: 20 }),
23409
23693
  fetchRecordings: ({ cursor, limit = DASHBOARD_RECORDINGS_PAGE_SIZE } = {}) => client.listRecordings({ limit, cursor }),
23694
+ fetchCachedRecordings: account ? ({ limit = DASHBOARD_RECORDINGS_PAGE_SIZE } = {}) => listCachedRecordingSessions({
23695
+ account,
23696
+ origin: auth.origin,
23697
+ limit,
23698
+ env: deps.env,
23699
+ homeDir: deps.homeDir
23700
+ }) : void 0,
23410
23701
  fetchDashboardStats: () => client.dashboardStats(),
23411
23702
  fetchAccountStatus: () => client.accountStatus(),
23412
23703
  fetchTranscript: (transcriptId) => client.getTranscript(transcriptId),