granola-toolkit 0.5.0 → 0.7.0

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.
Files changed (2) hide show
  1. package/dist/cli.js +46 -9
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
3
2
  import { existsSync } from "node:fs";
3
+ import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
4
4
  import { homedir } from "node:os";
5
5
  import { dirname, join } from "node:path";
6
6
  import { createHash } from "node:crypto";
@@ -37,7 +37,6 @@ function compareStrings(left, right) {
37
37
  }
38
38
  function firstExistingPath(candidates) {
39
39
  for (const candidate of candidates) if (existsSync(candidate)) return candidate;
40
- return candidates[0];
41
40
  }
42
41
  function granolaSupabaseCandidates() {
43
42
  const home = homedir();
@@ -383,10 +382,13 @@ function parseSimpleToml(contents) {
383
382
  return values;
384
383
  }
385
384
  async function loadTomlConfig(configPath) {
386
- if (configPath) return {
387
- path: configPath,
388
- values: parseSimpleToml(await readUtf8(configPath))
389
- };
385
+ if (configPath) {
386
+ if (!existsSync(configPath)) throw new Error(`config file not found: ${configPath}`);
387
+ return {
388
+ path: configPath,
389
+ values: parseSimpleToml(await readUtf8(configPath))
390
+ };
391
+ }
390
392
  const candidates = [join(process.cwd(), ".granola.toml"), join(homedir(), ".granola.toml")];
391
393
  for (const candidate of candidates) if (existsSync(candidate)) return {
392
394
  path: candidate,
@@ -818,6 +820,7 @@ const notesCommand = {
818
820
  subcommandFlags: commandFlags
819
821
  });
820
822
  if (!config.supabase) throw new Error(`supabase.json not found. Pass --supabase or create .granola.toml. Expected locations include: ${granolaSupabaseCandidates().join(", ")}`);
823
+ if (!existsSync(config.supabase)) throw new Error(`supabase.json not found: ${config.supabase}`);
821
824
  debug(config.debug, "using config", config.configFileUsed ?? "(none)");
822
825
  debug(config.debug, "supabase", config.supabase);
823
826
  debug(config.debug, "timeoutMs", config.notes.timeoutMs);
@@ -903,7 +906,40 @@ function parseCacheContents(contents) {
903
906
  }
904
907
  //#endregion
905
908
  //#region src/transcripts.ts
906
- function buildTranscriptExport(document, segments) {
909
+ function transcriptSegmentKey(segment) {
910
+ if (segment.id) return `id:${segment.id}`;
911
+ return [
912
+ segment.documentId,
913
+ segment.source,
914
+ segment.startTimestamp,
915
+ segment.endTimestamp
916
+ ].join("|");
917
+ }
918
+ function compareSegmentTimestamps(left, right) {
919
+ if (left === right) return 0;
920
+ const leftTime = Date.parse(left);
921
+ const rightTime = Date.parse(right);
922
+ if (!Number.isNaN(leftTime) && !Number.isNaN(rightTime)) return leftTime - rightTime;
923
+ return compareStrings(left, right);
924
+ }
925
+ function compareTranscriptSegments(left, right) {
926
+ return compareSegmentTimestamps(left.startTimestamp, right.startTimestamp) || compareSegmentTimestamps(left.endTimestamp, right.endTimestamp) || compareStrings(left.source, right.source) || compareStrings(left.id, right.id) || compareStrings(left.text, right.text);
927
+ }
928
+ function preferredTranscriptSegment(current, candidate) {
929
+ if (!current) return candidate;
930
+ if (candidate.isFinal !== current.isFinal) return candidate.isFinal ? candidate : current;
931
+ return compareSegmentTimestamps(candidate.endTimestamp, current.endTimestamp) > 0 || candidate.text.length > current.text.length ? candidate : current;
932
+ }
933
+ function normaliseTranscriptSegments(segments) {
934
+ const selected = /* @__PURE__ */ new Map();
935
+ for (const segment of [...segments].sort(compareTranscriptSegments)) {
936
+ const key = transcriptSegmentKey(segment);
937
+ const current = selected.get(key);
938
+ selected.set(key, preferredTranscriptSegment(current, segment));
939
+ }
940
+ return [...selected.values()].sort(compareTranscriptSegments);
941
+ }
942
+ function buildTranscriptExport(document, segments, rawSegments = segments) {
907
943
  const renderedSegments = segments.map((segment) => ({
908
944
  endTimestamp: segment.endTimestamp,
909
945
  id: segment.id,
@@ -918,7 +954,7 @@ function buildTranscriptExport(document, segments) {
918
954
  id: document.id,
919
955
  raw: {
920
956
  document,
921
- segments
957
+ segments: rawSegments
922
958
  },
923
959
  segments: renderedSegments,
924
960
  title: document.title,
@@ -987,7 +1023,7 @@ async function writeTranscripts(cacheData, outputDir, format = "text") {
987
1023
  title: documentId,
988
1024
  updatedAt: ""
989
1025
  };
990
- const content = renderTranscriptExport(buildTranscriptExport(document, segments), format);
1026
+ const content = renderTranscriptExport(buildTranscriptExport(document, normaliseTranscriptSegments(segments), segments), format);
991
1027
  if (!content) return [];
992
1028
  return [{
993
1029
  content,
@@ -1034,6 +1070,7 @@ const transcriptsCommand = {
1034
1070
  subcommandFlags: commandFlags
1035
1071
  });
1036
1072
  if (!config.transcripts.cacheFile) throw new Error(`Granola cache file not found. Pass --cache or create .granola.toml. Expected locations include: ${granolaCacheCandidates().join(", ")}`);
1073
+ if (!existsSync(config.transcripts.cacheFile)) throw new Error(`Granola cache file not found: ${config.transcripts.cacheFile}`);
1037
1074
  debug(config.debug, "using config", config.configFileUsed ?? "(none)");
1038
1075
  debug(config.debug, "cacheFile", config.transcripts.cacheFile);
1039
1076
  debug(config.debug, "output", config.transcripts.output);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "granola-toolkit",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "CLI toolkit for exporting and working with Granola notes and transcripts",
5
5
  "keywords": [
6
6
  "cli",