granola-toolkit 0.15.0 → 0.17.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.
- package/dist/cli.js +370 -136
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -689,96 +689,34 @@ var AuthenticatedHttpClient = class {
|
|
|
689
689
|
};
|
|
690
690
|
//#endregion
|
|
691
691
|
//#region src/client/default.ts
|
|
692
|
-
async function
|
|
692
|
+
async function inspectDefaultGranolaAuth(config) {
|
|
693
|
+
const storedSession = await createDefaultSessionStore().readSession();
|
|
694
|
+
const hasStoredSession = Boolean(storedSession?.accessToken.trim());
|
|
695
|
+
return {
|
|
696
|
+
mode: hasStoredSession ? "stored-session" : "supabase-file",
|
|
697
|
+
storedSessionAvailable: hasStoredSession,
|
|
698
|
+
supabasePath: config.supabase || void 0
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
async function createDefaultGranolaRuntime(config, logger = console) {
|
|
693
702
|
const sessionStore = createDefaultSessionStore();
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
if (!
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
703
|
+
const auth = await inspectDefaultGranolaAuth(config);
|
|
704
|
+
const hasStoredSession = auth.storedSessionAvailable;
|
|
705
|
+
if (!hasStoredSession && !config.supabase) throw new Error(`supabase.json not found. Pass --supabase or create .granola.toml. Expected locations include: ${granolaSupabaseCandidates().join(", ")}`);
|
|
706
|
+
if (!hasStoredSession && config.supabase && !existsSync(config.supabase)) throw new Error(`supabase.json not found: ${config.supabase}`);
|
|
707
|
+
return {
|
|
708
|
+
auth,
|
|
709
|
+
client: new GranolaApiClient(new AuthenticatedHttpClient({
|
|
710
|
+
logger,
|
|
711
|
+
tokenProvider: hasStoredSession ? new StoredSessionTokenProvider(sessionStore, { source: config.supabase && existsSync(config.supabase) ? new SupabaseFileSessionSource(config.supabase) : void 0 }) : new CachedTokenProvider(new SupabaseFileTokenSource(config.supabase), new NoopTokenStore())
|
|
712
|
+
}))
|
|
713
|
+
};
|
|
701
714
|
}
|
|
702
715
|
async function loadOptionalGranolaCache(cacheFile) {
|
|
703
716
|
if (!cacheFile || !existsSync(cacheFile)) return;
|
|
704
717
|
return parseCacheContents(await readFile(cacheFile, "utf8"));
|
|
705
718
|
}
|
|
706
719
|
//#endregion
|
|
707
|
-
//#region src/config.ts
|
|
708
|
-
function pickString(value) {
|
|
709
|
-
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
710
|
-
}
|
|
711
|
-
function pickBoolean(value) {
|
|
712
|
-
return typeof value === "boolean" ? value : void 0;
|
|
713
|
-
}
|
|
714
|
-
function parseTomlScalar(rawValue) {
|
|
715
|
-
const value = rawValue.trim();
|
|
716
|
-
if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) {
|
|
717
|
-
if (value.startsWith("\"")) try {
|
|
718
|
-
return JSON.parse(value);
|
|
719
|
-
} catch {
|
|
720
|
-
return value.slice(1, -1);
|
|
721
|
-
}
|
|
722
|
-
return value.slice(1, -1);
|
|
723
|
-
}
|
|
724
|
-
if (/^(true|false)$/i.test(value)) return value.toLowerCase() === "true";
|
|
725
|
-
if (/^-?\d+(?:\.\d+)?$/.test(value)) return Number(value);
|
|
726
|
-
return value;
|
|
727
|
-
}
|
|
728
|
-
function parseSimpleToml(contents) {
|
|
729
|
-
const values = {};
|
|
730
|
-
for (const rawLine of contents.split(/\r?\n/)) {
|
|
731
|
-
const line = rawLine.trim();
|
|
732
|
-
if (!line || line.startsWith("#")) continue;
|
|
733
|
-
const match = /^([A-Za-z0-9_-]+)\s*=\s*(.+)$/.exec(line);
|
|
734
|
-
if (!match) continue;
|
|
735
|
-
const [, key = "", rawValue = ""] = match;
|
|
736
|
-
values[key] = parseTomlScalar(rawValue);
|
|
737
|
-
}
|
|
738
|
-
return values;
|
|
739
|
-
}
|
|
740
|
-
async function loadTomlConfig(configPath) {
|
|
741
|
-
if (configPath) {
|
|
742
|
-
if (!existsSync(configPath)) throw new Error(`config file not found: ${configPath}`);
|
|
743
|
-
return {
|
|
744
|
-
path: configPath,
|
|
745
|
-
values: parseSimpleToml(await readUtf8(configPath))
|
|
746
|
-
};
|
|
747
|
-
}
|
|
748
|
-
const candidates = [join(process.cwd(), ".granola.toml"), join(homedir(), ".granola.toml")];
|
|
749
|
-
for (const candidate of candidates) if (existsSync(candidate)) return {
|
|
750
|
-
path: candidate,
|
|
751
|
-
values: parseSimpleToml(await readUtf8(candidate))
|
|
752
|
-
};
|
|
753
|
-
return { values: {} };
|
|
754
|
-
}
|
|
755
|
-
function envFlag(value) {
|
|
756
|
-
if (value == null) return;
|
|
757
|
-
if (/^(1|true|yes|on)$/i.test(value)) return true;
|
|
758
|
-
if (/^(0|false|no|off)$/i.test(value)) return false;
|
|
759
|
-
}
|
|
760
|
-
async function loadConfig(options) {
|
|
761
|
-
const env = options.env ?? process.env;
|
|
762
|
-
const config = await loadTomlConfig(pickString(options.globalFlags.config));
|
|
763
|
-
const configValues = config.values;
|
|
764
|
-
const defaultSupabase = firstExistingPath(granolaSupabaseCandidates());
|
|
765
|
-
const defaultCache = firstExistingPath(granolaCacheCandidates());
|
|
766
|
-
const timeoutValue = pickString(options.subcommandFlags.timeout) ?? pickString(env.TIMEOUT) ?? pickString(configValues.timeout) ?? "2m";
|
|
767
|
-
return {
|
|
768
|
-
configFileUsed: config.path,
|
|
769
|
-
debug: pickBoolean(options.globalFlags.debug) ?? envFlag(env.DEBUG_MODE) ?? pickBoolean(configValues.debug) ?? false,
|
|
770
|
-
notes: {
|
|
771
|
-
output: pickString(options.subcommandFlags.output) ?? pickString(env.OUTPUT) ?? pickString(configValues.output) ?? "./notes",
|
|
772
|
-
timeoutMs: parseDuration(timeoutValue)
|
|
773
|
-
},
|
|
774
|
-
supabase: pickString(options.globalFlags.supabase) ?? pickString(env.SUPABASE_FILE) ?? pickString(configValues.supabase) ?? defaultSupabase,
|
|
775
|
-
transcripts: {
|
|
776
|
-
cacheFile: pickString(options.subcommandFlags.cache) ?? pickString(env.CACHE_FILE) ?? pickString(configValues["cache-file"]) ?? pickString(configValues.cacheFile) ?? defaultCache ?? "",
|
|
777
|
-
output: pickString(options.subcommandFlags.output) ?? pickString(env.TRANSCRIPT_OUTPUT) ?? pickString(configValues["transcript-output"]) ?? pickString(configValues.transcriptOutput) ?? "./transcripts"
|
|
778
|
-
}
|
|
779
|
-
};
|
|
780
|
-
}
|
|
781
|
-
//#endregion
|
|
782
720
|
//#region src/export-state.ts
|
|
783
721
|
const EXPORT_STATE_VERSION = 1;
|
|
784
722
|
function exportStatePath(outputDir, kind) {
|
|
@@ -1524,6 +1462,285 @@ function renderMeetingTranscript(document, cacheData, format = "text") {
|
|
|
1524
1462
|
return renderTranscriptExport(transcript, format);
|
|
1525
1463
|
}
|
|
1526
1464
|
//#endregion
|
|
1465
|
+
//#region src/app/core.ts
|
|
1466
|
+
function transcriptCount(cacheData) {
|
|
1467
|
+
return Object.values(cacheData.transcripts).filter((segments) => segments.length > 0).length;
|
|
1468
|
+
}
|
|
1469
|
+
function cloneExportState(state) {
|
|
1470
|
+
return state ? { ...state } : void 0;
|
|
1471
|
+
}
|
|
1472
|
+
function cloneState(state) {
|
|
1473
|
+
return {
|
|
1474
|
+
auth: { ...state.auth },
|
|
1475
|
+
cache: { ...state.cache },
|
|
1476
|
+
config: {
|
|
1477
|
+
...state.config,
|
|
1478
|
+
notes: { ...state.config.notes },
|
|
1479
|
+
transcripts: { ...state.config.transcripts }
|
|
1480
|
+
},
|
|
1481
|
+
documents: { ...state.documents },
|
|
1482
|
+
exports: {
|
|
1483
|
+
notes: cloneExportState(state.exports.notes),
|
|
1484
|
+
transcripts: cloneExportState(state.exports.transcripts)
|
|
1485
|
+
},
|
|
1486
|
+
ui: { ...state.ui }
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
function defaultState(config, auth, surface) {
|
|
1490
|
+
return {
|
|
1491
|
+
auth: { ...auth },
|
|
1492
|
+
cache: {
|
|
1493
|
+
configured: Boolean(config.transcripts.cacheFile),
|
|
1494
|
+
documentCount: 0,
|
|
1495
|
+
filePath: config.transcripts.cacheFile || void 0,
|
|
1496
|
+
loaded: false,
|
|
1497
|
+
transcriptCount: 0
|
|
1498
|
+
},
|
|
1499
|
+
config: {
|
|
1500
|
+
...config,
|
|
1501
|
+
notes: { ...config.notes },
|
|
1502
|
+
transcripts: { ...config.transcripts }
|
|
1503
|
+
},
|
|
1504
|
+
documents: {
|
|
1505
|
+
count: 0,
|
|
1506
|
+
loaded: false
|
|
1507
|
+
},
|
|
1508
|
+
exports: {},
|
|
1509
|
+
ui: {
|
|
1510
|
+
surface,
|
|
1511
|
+
view: "idle"
|
|
1512
|
+
}
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
var GranolaApp = class {
|
|
1516
|
+
#cacheData;
|
|
1517
|
+
#cacheResolved = false;
|
|
1518
|
+
#granolaClient;
|
|
1519
|
+
#documents;
|
|
1520
|
+
#state;
|
|
1521
|
+
constructor(config, deps, options = {}) {
|
|
1522
|
+
this.config = config;
|
|
1523
|
+
this.deps = deps;
|
|
1524
|
+
this.#state = defaultState(config, deps.auth, options.surface ?? "cli");
|
|
1525
|
+
}
|
|
1526
|
+
getState() {
|
|
1527
|
+
return cloneState(this.#state);
|
|
1528
|
+
}
|
|
1529
|
+
setUiState(patch) {
|
|
1530
|
+
this.#state.ui = {
|
|
1531
|
+
...this.#state.ui,
|
|
1532
|
+
...patch
|
|
1533
|
+
};
|
|
1534
|
+
return this.getState();
|
|
1535
|
+
}
|
|
1536
|
+
nowIso() {
|
|
1537
|
+
return (this.deps.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
|
|
1538
|
+
}
|
|
1539
|
+
async getGranolaClient() {
|
|
1540
|
+
if (this.#granolaClient) return this.#granolaClient;
|
|
1541
|
+
if (this.deps.granolaClient) {
|
|
1542
|
+
this.#granolaClient = this.deps.granolaClient;
|
|
1543
|
+
return this.#granolaClient;
|
|
1544
|
+
}
|
|
1545
|
+
if (!this.deps.createGranolaClient) throw new Error("Granola API client is not configured");
|
|
1546
|
+
const runtime = await this.deps.createGranolaClient();
|
|
1547
|
+
this.#granolaClient = runtime.client;
|
|
1548
|
+
this.#state.auth = { ...runtime.auth };
|
|
1549
|
+
return this.#granolaClient;
|
|
1550
|
+
}
|
|
1551
|
+
missingCacheError() {
|
|
1552
|
+
return /* @__PURE__ */ new Error(`Granola cache file not found. Pass --cache or create .granola.toml. Expected locations include: ${granolaCacheCandidates().join(", ")}`);
|
|
1553
|
+
}
|
|
1554
|
+
async listDocuments() {
|
|
1555
|
+
if (this.#documents) return this.#documents;
|
|
1556
|
+
const documents = await (await this.getGranolaClient()).listDocuments({ timeoutMs: this.config.notes.timeoutMs });
|
|
1557
|
+
this.#documents = documents;
|
|
1558
|
+
this.#state.documents = {
|
|
1559
|
+
count: documents.length,
|
|
1560
|
+
loaded: true,
|
|
1561
|
+
loadedAt: this.nowIso()
|
|
1562
|
+
};
|
|
1563
|
+
return documents;
|
|
1564
|
+
}
|
|
1565
|
+
async loadCache(options = {}) {
|
|
1566
|
+
if (this.#cacheResolved) {
|
|
1567
|
+
if (options.required && !this.#cacheData) throw this.missingCacheError();
|
|
1568
|
+
return this.#cacheData;
|
|
1569
|
+
}
|
|
1570
|
+
const cacheFile = this.config.transcripts.cacheFile || void 0;
|
|
1571
|
+
if (!cacheFile) {
|
|
1572
|
+
this.#cacheResolved = true;
|
|
1573
|
+
if (options.required) throw this.missingCacheError();
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
if (!existsSync(cacheFile)) throw new Error(`Granola cache file not found: ${cacheFile}`);
|
|
1577
|
+
const cacheData = await this.deps.cacheLoader(cacheFile);
|
|
1578
|
+
this.#cacheResolved = true;
|
|
1579
|
+
this.#cacheData = cacheData;
|
|
1580
|
+
this.#state.cache = {
|
|
1581
|
+
configured: true,
|
|
1582
|
+
documentCount: cacheData ? Object.keys(cacheData.documents).length : 0,
|
|
1583
|
+
filePath: cacheFile,
|
|
1584
|
+
loaded: Boolean(cacheData),
|
|
1585
|
+
loadedAt: cacheData ? this.nowIso() : void 0,
|
|
1586
|
+
transcriptCount: cacheData ? transcriptCount(cacheData) : 0
|
|
1587
|
+
};
|
|
1588
|
+
if (options.required && !cacheData) throw this.missingCacheError();
|
|
1589
|
+
return cacheData;
|
|
1590
|
+
}
|
|
1591
|
+
async listMeetings(options = {}) {
|
|
1592
|
+
const meetings = listMeetings(await this.listDocuments(), {
|
|
1593
|
+
cacheData: await this.loadCache(),
|
|
1594
|
+
limit: options.limit,
|
|
1595
|
+
search: options.search
|
|
1596
|
+
});
|
|
1597
|
+
this.setUiState({
|
|
1598
|
+
meetingSearch: options.search,
|
|
1599
|
+
selectedMeetingId: void 0,
|
|
1600
|
+
view: "meeting-list"
|
|
1601
|
+
});
|
|
1602
|
+
return meetings;
|
|
1603
|
+
}
|
|
1604
|
+
async getMeeting(id, options = {}) {
|
|
1605
|
+
const documents = await this.listDocuments();
|
|
1606
|
+
const cacheData = await this.loadCache({ required: options.requireCache });
|
|
1607
|
+
const document = resolveMeeting(documents, id);
|
|
1608
|
+
const meeting = buildMeetingRecord(document, cacheData);
|
|
1609
|
+
this.setUiState({
|
|
1610
|
+
selectedMeetingId: document.id,
|
|
1611
|
+
view: "meeting-detail"
|
|
1612
|
+
});
|
|
1613
|
+
return {
|
|
1614
|
+
cacheData,
|
|
1615
|
+
document,
|
|
1616
|
+
meeting
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1619
|
+
async exportNotes(format = "markdown") {
|
|
1620
|
+
const documents = await this.listDocuments();
|
|
1621
|
+
const written = await writeNotes(documents, this.config.notes.output, format);
|
|
1622
|
+
this.#state.exports.notes = {
|
|
1623
|
+
format,
|
|
1624
|
+
itemCount: documents.length,
|
|
1625
|
+
outputDir: this.config.notes.output,
|
|
1626
|
+
ranAt: this.nowIso(),
|
|
1627
|
+
written
|
|
1628
|
+
};
|
|
1629
|
+
this.setUiState({ view: "notes-export" });
|
|
1630
|
+
return {
|
|
1631
|
+
documentCount: documents.length,
|
|
1632
|
+
documents,
|
|
1633
|
+
format,
|
|
1634
|
+
outputDir: this.config.notes.output,
|
|
1635
|
+
written
|
|
1636
|
+
};
|
|
1637
|
+
}
|
|
1638
|
+
async exportTranscripts(format = "text") {
|
|
1639
|
+
const cacheData = await this.loadCache({ required: true });
|
|
1640
|
+
if (!cacheData) throw this.missingCacheError();
|
|
1641
|
+
const written = await writeTranscripts(cacheData, this.config.transcripts.output, format);
|
|
1642
|
+
const count = transcriptCount(cacheData);
|
|
1643
|
+
this.#state.exports.transcripts = {
|
|
1644
|
+
format,
|
|
1645
|
+
itemCount: count,
|
|
1646
|
+
outputDir: this.config.transcripts.output,
|
|
1647
|
+
ranAt: this.nowIso(),
|
|
1648
|
+
written
|
|
1649
|
+
};
|
|
1650
|
+
this.setUiState({ view: "transcripts-export" });
|
|
1651
|
+
return {
|
|
1652
|
+
cacheData,
|
|
1653
|
+
format,
|
|
1654
|
+
outputDir: this.config.transcripts.output,
|
|
1655
|
+
transcriptCount: count,
|
|
1656
|
+
written
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
};
|
|
1660
|
+
async function createGranolaApp(config, options = {}) {
|
|
1661
|
+
return new GranolaApp(config, {
|
|
1662
|
+
auth: await inspectDefaultGranolaAuth(config),
|
|
1663
|
+
cacheLoader: loadOptionalGranolaCache,
|
|
1664
|
+
createGranolaClient: async () => await createDefaultGranolaRuntime(config, options.logger),
|
|
1665
|
+
now: options.now
|
|
1666
|
+
}, { surface: options.surface });
|
|
1667
|
+
}
|
|
1668
|
+
//#endregion
|
|
1669
|
+
//#region src/config.ts
|
|
1670
|
+
function pickString(value) {
|
|
1671
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
1672
|
+
}
|
|
1673
|
+
function pickBoolean(value) {
|
|
1674
|
+
return typeof value === "boolean" ? value : void 0;
|
|
1675
|
+
}
|
|
1676
|
+
function parseTomlScalar(rawValue) {
|
|
1677
|
+
const value = rawValue.trim();
|
|
1678
|
+
if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) {
|
|
1679
|
+
if (value.startsWith("\"")) try {
|
|
1680
|
+
return JSON.parse(value);
|
|
1681
|
+
} catch {
|
|
1682
|
+
return value.slice(1, -1);
|
|
1683
|
+
}
|
|
1684
|
+
return value.slice(1, -1);
|
|
1685
|
+
}
|
|
1686
|
+
if (/^(true|false)$/i.test(value)) return value.toLowerCase() === "true";
|
|
1687
|
+
if (/^-?\d+(?:\.\d+)?$/.test(value)) return Number(value);
|
|
1688
|
+
return value;
|
|
1689
|
+
}
|
|
1690
|
+
function parseSimpleToml(contents) {
|
|
1691
|
+
const values = {};
|
|
1692
|
+
for (const rawLine of contents.split(/\r?\n/)) {
|
|
1693
|
+
const line = rawLine.trim();
|
|
1694
|
+
if (!line || line.startsWith("#")) continue;
|
|
1695
|
+
const match = /^([A-Za-z0-9_-]+)\s*=\s*(.+)$/.exec(line);
|
|
1696
|
+
if (!match) continue;
|
|
1697
|
+
const [, key = "", rawValue = ""] = match;
|
|
1698
|
+
values[key] = parseTomlScalar(rawValue);
|
|
1699
|
+
}
|
|
1700
|
+
return values;
|
|
1701
|
+
}
|
|
1702
|
+
async function loadTomlConfig(configPath) {
|
|
1703
|
+
if (configPath) {
|
|
1704
|
+
if (!existsSync(configPath)) throw new Error(`config file not found: ${configPath}`);
|
|
1705
|
+
return {
|
|
1706
|
+
path: configPath,
|
|
1707
|
+
values: parseSimpleToml(await readUtf8(configPath))
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
const candidates = [join(process.cwd(), ".granola.toml"), join(homedir(), ".granola.toml")];
|
|
1711
|
+
for (const candidate of candidates) if (existsSync(candidate)) return {
|
|
1712
|
+
path: candidate,
|
|
1713
|
+
values: parseSimpleToml(await readUtf8(candidate))
|
|
1714
|
+
};
|
|
1715
|
+
return { values: {} };
|
|
1716
|
+
}
|
|
1717
|
+
function envFlag(value) {
|
|
1718
|
+
if (value == null) return;
|
|
1719
|
+
if (/^(1|true|yes|on)$/i.test(value)) return true;
|
|
1720
|
+
if (/^(0|false|no|off)$/i.test(value)) return false;
|
|
1721
|
+
}
|
|
1722
|
+
async function loadConfig(options) {
|
|
1723
|
+
const env = options.env ?? process.env;
|
|
1724
|
+
const config = await loadTomlConfig(pickString(options.globalFlags.config));
|
|
1725
|
+
const configValues = config.values;
|
|
1726
|
+
const defaultSupabase = firstExistingPath(granolaSupabaseCandidates());
|
|
1727
|
+
const defaultCache = firstExistingPath(granolaCacheCandidates());
|
|
1728
|
+
const timeoutValue = pickString(options.subcommandFlags.timeout) ?? pickString(env.TIMEOUT) ?? pickString(configValues.timeout) ?? "2m";
|
|
1729
|
+
return {
|
|
1730
|
+
configFileUsed: config.path,
|
|
1731
|
+
debug: pickBoolean(options.globalFlags.debug) ?? envFlag(env.DEBUG_MODE) ?? pickBoolean(configValues.debug) ?? false,
|
|
1732
|
+
notes: {
|
|
1733
|
+
output: pickString(options.subcommandFlags.output) ?? pickString(env.OUTPUT) ?? pickString(configValues.output) ?? "./notes",
|
|
1734
|
+
timeoutMs: parseDuration(timeoutValue)
|
|
1735
|
+
},
|
|
1736
|
+
supabase: pickString(options.globalFlags.supabase) ?? pickString(env.SUPABASE_FILE) ?? pickString(configValues.supabase) ?? defaultSupabase,
|
|
1737
|
+
transcripts: {
|
|
1738
|
+
cacheFile: pickString(options.subcommandFlags.cache) ?? pickString(env.CACHE_FILE) ?? pickString(configValues["cache-file"]) ?? pickString(configValues.cacheFile) ?? defaultCache ?? "",
|
|
1739
|
+
output: pickString(options.subcommandFlags.output) ?? pickString(env.TRANSCRIPT_OUTPUT) ?? pickString(configValues["transcript-output"]) ?? pickString(configValues.transcriptOutput) ?? "./transcripts"
|
|
1740
|
+
}
|
|
1741
|
+
};
|
|
1742
|
+
}
|
|
1743
|
+
//#endregion
|
|
1527
1744
|
//#region src/commands/shared.ts
|
|
1528
1745
|
function debug(enabled, ...values) {
|
|
1529
1746
|
if (enabled) console.error("[debug]", ...values);
|
|
@@ -1643,41 +1860,22 @@ const meetingCommand = {
|
|
|
1643
1860
|
}
|
|
1644
1861
|
}
|
|
1645
1862
|
};
|
|
1646
|
-
async function
|
|
1863
|
+
async function list(commandFlags, globalFlags) {
|
|
1864
|
+
const format = resolveListFormat(commandFlags.format);
|
|
1865
|
+
const limit = parseLimit(commandFlags.limit);
|
|
1866
|
+
const search = typeof commandFlags.search === "string" ? commandFlags.search : void 0;
|
|
1647
1867
|
const config = await loadConfig({
|
|
1648
1868
|
globalFlags,
|
|
1649
1869
|
subcommandFlags: commandFlags
|
|
1650
1870
|
});
|
|
1651
|
-
if (options.requireCache && !config.transcripts.cacheFile) throw new Error(`Granola cache file not found. Pass --cache or create .granola.toml. Expected locations include: ${granolaCacheCandidates().join(", ")}`);
|
|
1652
|
-
if (config.transcripts.cacheFile && !existsSync(config.transcripts.cacheFile)) throw new Error(`Granola cache file not found: ${config.transcripts.cacheFile}`);
|
|
1653
1871
|
debug(config.debug, "using config", config.configFileUsed ?? "(none)");
|
|
1654
1872
|
debug(config.debug, "supabase", config.supabase);
|
|
1655
1873
|
debug(config.debug, "cacheFile", config.transcripts.cacheFile || "(none)");
|
|
1656
1874
|
debug(config.debug, "timeoutMs", config.notes.timeoutMs);
|
|
1657
|
-
const
|
|
1658
|
-
|
|
1659
|
-
cacheData: await loadOptionalGranolaCache(config.transcripts.cacheFile),
|
|
1660
|
-
config,
|
|
1661
|
-
granolaClient
|
|
1662
|
-
};
|
|
1663
|
-
}
|
|
1664
|
-
async function loadResolvedMeeting(id, commandFlags, globalFlags, options = {}) {
|
|
1665
|
-
const { cacheData, config, granolaClient } = await loadMeetingData(commandFlags, globalFlags, options);
|
|
1666
|
-
console.log("Fetching meeting from Granola API...");
|
|
1667
|
-
return {
|
|
1668
|
-
cacheData,
|
|
1669
|
-
config,
|
|
1670
|
-
document: resolveMeeting(await granolaClient.listDocuments({ timeoutMs: config.notes.timeoutMs }), id)
|
|
1671
|
-
};
|
|
1672
|
-
}
|
|
1673
|
-
async function list(commandFlags, globalFlags) {
|
|
1674
|
-
const format = resolveListFormat(commandFlags.format);
|
|
1675
|
-
const limit = parseLimit(commandFlags.limit);
|
|
1676
|
-
const search = typeof commandFlags.search === "string" ? commandFlags.search : void 0;
|
|
1677
|
-
const { cacheData, config, granolaClient } = await loadMeetingData(commandFlags, globalFlags);
|
|
1875
|
+
const app = await createGranolaApp(config);
|
|
1876
|
+
debug(config.debug, "authMode", app.getState().auth.mode);
|
|
1678
1877
|
console.log("Fetching meetings from Granola API...");
|
|
1679
|
-
const meetings =
|
|
1680
|
-
cacheData,
|
|
1878
|
+
const meetings = await app.listMeetings({
|
|
1681
1879
|
limit,
|
|
1682
1880
|
search
|
|
1683
1881
|
});
|
|
@@ -1686,29 +1884,71 @@ async function list(commandFlags, globalFlags) {
|
|
|
1686
1884
|
}
|
|
1687
1885
|
async function view(id, commandFlags, globalFlags) {
|
|
1688
1886
|
const format = resolveViewFormat(commandFlags.format);
|
|
1689
|
-
const
|
|
1690
|
-
|
|
1691
|
-
|
|
1887
|
+
const config = await loadConfig({
|
|
1888
|
+
globalFlags,
|
|
1889
|
+
subcommandFlags: commandFlags
|
|
1890
|
+
});
|
|
1891
|
+
debug(config.debug, "using config", config.configFileUsed ?? "(none)");
|
|
1892
|
+
debug(config.debug, "supabase", config.supabase);
|
|
1893
|
+
debug(config.debug, "cacheFile", config.transcripts.cacheFile || "(none)");
|
|
1894
|
+
debug(config.debug, "timeoutMs", config.notes.timeoutMs);
|
|
1895
|
+
const app = await createGranolaApp(config);
|
|
1896
|
+
debug(config.debug, "authMode", app.getState().auth.mode);
|
|
1897
|
+
console.log("Fetching meeting from Granola API...");
|
|
1898
|
+
const result = await app.getMeeting(id);
|
|
1899
|
+
console.log(renderMeetingView(result.meeting, format).trimEnd());
|
|
1692
1900
|
return 0;
|
|
1693
1901
|
}
|
|
1694
1902
|
async function exportMeeting(id, commandFlags, globalFlags) {
|
|
1695
1903
|
const format = resolveExportFormat(commandFlags.format);
|
|
1696
|
-
const
|
|
1697
|
-
|
|
1698
|
-
|
|
1904
|
+
const config = await loadConfig({
|
|
1905
|
+
globalFlags,
|
|
1906
|
+
subcommandFlags: commandFlags
|
|
1907
|
+
});
|
|
1908
|
+
debug(config.debug, "using config", config.configFileUsed ?? "(none)");
|
|
1909
|
+
debug(config.debug, "supabase", config.supabase);
|
|
1910
|
+
debug(config.debug, "cacheFile", config.transcripts.cacheFile || "(none)");
|
|
1911
|
+
debug(config.debug, "timeoutMs", config.notes.timeoutMs);
|
|
1912
|
+
const app = await createGranolaApp(config);
|
|
1913
|
+
debug(config.debug, "authMode", app.getState().auth.mode);
|
|
1914
|
+
console.log("Fetching meeting from Granola API...");
|
|
1915
|
+
const result = await app.getMeeting(id);
|
|
1916
|
+
console.log(renderMeetingExport(result.meeting, format).trimEnd());
|
|
1699
1917
|
return 0;
|
|
1700
1918
|
}
|
|
1701
1919
|
async function notes(id, commandFlags, globalFlags) {
|
|
1702
1920
|
const format = resolveNotesFormat(commandFlags.format);
|
|
1703
|
-
const
|
|
1704
|
-
|
|
1921
|
+
const config = await loadConfig({
|
|
1922
|
+
globalFlags,
|
|
1923
|
+
subcommandFlags: commandFlags
|
|
1924
|
+
});
|
|
1925
|
+
debug(config.debug, "using config", config.configFileUsed ?? "(none)");
|
|
1926
|
+
debug(config.debug, "supabase", config.supabase);
|
|
1927
|
+
debug(config.debug, "cacheFile", config.transcripts.cacheFile || "(none)");
|
|
1928
|
+
debug(config.debug, "timeoutMs", config.notes.timeoutMs);
|
|
1929
|
+
const app = await createGranolaApp(config);
|
|
1930
|
+
debug(config.debug, "authMode", app.getState().auth.mode);
|
|
1931
|
+
console.log("Fetching meeting from Granola API...");
|
|
1932
|
+
const result = await app.getMeeting(id);
|
|
1933
|
+
console.log(renderMeetingNotes(result.document, format).trimEnd());
|
|
1705
1934
|
return 0;
|
|
1706
1935
|
}
|
|
1707
1936
|
async function transcript(id, commandFlags, globalFlags) {
|
|
1708
1937
|
const format = resolveTranscriptFormat$1(commandFlags.format);
|
|
1709
|
-
const
|
|
1710
|
-
|
|
1711
|
-
|
|
1938
|
+
const config = await loadConfig({
|
|
1939
|
+
globalFlags,
|
|
1940
|
+
subcommandFlags: commandFlags
|
|
1941
|
+
});
|
|
1942
|
+
debug(config.debug, "using config", config.configFileUsed ?? "(none)");
|
|
1943
|
+
debug(config.debug, "supabase", config.supabase);
|
|
1944
|
+
debug(config.debug, "cacheFile", config.transcripts.cacheFile || "(none)");
|
|
1945
|
+
debug(config.debug, "timeoutMs", config.notes.timeoutMs);
|
|
1946
|
+
const app = await createGranolaApp(config);
|
|
1947
|
+
debug(config.debug, "authMode", app.getState().auth.mode);
|
|
1948
|
+
console.log("Fetching meeting from Granola API...");
|
|
1949
|
+
const result = await app.getMeeting(id, { requireCache: true });
|
|
1950
|
+
const output = renderMeetingTranscript(result.document, result.cacheData, format);
|
|
1951
|
+
if (!output.trim()) throw new Error(`no transcript found for meeting: ${result.document.id}`);
|
|
1712
1952
|
console.log(output.trimEnd());
|
|
1713
1953
|
return 0;
|
|
1714
1954
|
}
|
|
@@ -1751,13 +1991,11 @@ const notesCommand = {
|
|
|
1751
1991
|
debug(config.debug, "output", config.notes.output);
|
|
1752
1992
|
const format = resolveNoteFormat(commandFlags.format);
|
|
1753
1993
|
debug(config.debug, "format", format);
|
|
1754
|
-
const
|
|
1755
|
-
|
|
1756
|
-
const
|
|
1757
|
-
console.log(
|
|
1758
|
-
|
|
1759
|
-
console.log("✓ Export completed successfully");
|
|
1760
|
-
debug(config.debug, "notes written", written);
|
|
1994
|
+
const app = await createGranolaApp(config);
|
|
1995
|
+
debug(config.debug, "authMode", app.getState().auth.mode);
|
|
1996
|
+
const result = await app.exportNotes(format);
|
|
1997
|
+
console.log(`✓ Exported ${result.documentCount} notes to ${result.outputDir}`);
|
|
1998
|
+
debug(config.debug, "notes written", result.written);
|
|
1761
1999
|
return 0;
|
|
1762
2000
|
}
|
|
1763
2001
|
};
|
|
@@ -1803,20 +2041,16 @@ const transcriptsCommand = {
|
|
|
1803
2041
|
globalFlags,
|
|
1804
2042
|
subcommandFlags: commandFlags
|
|
1805
2043
|
});
|
|
1806
|
-
if (!config.transcripts.cacheFile) throw new Error(`Granola cache file not found. Pass --cache or create .granola.toml. Expected locations include: ${granolaCacheCandidates().join(", ")}`);
|
|
1807
|
-
if (!existsSync(config.transcripts.cacheFile)) throw new Error(`Granola cache file not found: ${config.transcripts.cacheFile}`);
|
|
1808
2044
|
debug(config.debug, "using config", config.configFileUsed ?? "(none)");
|
|
1809
2045
|
debug(config.debug, "cacheFile", config.transcripts.cacheFile);
|
|
1810
2046
|
debug(config.debug, "output", config.transcripts.output);
|
|
1811
2047
|
const format = resolveTranscriptFormat(commandFlags.format);
|
|
1812
2048
|
debug(config.debug, "format", format);
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
const
|
|
1816
|
-
console.log(
|
|
1817
|
-
|
|
1818
|
-
console.log("✓ Export completed successfully");
|
|
1819
|
-
debug(config.debug, "transcripts written", written);
|
|
2049
|
+
const app = await createGranolaApp(config);
|
|
2050
|
+
debug(config.debug, "authMode", app.getState().auth.mode);
|
|
2051
|
+
const result = await app.exportTranscripts(format);
|
|
2052
|
+
console.log(`✓ Exported ${result.transcriptCount} transcripts to ${result.outputDir}`);
|
|
2053
|
+
debug(config.debug, "transcripts written", result.written);
|
|
1820
2054
|
return 0;
|
|
1821
2055
|
}
|
|
1822
2056
|
};
|