granola-toolkit 0.29.0 → 0.30.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/README.md +11 -0
- package/dist/cli.js +145 -62
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -208,6 +208,7 @@ The machine-readable `export` command includes:
|
|
|
208
208
|
The initial server API includes:
|
|
209
209
|
|
|
210
210
|
- `GET /health`
|
|
211
|
+
- `GET /server/info`
|
|
211
212
|
- `POST /auth/unlock` for password-protected servers
|
|
212
213
|
- `POST /auth/lock` to clear the browser/API unlock cookie
|
|
213
214
|
- `GET /auth/status`
|
|
@@ -272,6 +273,16 @@ Use it when you want:
|
|
|
272
273
|
|
|
273
274
|
The attach flow uses the existing local HTTP API plus `GET /events` for live state updates.
|
|
274
275
|
|
|
276
|
+
### Runtime Boundaries
|
|
277
|
+
|
|
278
|
+
The toolkit now keeps its local persistence and transport contracts explicit:
|
|
279
|
+
|
|
280
|
+
- one shared local data directory for export jobs, meeting index data, and any file-backed session state
|
|
281
|
+
- one versioned local HTTP transport contract, exposed by `GET /server/info`
|
|
282
|
+
- one remote client handshake that validates the transport protocol before attaching
|
|
283
|
+
|
|
284
|
+
That keeps the current single-package repo simple, while making a future split into separate server/client packages or remote-hosted clients much less invasive.
|
|
285
|
+
|
|
275
286
|
### TUI
|
|
276
287
|
|
|
277
288
|
`tui` starts a full-screen terminal workspace on the shared app core, without requiring the local server or browser client. Use `attach` when you want the same workspace against an existing shared server instance instead.
|
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,60 @@ import { NodeHtmlMarkdown } from "node-html-markdown";
|
|
|
9
9
|
import { execFile } from "node:child_process";
|
|
10
10
|
import { promisify } from "node:util";
|
|
11
11
|
import { createServer } from "node:http";
|
|
12
|
+
//#region src/transport.ts
|
|
13
|
+
const granolaTransportPaths = {
|
|
14
|
+
authLock: "/auth/lock",
|
|
15
|
+
authLogin: "/auth/login",
|
|
16
|
+
authLogout: "/auth/logout",
|
|
17
|
+
authMode: "/auth/mode",
|
|
18
|
+
authRefresh: "/auth/refresh",
|
|
19
|
+
authStatus: "/auth/status",
|
|
20
|
+
authUnlock: "/auth/unlock",
|
|
21
|
+
events: "/events",
|
|
22
|
+
exportJobs: "/exports/jobs",
|
|
23
|
+
exportNotes: "/exports/notes",
|
|
24
|
+
exportTranscripts: "/exports/transcripts",
|
|
25
|
+
health: "/health",
|
|
26
|
+
meetingResolve: "/meetings/resolve",
|
|
27
|
+
meetings: "/meetings",
|
|
28
|
+
root: "/",
|
|
29
|
+
serverInfo: "/server/info",
|
|
30
|
+
state: "/state"
|
|
31
|
+
};
|
|
32
|
+
function appendSearchParams(path, params) {
|
|
33
|
+
const url = new URL(path, "http://localhost");
|
|
34
|
+
for (const [key, value] of Object.entries(params)) {
|
|
35
|
+
if (value === void 0 || value === false || value === "") continue;
|
|
36
|
+
url.searchParams.set(key, String(value));
|
|
37
|
+
}
|
|
38
|
+
return `${url.pathname}${url.search}`;
|
|
39
|
+
}
|
|
40
|
+
function granolaMeetingPath(id) {
|
|
41
|
+
return `${granolaTransportPaths.meetings}/${encodeURIComponent(id)}`;
|
|
42
|
+
}
|
|
43
|
+
function granolaMeetingResolvePath(query, options = {}) {
|
|
44
|
+
return appendSearchParams(granolaTransportPaths.meetingResolve, {
|
|
45
|
+
includeTranscript: options.includeTranscript ? "true" : void 0,
|
|
46
|
+
q: query
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function granolaMeetingsPath(options = {}) {
|
|
50
|
+
return appendSearchParams(granolaTransportPaths.meetings, {
|
|
51
|
+
limit: options.limit,
|
|
52
|
+
refresh: options.forceRefresh ? "true" : void 0,
|
|
53
|
+
search: options.search,
|
|
54
|
+
sort: options.sort,
|
|
55
|
+
updatedFrom: options.updatedFrom,
|
|
56
|
+
updatedTo: options.updatedTo
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function granolaExportJobsPath(options = {}) {
|
|
60
|
+
return appendSearchParams(granolaTransportPaths.exportJobs, { limit: options.limit });
|
|
61
|
+
}
|
|
62
|
+
function granolaExportJobRerunPath(id) {
|
|
63
|
+
return `${granolaTransportPaths.exportJobs}/${encodeURIComponent(id)}/rerun`;
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
12
66
|
//#region src/server/client.ts
|
|
13
67
|
function cloneValue(value) {
|
|
14
68
|
return structuredClone(value);
|
|
@@ -24,14 +78,6 @@ function normaliseServerUrl(serverUrl) {
|
|
|
24
78
|
parsed.hash = "";
|
|
25
79
|
return parsed;
|
|
26
80
|
}
|
|
27
|
-
function appendSearchParams(path, params) {
|
|
28
|
-
const url = new URL(path, "http://localhost");
|
|
29
|
-
for (const [key, value] of Object.entries(params)) {
|
|
30
|
-
if (value === void 0 || value === false || value === "") continue;
|
|
31
|
-
url.searchParams.set(key, String(value));
|
|
32
|
-
}
|
|
33
|
-
return `${url.pathname}${url.search}`;
|
|
34
|
-
}
|
|
35
81
|
function mergeHeaders(...values) {
|
|
36
82
|
const headers = new Headers();
|
|
37
83
|
for (const value of values) {
|
|
@@ -66,22 +112,32 @@ var GranolaServerClient = class GranolaServerClient {
|
|
|
66
112
|
#fetchImpl;
|
|
67
113
|
#password;
|
|
68
114
|
#reconnectDelayMs;
|
|
115
|
+
info;
|
|
69
116
|
#streamAbortController;
|
|
70
|
-
constructor(url, initialState, options = {}) {
|
|
117
|
+
constructor(info, url, initialState, options = {}) {
|
|
71
118
|
this.url = url;
|
|
72
119
|
this.#fetchImpl = options.fetchImpl ?? fetch;
|
|
120
|
+
this.info = cloneValue(info);
|
|
73
121
|
this.#password = options.password?.trim() || void 0;
|
|
74
122
|
this.#reconnectDelayMs = options.reconnectDelayMs ?? 1e3;
|
|
75
123
|
this.#state = cloneValue(initialState);
|
|
76
124
|
}
|
|
77
125
|
static async connect(serverUrl, options = {}) {
|
|
78
126
|
const url = normaliseServerUrl(serverUrl);
|
|
79
|
-
const
|
|
127
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
128
|
+
const infoResponse = await fetchImpl(new URL(granolaTransportPaths.serverInfo, url), { headers: mergeHeaders({
|
|
129
|
+
...options.password?.trim() ? { "x-granola-password": options.password.trim() } : {},
|
|
130
|
+
accept: "application/json"
|
|
131
|
+
}) });
|
|
132
|
+
if (!infoResponse.ok) throw await responseError(infoResponse);
|
|
133
|
+
const info = await infoResponse.json();
|
|
134
|
+
if (info.protocolVersion !== 1) throw new Error(`unsupported Granola transport protocol: expected 1, got ${info.protocolVersion}`);
|
|
135
|
+
const response = await fetchImpl(new URL(granolaTransportPaths.state, url), { headers: mergeHeaders({
|
|
80
136
|
...options.password?.trim() ? { "x-granola-password": options.password.trim() } : {},
|
|
81
137
|
accept: "application/json"
|
|
82
138
|
}) });
|
|
83
139
|
if (!response.ok) throw await responseError(response);
|
|
84
|
-
const client = new GranolaServerClient(url, await response.json(), options);
|
|
140
|
+
const client = new GranolaServerClient(info, url, await response.json(), options);
|
|
85
141
|
client.startEvents();
|
|
86
142
|
return client;
|
|
87
143
|
}
|
|
@@ -103,66 +159,56 @@ var GranolaServerClient = class GranolaServerClient {
|
|
|
103
159
|
};
|
|
104
160
|
}
|
|
105
161
|
async inspectAuth() {
|
|
106
|
-
return await this.requestJson(
|
|
162
|
+
return await this.requestJson(granolaTransportPaths.authStatus);
|
|
107
163
|
}
|
|
108
164
|
async loginAuth(options = {}) {
|
|
109
|
-
return await this.requestJson(
|
|
165
|
+
return await this.requestJson(granolaTransportPaths.authLogin, {
|
|
110
166
|
body: JSON.stringify(options),
|
|
111
167
|
headers: { "content-type": "application/json" },
|
|
112
168
|
method: "POST"
|
|
113
169
|
});
|
|
114
170
|
}
|
|
115
171
|
async logoutAuth() {
|
|
116
|
-
return await this.requestJson(
|
|
172
|
+
return await this.requestJson(granolaTransportPaths.authLogout, { method: "POST" });
|
|
117
173
|
}
|
|
118
174
|
async refreshAuth() {
|
|
119
|
-
return await this.requestJson(
|
|
175
|
+
return await this.requestJson(granolaTransportPaths.authRefresh, { method: "POST" });
|
|
120
176
|
}
|
|
121
177
|
async switchAuthMode(mode) {
|
|
122
|
-
return await this.requestJson(
|
|
178
|
+
return await this.requestJson(granolaTransportPaths.authMode, {
|
|
123
179
|
body: JSON.stringify({ mode }),
|
|
124
180
|
headers: { "content-type": "application/json" },
|
|
125
181
|
method: "POST"
|
|
126
182
|
});
|
|
127
183
|
}
|
|
128
184
|
async listMeetings(options = {}) {
|
|
129
|
-
return await this.requestJson(
|
|
130
|
-
limit: options.limit,
|
|
131
|
-
refresh: options.forceRefresh ? "true" : void 0,
|
|
132
|
-
search: options.search,
|
|
133
|
-
sort: options.sort,
|
|
134
|
-
updatedFrom: options.updatedFrom,
|
|
135
|
-
updatedTo: options.updatedTo
|
|
136
|
-
}));
|
|
185
|
+
return await this.requestJson(granolaMeetingsPath(options));
|
|
137
186
|
}
|
|
138
187
|
async getMeeting(id, options = {}) {
|
|
139
|
-
return await this.requestJson(
|
|
188
|
+
return await this.requestJson(`${granolaMeetingPath(id)}${options.requireCache ? "?includeTranscript=true" : ""}`);
|
|
140
189
|
}
|
|
141
190
|
async findMeeting(query, options = {}) {
|
|
142
|
-
return await this.requestJson(
|
|
143
|
-
includeTranscript: options.requireCache ? "true" : void 0,
|
|
144
|
-
q: query
|
|
145
|
-
}));
|
|
191
|
+
return await this.requestJson(granolaMeetingResolvePath(query, { includeTranscript: options.requireCache }));
|
|
146
192
|
}
|
|
147
193
|
async listExportJobs(options = {}) {
|
|
148
|
-
return await this.requestJson(
|
|
194
|
+
return await this.requestJson(granolaExportJobsPath(options));
|
|
149
195
|
}
|
|
150
196
|
async exportNotes(format = "markdown") {
|
|
151
|
-
return await this.requestJson(
|
|
197
|
+
return await this.requestJson(granolaTransportPaths.exportNotes, {
|
|
152
198
|
body: JSON.stringify({ format }),
|
|
153
199
|
headers: { "content-type": "application/json" },
|
|
154
200
|
method: "POST"
|
|
155
201
|
});
|
|
156
202
|
}
|
|
157
203
|
async exportTranscripts(format = "text") {
|
|
158
|
-
return await this.requestJson(
|
|
204
|
+
return await this.requestJson(granolaTransportPaths.exportTranscripts, {
|
|
159
205
|
body: JSON.stringify({ format }),
|
|
160
206
|
headers: { "content-type": "application/json" },
|
|
161
207
|
method: "POST"
|
|
162
208
|
});
|
|
163
209
|
}
|
|
164
210
|
async rerunExportJob(id) {
|
|
165
|
-
return await this.requestJson(
|
|
211
|
+
return await this.requestJson(granolaExportJobRerunPath(id), { method: "POST" });
|
|
166
212
|
}
|
|
167
213
|
async request(path, init = {}) {
|
|
168
214
|
const response = await this.#fetchImpl(new URL(path, this.url), {
|
|
@@ -192,7 +238,7 @@ var GranolaServerClient = class GranolaServerClient {
|
|
|
192
238
|
const controller = new AbortController();
|
|
193
239
|
this.#streamAbortController = controller;
|
|
194
240
|
try {
|
|
195
|
-
const response = await this.request(
|
|
241
|
+
const response = await this.request(granolaTransportPaths.events, {
|
|
196
242
|
headers: { accept: "text/event-stream" },
|
|
197
243
|
signal: controller.signal
|
|
198
244
|
});
|
|
@@ -2131,6 +2177,22 @@ function parseCacheContents(contents) {
|
|
|
2131
2177
|
};
|
|
2132
2178
|
}
|
|
2133
2179
|
//#endregion
|
|
2180
|
+
//#region src/persistence/layout.ts
|
|
2181
|
+
function defaultGranolaToolkitDataDirectory(targetPlatform = platform(), homeDirectory = homedir()) {
|
|
2182
|
+
return targetPlatform === "darwin" ? join(homeDirectory, "Library", "Application Support", "granola-toolkit") : join(homeDirectory, ".config", "granola-toolkit");
|
|
2183
|
+
}
|
|
2184
|
+
function defaultGranolaToolkitPersistenceLayout(options = {}) {
|
|
2185
|
+
const targetPlatform = options.platform ?? platform();
|
|
2186
|
+
const dataDirectory = defaultGranolaToolkitDataDirectory(targetPlatform, options.homeDirectory ?? homedir());
|
|
2187
|
+
return {
|
|
2188
|
+
dataDirectory,
|
|
2189
|
+
exportJobsFile: join(dataDirectory, "export-jobs.json"),
|
|
2190
|
+
meetingIndexFile: join(dataDirectory, "meeting-index.json"),
|
|
2191
|
+
sessionFile: join(dataDirectory, "session.json"),
|
|
2192
|
+
sessionStoreKind: targetPlatform === "darwin" ? "keychain" : "file"
|
|
2193
|
+
};
|
|
2194
|
+
}
|
|
2195
|
+
//#endregion
|
|
2134
2196
|
//#region src/client/auth.ts
|
|
2135
2197
|
const execFileAsync$1 = promisify(execFile);
|
|
2136
2198
|
const DEFAULT_CLIENT_ID = "client_GranolaMac";
|
|
@@ -2351,11 +2413,10 @@ async function refreshGranolaSession(session, fetchImpl = fetch) {
|
|
|
2351
2413
|
};
|
|
2352
2414
|
}
|
|
2353
2415
|
function defaultSessionFilePath() {
|
|
2354
|
-
|
|
2355
|
-
return platform() === "darwin" ? join(home, "Library", "Application Support", "granola-toolkit", "session.json") : join(home, ".config", "granola-toolkit", "session.json");
|
|
2416
|
+
return defaultGranolaToolkitPersistenceLayout().sessionFile;
|
|
2356
2417
|
}
|
|
2357
2418
|
function createDefaultSessionStore() {
|
|
2358
|
-
return
|
|
2419
|
+
return defaultGranolaToolkitPersistenceLayout().sessionStoreKind === "keychain" ? new KeychainSessionStore() : new FileSessionStore();
|
|
2359
2420
|
}
|
|
2360
2421
|
//#endregion
|
|
2361
2422
|
//#region src/client/default-auth.ts
|
|
@@ -2736,8 +2797,7 @@ function createExportJobId(kind) {
|
|
|
2736
2797
|
return `${kind}-${randomUUID()}`;
|
|
2737
2798
|
}
|
|
2738
2799
|
function defaultExportJobsFilePath() {
|
|
2739
|
-
|
|
2740
|
-
return platform() === "darwin" ? join(home, "Library", "Application Support", "granola-toolkit", "export-jobs.json") : join(home, ".config", "granola-toolkit", "export-jobs.json");
|
|
2800
|
+
return defaultGranolaToolkitPersistenceLayout().exportJobsFile;
|
|
2741
2801
|
}
|
|
2742
2802
|
var FileExportJobStore = class {
|
|
2743
2803
|
constructor(filePath = defaultExportJobsFilePath()) {
|
|
@@ -2801,8 +2861,7 @@ var FileMeetingIndexStore = class {
|
|
|
2801
2861
|
}
|
|
2802
2862
|
};
|
|
2803
2863
|
function defaultMeetingIndexFilePath() {
|
|
2804
|
-
|
|
2805
|
-
return platform() === "darwin" ? join(home, "Library", "Application Support", "granola-toolkit", "meeting-index.json") : join(home, ".config", "granola-toolkit", "meeting-index.json");
|
|
2864
|
+
return defaultGranolaToolkitPersistenceLayout().meetingIndexFile;
|
|
2806
2865
|
}
|
|
2807
2866
|
function createDefaultMeetingIndexStore() {
|
|
2808
2867
|
return new FileMeetingIndexStore();
|
|
@@ -5355,7 +5414,7 @@ function isPasswordAuthenticated(request, password) {
|
|
|
5355
5414
|
return parseCookies(request)[PASSWORD_COOKIE_NAME] === password;
|
|
5356
5415
|
}
|
|
5357
5416
|
function publicRoute(path, enableWebClient) {
|
|
5358
|
-
return path ===
|
|
5417
|
+
return path === granolaTransportPaths.health || path === granolaTransportPaths.serverInfo || path === granolaTransportPaths.authUnlock || enableWebClient && path === granolaTransportPaths.root;
|
|
5359
5418
|
}
|
|
5360
5419
|
async function startGranolaServer(app, options = {}) {
|
|
5361
5420
|
const enableWebClient = options.enableWebClient ?? false;
|
|
@@ -5365,6 +5424,24 @@ async function startGranolaServer(app, options = {}) {
|
|
|
5365
5424
|
password: options.security?.password?.trim() || void 0,
|
|
5366
5425
|
trustedOrigins: (options.security?.trustedOrigins ?? []).map((origin) => origin.trim()).filter(Boolean)
|
|
5367
5426
|
};
|
|
5427
|
+
const serverInfo = {
|
|
5428
|
+
capabilities: {
|
|
5429
|
+
attach: true,
|
|
5430
|
+
auth: true,
|
|
5431
|
+
events: true,
|
|
5432
|
+
exports: true,
|
|
5433
|
+
meetingOpen: true,
|
|
5434
|
+
webClient: enableWebClient
|
|
5435
|
+
},
|
|
5436
|
+
persistence: {
|
|
5437
|
+
exportJobs: true,
|
|
5438
|
+
meetingIndex: true,
|
|
5439
|
+
sessionStore: defaultGranolaToolkitPersistenceLayout().sessionStoreKind
|
|
5440
|
+
},
|
|
5441
|
+
product: "granola-toolkit",
|
|
5442
|
+
protocolVersion: 1,
|
|
5443
|
+
transport: "local-http"
|
|
5444
|
+
};
|
|
5368
5445
|
const server = createServer(async (request, response) => {
|
|
5369
5446
|
const method = request.method ?? "GET";
|
|
5370
5447
|
const url = new URL(request.url ?? "/", `http://${hostname}`);
|
|
@@ -5392,11 +5469,11 @@ async function startGranolaServer(app, options = {}) {
|
|
|
5392
5469
|
sendNoContent(response, 204, originHeaders);
|
|
5393
5470
|
return;
|
|
5394
5471
|
}
|
|
5395
|
-
if (method === "GET" && path ===
|
|
5472
|
+
if (method === "GET" && path === granolaTransportPaths.root && enableWebClient) {
|
|
5396
5473
|
sendHtml(response, renderGranolaWebPage({ serverPasswordRequired: Boolean(security.password) }), 200, originHeaders);
|
|
5397
5474
|
return;
|
|
5398
5475
|
}
|
|
5399
|
-
if (method === "GET" && path ===
|
|
5476
|
+
if (method === "GET" && path === granolaTransportPaths.health) {
|
|
5400
5477
|
sendJson(response, {
|
|
5401
5478
|
ok: true,
|
|
5402
5479
|
service: "granola-toolkit",
|
|
@@ -5404,7 +5481,11 @@ async function startGranolaServer(app, options = {}) {
|
|
|
5404
5481
|
}, { headers: originHeaders });
|
|
5405
5482
|
return;
|
|
5406
5483
|
}
|
|
5407
|
-
if (method === "
|
|
5484
|
+
if (method === "GET" && path === granolaTransportPaths.serverInfo) {
|
|
5485
|
+
sendJson(response, serverInfo, { headers: originHeaders });
|
|
5486
|
+
return;
|
|
5487
|
+
}
|
|
5488
|
+
if (method === "POST" && path === granolaTransportPaths.authUnlock) {
|
|
5408
5489
|
if (!security.password) {
|
|
5409
5490
|
sendJson(response, {
|
|
5410
5491
|
ok: true,
|
|
@@ -5443,22 +5524,22 @@ async function startGranolaServer(app, options = {}) {
|
|
|
5443
5524
|
});
|
|
5444
5525
|
return;
|
|
5445
5526
|
}
|
|
5446
|
-
if (method === "GET" && path ===
|
|
5527
|
+
if (method === "GET" && path === granolaTransportPaths.state) {
|
|
5447
5528
|
sendJson(response, app.getState(), { headers: originHeaders });
|
|
5448
5529
|
return;
|
|
5449
5530
|
}
|
|
5450
|
-
if (method === "GET" && path ===
|
|
5531
|
+
if (method === "GET" && path === granolaTransportPaths.authStatus) {
|
|
5451
5532
|
sendJson(response, await app.inspectAuth(), { headers: originHeaders });
|
|
5452
5533
|
return;
|
|
5453
5534
|
}
|
|
5454
|
-
if (method === "POST" && path ===
|
|
5535
|
+
if (method === "POST" && path === granolaTransportPaths.authLock) {
|
|
5455
5536
|
sendJson(response, { ok: true }, { headers: {
|
|
5456
5537
|
...originHeaders,
|
|
5457
5538
|
"set-cookie": clearPasswordCookieHeader()
|
|
5458
5539
|
} });
|
|
5459
5540
|
return;
|
|
5460
5541
|
}
|
|
5461
|
-
if (method === "GET" && path ===
|
|
5542
|
+
if (method === "GET" && path === granolaTransportPaths.events) {
|
|
5462
5543
|
response.writeHead(200, {
|
|
5463
5544
|
"cache-control": "no-cache, no-transform",
|
|
5464
5545
|
connection: "keep-alive",
|
|
@@ -5479,7 +5560,7 @@ async function startGranolaServer(app, options = {}) {
|
|
|
5479
5560
|
});
|
|
5480
5561
|
return;
|
|
5481
5562
|
}
|
|
5482
|
-
if (method === "GET" && path ===
|
|
5563
|
+
if (method === "GET" && path === granolaTransportPaths.meetings) {
|
|
5483
5564
|
const limit = parseInteger(url.searchParams.get("limit"));
|
|
5484
5565
|
const refresh = url.searchParams.get("refresh") === "true";
|
|
5485
5566
|
const search = url.searchParams.get("search")?.trim() || void 0;
|
|
@@ -5505,38 +5586,38 @@ async function startGranolaServer(app, options = {}) {
|
|
|
5505
5586
|
}, { headers: originHeaders });
|
|
5506
5587
|
return;
|
|
5507
5588
|
}
|
|
5508
|
-
if (method === "GET" && path ===
|
|
5589
|
+
if (method === "GET" && path === granolaTransportPaths.meetingResolve) {
|
|
5509
5590
|
const query = url.searchParams.get("q")?.trim();
|
|
5510
5591
|
if (!query) throw new Error("meeting query is required");
|
|
5511
5592
|
sendJson(response, await app.findMeeting(query, { requireCache: url.searchParams.get("includeTranscript") === "true" }), { headers: originHeaders });
|
|
5512
5593
|
return;
|
|
5513
5594
|
}
|
|
5514
|
-
if (method === "GET" && path.startsWith(
|
|
5515
|
-
const id = decodeURIComponent(path.slice(
|
|
5595
|
+
if (method === "GET" && path.startsWith(`${granolaTransportPaths.meetings}/`) && path !== granolaTransportPaths.meetingResolve) {
|
|
5596
|
+
const id = decodeURIComponent(path.slice(`${granolaTransportPaths.meetings}/`.length));
|
|
5516
5597
|
if (!id) throw new Error("meeting id is required");
|
|
5517
5598
|
sendJson(response, await app.getMeeting(id, { requireCache: url.searchParams.get("includeTranscript") === "true" }), { headers: originHeaders });
|
|
5518
5599
|
return;
|
|
5519
5600
|
}
|
|
5520
|
-
if (method === "POST" && path ===
|
|
5601
|
+
if (method === "POST" && path === granolaTransportPaths.authLogin) {
|
|
5521
5602
|
const body = await readJsonBody(request);
|
|
5522
5603
|
const supabasePath = typeof body.supabasePath === "string" && body.supabasePath.trim() ? body.supabasePath.trim() : void 0;
|
|
5523
5604
|
sendJson(response, await app.loginAuth({ supabasePath }), { headers: originHeaders });
|
|
5524
5605
|
return;
|
|
5525
5606
|
}
|
|
5526
|
-
if (method === "POST" && path ===
|
|
5607
|
+
if (method === "POST" && path === granolaTransportPaths.authLogout) {
|
|
5527
5608
|
sendJson(response, await app.logoutAuth(), { headers: originHeaders });
|
|
5528
5609
|
return;
|
|
5529
5610
|
}
|
|
5530
|
-
if (method === "POST" && path ===
|
|
5611
|
+
if (method === "POST" && path === granolaTransportPaths.authRefresh) {
|
|
5531
5612
|
sendJson(response, await app.refreshAuth(), { headers: originHeaders });
|
|
5532
5613
|
return;
|
|
5533
5614
|
}
|
|
5534
|
-
if (method === "POST" && path ===
|
|
5615
|
+
if (method === "POST" && path === granolaTransportPaths.authMode) {
|
|
5535
5616
|
const body = await readJsonBody(request);
|
|
5536
5617
|
sendJson(response, await app.switchAuthMode(parseAuthMode(body.mode)), { headers: originHeaders });
|
|
5537
5618
|
return;
|
|
5538
5619
|
}
|
|
5539
|
-
if (method === "POST" && path ===
|
|
5620
|
+
if (method === "POST" && path === granolaTransportPaths.exportNotes) {
|
|
5540
5621
|
const body = await readJsonBody(request);
|
|
5541
5622
|
sendJson(response, await app.exportNotes(noteFormatFromBody(body.format)), {
|
|
5542
5623
|
headers: originHeaders,
|
|
@@ -5544,13 +5625,13 @@ async function startGranolaServer(app, options = {}) {
|
|
|
5544
5625
|
});
|
|
5545
5626
|
return;
|
|
5546
5627
|
}
|
|
5547
|
-
if (method === "GET" && path ===
|
|
5628
|
+
if (method === "GET" && path === granolaTransportPaths.exportJobs) {
|
|
5548
5629
|
const limit = parseInteger(url.searchParams.get("limit"));
|
|
5549
5630
|
sendJson(response, await app.listExportJobs({ limit }), { headers: originHeaders });
|
|
5550
5631
|
return;
|
|
5551
5632
|
}
|
|
5552
|
-
if (method === "POST" && path.startsWith(
|
|
5553
|
-
const id = decodeURIComponent(path.slice(
|
|
5633
|
+
if (method === "POST" && path.startsWith(`${granolaTransportPaths.exportJobs}/`) && path.endsWith("/rerun")) {
|
|
5634
|
+
const id = decodeURIComponent(path.slice(`${granolaTransportPaths.exportJobs}/`.length, -6));
|
|
5554
5635
|
if (!id) throw new Error("export job id is required");
|
|
5555
5636
|
sendJson(response, await app.rerunExportJob(id), {
|
|
5556
5637
|
headers: originHeaders,
|
|
@@ -5558,7 +5639,7 @@ async function startGranolaServer(app, options = {}) {
|
|
|
5558
5639
|
});
|
|
5559
5640
|
return;
|
|
5560
5641
|
}
|
|
5561
|
-
if (method === "POST" && path ===
|
|
5642
|
+
if (method === "POST" && path === granolaTransportPaths.exportTranscripts) {
|
|
5562
5643
|
const body = await readJsonBody(request);
|
|
5563
5644
|
sendJson(response, await app.exportTranscripts(transcriptFormatFromBody(body.format)), {
|
|
5564
5645
|
headers: originHeaders,
|
|
@@ -5630,6 +5711,7 @@ function printWebRoutes() {
|
|
|
5630
5711
|
console.log("Routes:");
|
|
5631
5712
|
console.log(" GET /");
|
|
5632
5713
|
console.log(" GET /health");
|
|
5714
|
+
console.log(" GET /server/info");
|
|
5633
5715
|
console.log(" POST /auth/unlock");
|
|
5634
5716
|
console.log(" POST /auth/lock");
|
|
5635
5717
|
console.log(" GET /auth/status");
|
|
@@ -6042,6 +6124,7 @@ const serveCommand = {
|
|
|
6042
6124
|
if (trustedOrigins.length > 0) console.log(`Trusted origins: ${trustedOrigins.join(", ")}`);
|
|
6043
6125
|
console.log("Endpoints:");
|
|
6044
6126
|
console.log(" GET /health");
|
|
6127
|
+
console.log(" GET /server/info");
|
|
6045
6128
|
console.log(" POST /auth/unlock");
|
|
6046
6129
|
console.log(" POST /auth/lock");
|
|
6047
6130
|
console.log(" GET /auth/status");
|