@corsair-dev/cli 0.1.1

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 ADDED
@@ -0,0 +1,1302 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // get-tsconfig-info.ts
13
+ import fs from "fs";
14
+ import path from "path";
15
+ function stripJsonComments(jsonString) {
16
+ return jsonString.replace(
17
+ /\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g,
18
+ (m, g) => g ? "" : m
19
+ ).replace(/,(?=\s*[}\]])/g, "");
20
+ }
21
+ function getTsconfigInfo(cwd, flatPath) {
22
+ let tsConfigPath;
23
+ if (flatPath) {
24
+ tsConfigPath = flatPath;
25
+ } else {
26
+ tsConfigPath = cwd ? path.join(cwd, "tsconfig.json") : path.join("tsconfig.json");
27
+ }
28
+ try {
29
+ const text2 = fs.readFileSync(tsConfigPath, "utf-8");
30
+ return JSON.parse(stripJsonComments(text2));
31
+ } catch (error) {
32
+ throw error;
33
+ }
34
+ }
35
+ var init_get_tsconfig_info = __esm({
36
+ "get-tsconfig-info.ts"() {
37
+ "use strict";
38
+ }
39
+ });
40
+
41
+ // auth.ts
42
+ var auth_exports = {};
43
+ __export(auth_exports, {
44
+ runAuth: () => runAuth
45
+ });
46
+ import * as http from "http";
47
+ import * as https from "https";
48
+ import * as net from "net";
49
+ import * as querystring from "querystring";
50
+ import {
51
+ CORSAIR_INTERNAL,
52
+ createAccountKeyManager,
53
+ createIntegrationKeyManager,
54
+ encryptDEK,
55
+ generateDEK
56
+ } from "corsair/core";
57
+ import { createCorsairOrm } from "corsair/orm";
58
+ function out(data) {
59
+ console.log(JSON.stringify(data));
60
+ }
61
+ function getOAuthConfigForPlugin(plugin) {
62
+ return plugin.oauthConfig ?? null;
63
+ }
64
+ function getAuthType(plugin) {
65
+ return plugin.options?.authType;
66
+ }
67
+ function getCustomIntegrationFields(plugin, authType) {
68
+ const authConfig = plugin.authConfig;
69
+ return authConfig?.[authType]?.integration ?? [];
70
+ }
71
+ function getCustomAccountFields(plugin, authType) {
72
+ const authConfig = plugin.authConfig;
73
+ return authConfig?.[authType]?.account ?? [];
74
+ }
75
+ function findFreePort() {
76
+ return new Promise((resolve, reject) => {
77
+ const server = net.createServer();
78
+ server.listen(0, "127.0.0.1", () => {
79
+ const address = server.address();
80
+ const port = typeof address === "object" && address ? address.port : 0;
81
+ server.close((err) => {
82
+ if (err) reject(err);
83
+ else resolve(port);
84
+ });
85
+ });
86
+ server.on("error", reject);
87
+ });
88
+ }
89
+ function waitForOAuthCode(port) {
90
+ return new Promise((resolve, reject) => {
91
+ const server = http.createServer((req, res) => {
92
+ const url = new URL(req.url ?? "/", `http://localhost:${port}`);
93
+ const code = url.searchParams.get("code");
94
+ const error = url.searchParams.get("error");
95
+ if (error) {
96
+ res.writeHead(400, { "Content-Type": "text/html" });
97
+ res.end(`<html><body><h2>Authorization failed: ${error}</h2><p>You can close this tab.</p></body></html>`);
98
+ server.close();
99
+ reject(new Error(`OAuth error: ${error}`));
100
+ return;
101
+ }
102
+ if (code) {
103
+ res.writeHead(200, { "Content-Type": "text/html" });
104
+ res.end("<html><body><h2>Authorization successful!</h2><p>You can close this tab and return to the terminal.</p></body></html>");
105
+ server.close();
106
+ resolve(code);
107
+ } else {
108
+ res.writeHead(400, { "Content-Type": "text/html" });
109
+ res.end("<html><body><h2>No code received.</h2><p>You can close this tab.</p></body></html>");
110
+ }
111
+ });
112
+ server.listen(port, "127.0.0.1", () => {
113
+ });
114
+ server.on("error", reject);
115
+ });
116
+ }
117
+ function exchangeCodeForTokens(code, clientId, clientSecret, oauthConfig, redirectUri) {
118
+ const tokenUrl = new URL(oauthConfig.tokenUrl);
119
+ const useBasicAuth = oauthConfig.tokenAuthMethod === "basic";
120
+ return new Promise((resolve, reject) => {
121
+ const postDataParams = {
122
+ code: code.trim(),
123
+ redirect_uri: redirectUri,
124
+ grant_type: "authorization_code"
125
+ };
126
+ if (!useBasicAuth) {
127
+ postDataParams.client_id = clientId;
128
+ postDataParams.client_secret = clientSecret;
129
+ }
130
+ const postData = querystring.stringify(postDataParams);
131
+ const headers = {
132
+ "Content-Type": "application/x-www-form-urlencoded",
133
+ "Content-Length": Buffer.byteLength(postData).toString()
134
+ };
135
+ if (useBasicAuth) {
136
+ headers.Authorization = `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString("base64")}`;
137
+ }
138
+ const req = https.request(
139
+ { hostname: tokenUrl.hostname, path: tokenUrl.pathname, method: "POST", headers },
140
+ (res) => {
141
+ let data = "";
142
+ res.on("data", (chunk) => {
143
+ data += chunk;
144
+ });
145
+ res.on("end", () => {
146
+ if (res.statusCode !== 200) {
147
+ reject(new Error(`Token exchange failed: ${data}`));
148
+ return;
149
+ }
150
+ resolve(JSON.parse(data));
151
+ });
152
+ }
153
+ );
154
+ req.on("error", (error) => reject(new Error(`Request failed: ${error.message}`)));
155
+ req.write(postData);
156
+ req.end();
157
+ });
158
+ }
159
+ async function extractInternalConfig(cwd) {
160
+ const instance = await getCorsairInstance({ cwd, shouldThrowOnError: true });
161
+ const internal = instance[CORSAIR_INTERNAL];
162
+ if (!internal) {
163
+ throw new Error(
164
+ "Could not read internal config from Corsair instance. Make sure you are using the latest version of corsair."
165
+ );
166
+ }
167
+ return internal;
168
+ }
169
+ async function ensureIntegration(database, pluginId, kek) {
170
+ const orm = createCorsairOrm(database);
171
+ const existing = await orm.integrations.findByName(pluginId);
172
+ if (existing) return { id: existing.id };
173
+ const dek = generateDEK();
174
+ const encryptedDek = await encryptDEK(dek, kek);
175
+ const row = await orm.integrations.create({ name: pluginId, config: {}, dek: encryptedDek });
176
+ return { id: row.id };
177
+ }
178
+ async function ensureAccount(database, integrationId, tenantId, kek) {
179
+ const orm = createCorsairOrm(database);
180
+ const existing = await orm.accounts.findOne({ tenant_id: tenantId, integration_id: integrationId });
181
+ if (existing) return;
182
+ const dek = generateDEK();
183
+ const encryptedDek = await encryptDEK(dek, kek);
184
+ await orm.accounts.create({ tenant_id: tenantId, integration_id: integrationId, config: {}, dek: encryptedDek });
185
+ }
186
+ async function oauthGetUrl(database, plugin, kek, tenantId) {
187
+ const oauthCfg = getOAuthConfigForPlugin(plugin);
188
+ if (!oauthCfg) {
189
+ out({ error: `No oauthConfig defined on plugin '${plugin.id}'.` });
190
+ return;
191
+ }
192
+ const extraFields = getCustomIntegrationFields(plugin, "oauth_2");
193
+ const integrationKm = createIntegrationKeyManager({
194
+ authType: "oauth_2",
195
+ integrationName: plugin.id,
196
+ kek,
197
+ database,
198
+ extraIntegrationFields: extraFields
199
+ });
200
+ const clientId = await integrationKm.get_client_id();
201
+ if (!clientId) {
202
+ out({ error: `client_id not set for '${plugin.id}'. Run: corsair setup --${plugin.id} client_id=YOUR_CLIENT_ID` });
203
+ return;
204
+ }
205
+ const clientSecret = await integrationKm.get_client_secret();
206
+ if (!clientSecret) {
207
+ out({ error: `client_secret not set for '${plugin.id}'. Run: corsair setup --${plugin.id} client_secret=YOUR_CLIENT_SECRET` });
208
+ return;
209
+ }
210
+ let redirectUri;
211
+ if (oauthCfg.requiresRegisteredRedirect) {
212
+ const stored = await integrationKm.get_redirect_url();
213
+ if (!stored) {
214
+ out({ error: `redirect_url required for '${plugin.id}'. Run: corsair setup --${plugin.id} redirect_url=YOUR_REDIRECT_URI` });
215
+ return;
216
+ }
217
+ redirectUri = stored;
218
+ } else {
219
+ const port = await findFreePort();
220
+ redirectUri = `http://localhost:${port}`;
221
+ }
222
+ const authParams = {
223
+ client_id: clientId,
224
+ redirect_uri: redirectUri,
225
+ response_type: "code",
226
+ scope: oauthCfg.scopes.join(" "),
227
+ ...oauthCfg.authParams
228
+ };
229
+ const authUrl = `${oauthCfg.authUrl}?${querystring.stringify(authParams)}`;
230
+ if (!oauthCfg.requiresRegisteredRedirect) {
231
+ const portMatch = redirectUri.match(/:(\d+)$/);
232
+ const port = portMatch?.[1] ? parseInt(portMatch[1], 10) : null;
233
+ if (port) {
234
+ out({ status: "pending_oauth", authUrl, redirectUri, plugin: plugin.id, tenant: tenantId, note: "Open authUrl in a browser. Tokens will be saved automatically once authorized." });
235
+ let code;
236
+ try {
237
+ code = await waitForOAuthCode(port);
238
+ } catch (err) {
239
+ out({ error: `Authorization failed: ${err instanceof Error ? err.message : String(err)}` });
240
+ return;
241
+ }
242
+ await oauthExchangeCode(database, plugin, kek, tenantId, code, redirectUri, clientId, clientSecret, oauthCfg);
243
+ return;
244
+ }
245
+ }
246
+ out({ status: "needs_code", authUrl, redirectUri, plugin: plugin.id, tenant: tenantId, note: "Open authUrl, complete auth, then run: corsair auth --plugin=<id> --code=CODE" });
247
+ }
248
+ async function oauthExchangeCode(database, plugin, kek, tenantId, code, redirectUri, clientId, clientSecret, oauthCfg) {
249
+ let tokens;
250
+ try {
251
+ tokens = await exchangeCodeForTokens(code, clientId, clientSecret, oauthCfg, redirectUri);
252
+ } catch (err) {
253
+ out({ error: `Token exchange failed: ${err instanceof Error ? err.message : String(err)}` });
254
+ return;
255
+ }
256
+ if (!tokens.access_token) {
257
+ out({ error: `No access_token in response from ${oauthCfg.providerName}.` });
258
+ return;
259
+ }
260
+ const extraAccountFields = getCustomAccountFields(plugin, "oauth_2");
261
+ const accountKm = createAccountKeyManager({
262
+ authType: "oauth_2",
263
+ integrationName: plugin.id,
264
+ tenantId,
265
+ kek,
266
+ database,
267
+ extraAccountFields
268
+ });
269
+ await accountKm.set_access_token(tokens.access_token);
270
+ if (tokens.refresh_token) await accountKm.set_refresh_token(tokens.refresh_token);
271
+ if (tokens.expires_in) await accountKm.set_expires_at((Date.now() + tokens.expires_in * 1e3).toString());
272
+ out({ status: "success", plugin: plugin.id, tenant: tenantId });
273
+ }
274
+ async function oauthWithCode(database, plugin, kek, tenantId, code) {
275
+ const oauthCfg = getOAuthConfigForPlugin(plugin);
276
+ if (!oauthCfg) {
277
+ out({ error: `No oauthConfig defined on plugin '${plugin.id}'.` });
278
+ return;
279
+ }
280
+ const extraFields = getCustomIntegrationFields(plugin, "oauth_2");
281
+ const integrationKm = createIntegrationKeyManager({
282
+ authType: "oauth_2",
283
+ integrationName: plugin.id,
284
+ kek,
285
+ database,
286
+ extraIntegrationFields: extraFields
287
+ });
288
+ const clientId = await integrationKm.get_client_id();
289
+ const clientSecret = await integrationKm.get_client_secret();
290
+ const redirectUri = await integrationKm.get_redirect_url() ?? "";
291
+ if (!clientId || !clientSecret) {
292
+ out({ error: `client_id and client_secret must be set before exchanging a code.` });
293
+ return;
294
+ }
295
+ await oauthExchangeCode(database, plugin, kek, tenantId, code, redirectUri, clientId, clientSecret, oauthCfg);
296
+ }
297
+ async function getCredentials(database, plugin, kek, tenantId) {
298
+ const authType = getAuthType(plugin);
299
+ const result = {};
300
+ if (authType === "oauth_2") {
301
+ const extraIntegrationFields = getCustomIntegrationFields(plugin, authType);
302
+ const integrationKm = createIntegrationKeyManager({
303
+ authType,
304
+ integrationName: plugin.id,
305
+ kek,
306
+ database,
307
+ extraIntegrationFields
308
+ });
309
+ result.client_id = await integrationKm.get_client_id();
310
+ result.client_secret = await integrationKm.get_client_secret();
311
+ result.redirect_url = await integrationKm.get_redirect_url();
312
+ for (const field of extraIntegrationFields) {
313
+ const fn = integrationKm[`get_${field}`];
314
+ if (typeof fn === "function") result[field] = await fn();
315
+ }
316
+ const extraAccountFields = getCustomAccountFields(plugin, authType);
317
+ const accountKm = createAccountKeyManager({
318
+ authType,
319
+ integrationName: plugin.id,
320
+ tenantId,
321
+ kek,
322
+ database,
323
+ extraAccountFields
324
+ });
325
+ result.access_token = await accountKm.get_access_token();
326
+ result.refresh_token = await accountKm.get_refresh_token();
327
+ result.expires_at = await accountKm.get_expires_at();
328
+ result.webhook_signature = await accountKm.get_webhook_signature();
329
+ for (const field of extraAccountFields) {
330
+ const fn = accountKm[`get_${field}`];
331
+ if (typeof fn === "function") result[field] = await fn();
332
+ }
333
+ } else if (authType === "api_key") {
334
+ const extraAccountFields = getCustomAccountFields(plugin, authType);
335
+ const accountKm = createAccountKeyManager({
336
+ authType,
337
+ integrationName: plugin.id,
338
+ tenantId,
339
+ kek,
340
+ database,
341
+ extraAccountFields
342
+ });
343
+ result.api_key = await accountKm.get_api_key();
344
+ result.webhook_signature = await accountKm.get_webhook_signature();
345
+ } else if (authType === "bot_token") {
346
+ const extraAccountFields = getCustomAccountFields(plugin, authType);
347
+ const accountKm = createAccountKeyManager({
348
+ authType,
349
+ integrationName: plugin.id,
350
+ tenantId,
351
+ kek,
352
+ database,
353
+ extraAccountFields
354
+ });
355
+ result.bot_token = await accountKm.get_bot_token();
356
+ result.webhook_signature = await accountKm.get_webhook_signature();
357
+ }
358
+ const masked = {};
359
+ for (const [k, v] of Object.entries(result)) {
360
+ if (!v) {
361
+ masked[k] = null;
362
+ continue;
363
+ }
364
+ masked[k] = v.length <= 9 ? "***" : `${v.slice(0, 6)}...${v.slice(-3)}`;
365
+ }
366
+ out({ plugin: plugin.id, tenant: tenantId, credentials: masked });
367
+ }
368
+ async function runAuth({
369
+ cwd,
370
+ pluginId: pluginIdArg,
371
+ tenantId: tenantIdArg,
372
+ code: codeArg,
373
+ credentials: showCredentials = false
374
+ }) {
375
+ let internal;
376
+ try {
377
+ internal = await extractInternalConfig(cwd);
378
+ } catch (error) {
379
+ out({ error: error instanceof Error ? error.message : String(error) });
380
+ process.exit(1);
381
+ }
382
+ const { plugins, database, kek } = internal;
383
+ if (!database) {
384
+ out({ error: "No database adapter configured." });
385
+ process.exit(1);
386
+ }
387
+ if (!pluginIdArg) {
388
+ out({
389
+ error: "No plugin specified. Use --plugin=<id>.",
390
+ available: plugins.map((pl) => ({ id: pl.id, authType: getAuthType(pl) }))
391
+ });
392
+ process.exit(1);
393
+ }
394
+ const plugin = plugins.find((pl) => pl.id === pluginIdArg);
395
+ if (!plugin) {
396
+ out({ error: `Plugin '${pluginIdArg}' not found.`, available: plugins.map((pl) => pl.id) });
397
+ process.exit(1);
398
+ }
399
+ const integration = await ensureIntegration(database, plugin.id, kek);
400
+ const tenantId = tenantIdArg ?? "default";
401
+ await ensureAccount(database, integration.id, tenantId, kek);
402
+ if (showCredentials) {
403
+ await getCredentials(database, plugin, kek, tenantId);
404
+ return;
405
+ }
406
+ const authType = getAuthType(plugin);
407
+ if (authType !== "oauth_2") {
408
+ out({ error: `'corsair auth' is for OAuth flows. Plugin '${plugin.id}' uses '${authType}'. Set credentials via: corsair setup --${plugin.id} <field>=<value>` });
409
+ return;
410
+ }
411
+ if (codeArg) {
412
+ await oauthWithCode(database, plugin, kek, tenantId, codeArg);
413
+ } else {
414
+ await oauthGetUrl(database, plugin, kek, tenantId);
415
+ }
416
+ }
417
+ var init_auth = __esm({
418
+ "auth.ts"() {
419
+ "use strict";
420
+ init_index();
421
+ }
422
+ });
423
+
424
+ // watch-renew.ts
425
+ var watch_renew_exports = {};
426
+ __export(watch_renew_exports, {
427
+ runWatchRenew: () => runWatchRenew
428
+ });
429
+ import { execSync } from "child_process";
430
+ import * as crypto from "crypto";
431
+ import * as p from "@clack/prompts";
432
+ import {
433
+ CORSAIR_INTERNAL as CORSAIR_INTERNAL2,
434
+ createAccountKeyManager as createAccountKeyManager2,
435
+ createIntegrationKeyManager as createIntegrationKeyManager2
436
+ } from "corsair/core";
437
+ async function extractInternalConfig2(cwd) {
438
+ const instance = await getCorsairInstance({ cwd, shouldThrowOnError: true });
439
+ const internal = instance[CORSAIR_INTERNAL2];
440
+ if (!internal) {
441
+ throw new Error(
442
+ "Could not read internal config from Corsair instance. Make sure you are using the latest version of corsair."
443
+ );
444
+ }
445
+ return internal;
446
+ }
447
+ async function refreshAccessToken(clientId, clientSecret, refreshToken) {
448
+ const response = await fetch("https://oauth2.googleapis.com/token", {
449
+ method: "POST",
450
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
451
+ body: new URLSearchParams({
452
+ client_id: clientId,
453
+ client_secret: clientSecret,
454
+ refresh_token: refreshToken,
455
+ grant_type: "refresh_token"
456
+ })
457
+ });
458
+ if (!response.ok) {
459
+ const error = await response.text();
460
+ throw new Error(`Failed to refresh access token: ${error}`);
461
+ }
462
+ const data = await response.json();
463
+ return data.access_token;
464
+ }
465
+ async function renewGmailWatch(accessToken, topicName) {
466
+ const watchSpin = p.spinner();
467
+ watchSpin.start("Starting Gmail watch...");
468
+ const response = await fetch(`${GMAIL_API_BASE}/users/me/watch`, {
469
+ method: "POST",
470
+ headers: {
471
+ Authorization: `Bearer ${accessToken}`,
472
+ "Content-Type": "application/json"
473
+ },
474
+ body: JSON.stringify({
475
+ topicName,
476
+ labelIds: ["INBOX"]
477
+ })
478
+ });
479
+ if (!response.ok) {
480
+ const error = await response.text();
481
+ watchSpin.stop("Failed.");
482
+ p.log.error(`Gmail watch failed: ${error}`);
483
+ p.outro("");
484
+ process.exit(1);
485
+ }
486
+ const data = await response.json();
487
+ watchSpin.stop("Watch started.");
488
+ p.note(
489
+ `History ID: ${data.historyId}
490
+ Expiration: ${new Date(Number(data.expiration)).toISOString()}`,
491
+ "Watch Response"
492
+ );
493
+ }
494
+ async function renewDriveWatch(accessToken, webhookUrl) {
495
+ const watchSpin = p.spinner();
496
+ watchSpin.start("Getting start page token...");
497
+ const startPageTokenRes = await fetch(
498
+ `${DRIVE_API_BASE}/changes/startPageToken`,
499
+ {
500
+ headers: { Authorization: `Bearer ${accessToken}` }
501
+ }
502
+ );
503
+ if (!startPageTokenRes.ok) {
504
+ const error = await startPageTokenRes.text();
505
+ watchSpin.stop("Failed.");
506
+ p.log.error(`Failed to get start page token: ${error}`);
507
+ p.outro("");
508
+ process.exit(1);
509
+ }
510
+ const startPageTokenData = await startPageTokenRes.json();
511
+ watchSpin.message("Starting Drive watch...");
512
+ const channelId = crypto.randomUUID();
513
+ const watchRes = await fetch(
514
+ `${DRIVE_API_BASE}/changes/watch?pageToken=${startPageTokenData.startPageToken}`,
515
+ {
516
+ method: "POST",
517
+ headers: {
518
+ Authorization: `Bearer ${accessToken}`,
519
+ "Content-Type": "application/json"
520
+ },
521
+ body: JSON.stringify({
522
+ id: channelId,
523
+ type: "web_hook",
524
+ address: webhookUrl
525
+ })
526
+ }
527
+ );
528
+ if (!watchRes.ok) {
529
+ const error = await watchRes.text();
530
+ watchSpin.stop("Failed.");
531
+ p.log.error(`Drive watch failed: ${error}`);
532
+ p.outro("");
533
+ process.exit(1);
534
+ }
535
+ const data = await watchRes.json();
536
+ watchSpin.stop("Watch started.");
537
+ p.note(
538
+ `Channel ID: ${channelId}
539
+ Resource ID: ${data.resourceId}
540
+ Start Page Token: ${startPageTokenData.startPageToken}
541
+ Expiration: ${new Date(Number(data.expiration)).toISOString()}`,
542
+ "Watch Response"
543
+ );
544
+ }
545
+ async function renewCalendarWatch(accessToken, webhookUrl, calendarId) {
546
+ const watchSpin = p.spinner();
547
+ watchSpin.start("Starting Calendar watch...");
548
+ const channelId = crypto.randomUUID();
549
+ const watchRes = await fetch(
550
+ `${CALENDAR_API_BASE}/calendars/${encodeURIComponent(calendarId)}/events/watch`,
551
+ {
552
+ method: "POST",
553
+ headers: {
554
+ Authorization: `Bearer ${accessToken}`,
555
+ "Content-Type": "application/json"
556
+ },
557
+ body: JSON.stringify({
558
+ id: channelId,
559
+ type: "web_hook",
560
+ address: webhookUrl
561
+ })
562
+ }
563
+ );
564
+ if (!watchRes.ok) {
565
+ const error = await watchRes.text();
566
+ watchSpin.stop("Failed.");
567
+ p.log.error(`Calendar watch failed: ${error}`);
568
+ p.outro("");
569
+ process.exit(1);
570
+ }
571
+ const data = await watchRes.json();
572
+ watchSpin.stop("Watch started.");
573
+ p.note(
574
+ `Channel ID: ${channelId}
575
+ Resource ID: ${data.resourceId}
576
+ Expiration: ${new Date(Number(data.expiration)).toISOString()}`,
577
+ "Watch Response"
578
+ );
579
+ }
580
+ function copyToClipboard(text2) {
581
+ try {
582
+ const platform = process.platform;
583
+ if (platform === "darwin") {
584
+ execSync("pbcopy", { input: text2 });
585
+ return true;
586
+ } else if (platform === "linux") {
587
+ try {
588
+ execSync("xclip -selection clipboard", { input: text2 });
589
+ return true;
590
+ } catch {
591
+ try {
592
+ execSync("xsel --clipboard --input", { input: text2 });
593
+ return true;
594
+ } catch {
595
+ return false;
596
+ }
597
+ }
598
+ } else if (platform === "win32") {
599
+ execSync("clip", { input: text2 });
600
+ return true;
601
+ }
602
+ return false;
603
+ } catch {
604
+ return false;
605
+ }
606
+ }
607
+ async function renewSheetsWatch(webhookUrl) {
608
+ const appsScriptCode = `var WEBHOOK_URL = "${webhookUrl}";
609
+
610
+ function onEditTrigger(e) {
611
+ if (!e || !e.range) return;
612
+
613
+ var sheet = e.range.getSheet();
614
+ var payload = {
615
+ spreadsheetId: e.source.getId(),
616
+ sheetName: sheet.getName(),
617
+ range: e.range.getA1Notation(),
618
+ values: e.range.getValues(),
619
+ eventType: "rangeUpdated",
620
+ timestamp: new Date().toISOString()
621
+ };
622
+
623
+ UrlFetchApp.fetch(WEBHOOK_URL, {
624
+ method: "post",
625
+ contentType: "application/json",
626
+ payload: JSON.stringify(payload)
627
+ });
628
+ }
629
+
630
+ function onChangeTrigger(e) {
631
+ if (!e) return;
632
+
633
+ var ss = SpreadsheetApp.getActiveSpreadsheet();
634
+ var sheet = ss.getActiveSheet();
635
+ var lastRow = sheet.getLastRow();
636
+ var lastCol = sheet.getLastColumn();
637
+ if (lastRow === 0 || lastCol === 0) return;
638
+
639
+ var values = sheet.getRange(lastRow, 1, 1, lastCol).getValues();
640
+
641
+ var payload = {
642
+ spreadsheetId: ss.getId(),
643
+ sheetName: sheet.getName(),
644
+ range: "A" + lastRow + ":" + String.fromCharCode(64 + lastCol) + lastRow,
645
+ values: values,
646
+ eventType: "rangeUpdated",
647
+ timestamp: new Date().toISOString()
648
+ };
649
+
650
+ UrlFetchApp.fetch(WEBHOOK_URL, {
651
+ method: "post",
652
+ contentType: "application/json",
653
+ payload: JSON.stringify(payload)
654
+ });
655
+ }
656
+
657
+ function createTriggers() {
658
+ var ss = SpreadsheetApp.getActiveSpreadsheet();
659
+
660
+ ScriptApp.newTrigger("onEditTrigger").forSpreadsheet(ss).onEdit().create();
661
+ ScriptApp.newTrigger("onChangeTrigger").forSpreadsheet(ss).onChange().create();
662
+
663
+ Logger.log("Triggers created successfully!");
664
+ }
665
+
666
+ function removeTriggers() {
667
+ var triggers = ScriptApp.getProjectTriggers();
668
+ triggers.forEach(function(trigger) {
669
+ ScriptApp.deleteTrigger(trigger);
670
+ });
671
+ Logger.log("All triggers removed.");
672
+ }`;
673
+ p.log.info("Google Sheets does not have a native watch API.");
674
+ p.log.info(
675
+ "Instead, you need to set up Google Apps Script triggers in your spreadsheet."
676
+ );
677
+ p.log.info("");
678
+ const shouldCopy = await p.confirm({
679
+ message: "Copy Apps Script code to clipboard?",
680
+ initialValue: true
681
+ });
682
+ let copied = false;
683
+ if (shouldCopy) {
684
+ copied = copyToClipboard(appsScriptCode);
685
+ if (copied) {
686
+ p.log.success("Apps Script code copied to clipboard!");
687
+ } else {
688
+ p.log.warn("Failed to copy to clipboard. Code will be displayed below.");
689
+ }
690
+ }
691
+ p.log.info("");
692
+ p.log.info("Setup Instructions:");
693
+ p.log.info("1. Open your Google Sheet");
694
+ p.log.info("2. Go to Extensions > Apps Script");
695
+ p.log.info("3. Paste the code into the script editor");
696
+ p.log.info(
697
+ '4. Run the "createTriggers" function once to install the triggers'
698
+ );
699
+ p.log.info("5. Authorize the script when prompted");
700
+ p.log.info("");
701
+ if (!copied) {
702
+ p.log.info("Apps Script Code:");
703
+ p.log.info("");
704
+ p.log.info(appsScriptCode);
705
+ p.log.info("");
706
+ }
707
+ p.note(
708
+ `Webhook URL: ${webhookUrl}
709
+
710
+ The Apps Script code ${copied ? "has been copied to your clipboard" : "is displayed above"}. Paste it into your Google Sheet's Apps Script editor.`,
711
+ "Google Sheets Setup"
712
+ );
713
+ }
714
+ async function runWatchRenew({ cwd }) {
715
+ p.intro("Corsair \u2014 Google Watch Renewal");
716
+ const spin = p.spinner();
717
+ spin.start("Loading corsair instance...");
718
+ let internal;
719
+ try {
720
+ internal = await extractInternalConfig2(cwd);
721
+ } catch (error) {
722
+ spin.stop("Failed to load.");
723
+ p.log.error(error instanceof Error ? error.message : String(error));
724
+ p.outro("");
725
+ process.exit(1);
726
+ }
727
+ const { plugins, database, kek } = internal;
728
+ if (!database) {
729
+ spin.stop("Failed.");
730
+ p.log.error("No database adapter configured.");
731
+ p.outro("");
732
+ process.exit(1);
733
+ }
734
+ const googlePlugins = plugins.filter((pl) => {
735
+ const opts = pl.options;
736
+ return opts?.authType === "oauth_2" && GOOGLE_PLUGINS.includes(pl.id);
737
+ });
738
+ if (googlePlugins.length === 0) {
739
+ spin.stop("No Google OAuth2 plugins found.");
740
+ p.outro("");
741
+ process.exit(1);
742
+ }
743
+ spin.stop(
744
+ `Loaded. Found ${googlePlugins.length} Google plugin${googlePlugins.length === 1 ? "" : "s"}.`
745
+ );
746
+ const pluginId = await p.select({
747
+ message: "Select a Google integration:",
748
+ options: googlePlugins.map((pl) => ({
749
+ value: pl.id,
750
+ label: pl.id
751
+ }))
752
+ });
753
+ if (p.isCancel(pluginId)) {
754
+ p.cancel("Operation cancelled.");
755
+ process.exit(0);
756
+ }
757
+ const pluginType = pluginId;
758
+ if (pluginType === "googlesheets") {
759
+ const webhookUrl = await p.text({
760
+ message: "Enter webhook URL:",
761
+ placeholder: "https://example.com/api/webhook",
762
+ validate: (v) => {
763
+ if (!v || v.trim().length === 0) return "Webhook URL is required";
764
+ if (!v.startsWith("http://") && !v.startsWith("https://")) {
765
+ return "Webhook URL must start with http:// or https://";
766
+ }
767
+ }
768
+ });
769
+ if (p.isCancel(webhookUrl)) {
770
+ p.cancel("Operation cancelled.");
771
+ process.exit(0);
772
+ }
773
+ await renewSheetsWatch(webhookUrl);
774
+ p.outro("Done!");
775
+ return;
776
+ }
777
+ const tenantId = await p.text({
778
+ message: "Enter tenant ID:",
779
+ defaultValue: "default",
780
+ placeholder: "default"
781
+ });
782
+ if (p.isCancel(tenantId)) {
783
+ p.cancel("Operation cancelled.");
784
+ process.exit(0);
785
+ }
786
+ const credSpin = p.spinner();
787
+ credSpin.start("Fetching credentials...");
788
+ try {
789
+ const integrationKm = createIntegrationKeyManager2({
790
+ authType: "oauth_2",
791
+ integrationName: pluginId,
792
+ kek,
793
+ database
794
+ });
795
+ const accountKm = createAccountKeyManager2({
796
+ authType: "oauth_2",
797
+ integrationName: pluginId,
798
+ tenantId,
799
+ kek,
800
+ database
801
+ });
802
+ const clientId = await integrationKm.get_client_id();
803
+ const clientSecret = await integrationKm.get_client_secret();
804
+ const refreshToken = await accountKm.get_refresh_token();
805
+ if (!clientId || !clientSecret || !refreshToken) {
806
+ credSpin.stop("Missing credentials.");
807
+ p.log.error(
808
+ 'Client ID, Client Secret, and Refresh Token are required. Use "corsair auth" to set them.'
809
+ );
810
+ p.outro("");
811
+ process.exit(1);
812
+ }
813
+ const accessToken = await refreshAccessToken(
814
+ clientId,
815
+ clientSecret,
816
+ refreshToken
817
+ );
818
+ credSpin.stop("Credentials loaded.");
819
+ if (pluginType === "gmail") {
820
+ const topicName = await p.text({
821
+ message: "Enter Pub/Sub topic name:",
822
+ placeholder: "projects/my-project/topics/my-topic",
823
+ validate: (v) => {
824
+ if (!v || v.trim().length === 0) return "Topic name is required";
825
+ }
826
+ });
827
+ if (p.isCancel(topicName)) {
828
+ p.cancel("Operation cancelled.");
829
+ process.exit(0);
830
+ }
831
+ await renewGmailWatch(accessToken, topicName);
832
+ } else if (pluginType === "googledrive") {
833
+ const webhookUrl = await p.text({
834
+ message: "Enter webhook URL:",
835
+ placeholder: "https://example.com/api/webhook",
836
+ validate: (v) => {
837
+ if (!v || v.trim().length === 0) return "Webhook URL is required";
838
+ }
839
+ });
840
+ if (p.isCancel(webhookUrl)) {
841
+ p.cancel("Operation cancelled.");
842
+ process.exit(0);
843
+ }
844
+ await renewDriveWatch(accessToken, webhookUrl);
845
+ } else if (pluginType === "googlecalendar") {
846
+ const webhookUrl = await p.text({
847
+ message: "Enter webhook URL:",
848
+ placeholder: "https://example.com/api/webhook",
849
+ validate: (v) => {
850
+ if (!v || v.trim().length === 0) return "Webhook URL is required";
851
+ }
852
+ });
853
+ if (p.isCancel(webhookUrl)) {
854
+ p.cancel("Operation cancelled.");
855
+ process.exit(0);
856
+ }
857
+ const calendarId = await p.text({
858
+ message: "Enter calendar ID:",
859
+ defaultValue: "primary",
860
+ placeholder: "primary"
861
+ });
862
+ if (p.isCancel(calendarId)) {
863
+ p.cancel("Operation cancelled.");
864
+ process.exit(0);
865
+ }
866
+ await renewCalendarWatch(
867
+ accessToken,
868
+ webhookUrl,
869
+ calendarId
870
+ );
871
+ } else {
872
+ p.log.error(`Unsupported Google plugin: ${pluginType}`);
873
+ process.exit(1);
874
+ }
875
+ } catch (error) {
876
+ credSpin.stop("Failed.");
877
+ p.log.error(
878
+ `Error: ${error instanceof Error ? error.message : String(error)}`
879
+ );
880
+ process.exit(1);
881
+ }
882
+ p.outro("Done!");
883
+ }
884
+ var GMAIL_API_BASE, DRIVE_API_BASE, CALENDAR_API_BASE, GOOGLE_PLUGINS;
885
+ var init_watch_renew = __esm({
886
+ "watch-renew.ts"() {
887
+ "use strict";
888
+ init_index();
889
+ GMAIL_API_BASE = "https://gmail.googleapis.com/gmail/v1";
890
+ DRIVE_API_BASE = "https://www.googleapis.com/drive/v3";
891
+ CALENDAR_API_BASE = "https://www.googleapis.com/calendar/v3";
892
+ GOOGLE_PLUGINS = [
893
+ "gmail",
894
+ "googledrive",
895
+ "googlecalendar",
896
+ "googlesheets"
897
+ ];
898
+ }
899
+ });
900
+
901
+ // index.ts
902
+ import fs2, { existsSync } from "fs";
903
+ import path2 from "path";
904
+ import babelPresetReact from "@babel/preset-react";
905
+ import babelPresetTypeScript from "@babel/preset-typescript";
906
+ import { loadConfig } from "c12";
907
+ function resolveReferencePath(configDir, refPath) {
908
+ const resolvedPath = path2.resolve(configDir, refPath);
909
+ if (refPath.endsWith(".json")) {
910
+ return resolvedPath;
911
+ }
912
+ if (fs2.existsSync(resolvedPath)) {
913
+ try {
914
+ const stats = fs2.statSync(resolvedPath);
915
+ if (stats.isFile()) {
916
+ return resolvedPath;
917
+ }
918
+ } catch {
919
+ }
920
+ }
921
+ return path2.resolve(configDir, refPath, "tsconfig.json");
922
+ }
923
+ function getPathAliasesRecursive(tsconfigPath, visited = /* @__PURE__ */ new Set()) {
924
+ if (visited.has(tsconfigPath)) {
925
+ return {};
926
+ }
927
+ visited.add(tsconfigPath);
928
+ if (!fs2.existsSync(tsconfigPath)) {
929
+ console.warn(`Referenced tsconfig not found: ${tsconfigPath}`);
930
+ return {};
931
+ }
932
+ try {
933
+ const tsConfig = getTsconfigInfo(void 0, tsconfigPath);
934
+ const { paths = {}, baseUrl = "." } = tsConfig.compilerOptions || {};
935
+ const result = {};
936
+ const configDir = path2.dirname(tsconfigPath);
937
+ const obj = Object.entries(paths);
938
+ for (const [alias, aliasPaths] of obj) {
939
+ for (const aliasedPath of aliasPaths) {
940
+ const resolvedBaseUrl = path2.resolve(configDir, baseUrl);
941
+ const finalAlias = alias.slice(-1) === "*" ? alias.slice(0, -1) : alias;
942
+ const finalAliasedPath = aliasedPath.slice(-1) === "*" ? aliasedPath.slice(0, -1) : aliasedPath;
943
+ result[finalAlias || ""] = path2.join(resolvedBaseUrl, finalAliasedPath);
944
+ }
945
+ }
946
+ if (tsConfig.references) {
947
+ for (const ref of tsConfig.references) {
948
+ const refPath = resolveReferencePath(configDir, ref.path);
949
+ const refAliases = getPathAliasesRecursive(refPath, visited);
950
+ for (const [alias, aliasPath] of Object.entries(refAliases)) {
951
+ if (!(alias in result)) {
952
+ result[alias] = aliasPath;
953
+ }
954
+ }
955
+ }
956
+ }
957
+ return result;
958
+ } catch (error) {
959
+ console.warn(`Error parsing tsconfig at ${tsconfigPath}: ${error}`);
960
+ return {};
961
+ }
962
+ }
963
+ function getPathAliases(cwd) {
964
+ const tsConfigPath = path2.join(cwd, "tsconfig.json");
965
+ if (!fs2.existsSync(tsConfigPath)) {
966
+ return null;
967
+ }
968
+ try {
969
+ const result = getPathAliasesRecursive(tsConfigPath);
970
+ return result;
971
+ } catch (error) {
972
+ console.error(error);
973
+ throw new Error("Error parsing tsconfig.json");
974
+ }
975
+ }
976
+ function findCorsairConfigPath(cwd) {
977
+ for (const possiblePath of possiblePaths) {
978
+ const fullPath = path2.join(cwd, possiblePath);
979
+ if (existsSync(fullPath)) {
980
+ return fullPath;
981
+ }
982
+ }
983
+ return null;
984
+ }
985
+ async function getCorsairInstance({
986
+ cwd,
987
+ configPath,
988
+ shouldThrowOnError = false
989
+ }) {
990
+ try {
991
+ let corsairInstance = null;
992
+ if (configPath) {
993
+ let resolvedPath = path2.join(cwd, configPath);
994
+ if (existsSync(configPath)) {
995
+ resolvedPath = configPath;
996
+ }
997
+ const { config } = await loadConfig({
998
+ configFile: resolvedPath,
999
+ dotenv: true,
1000
+ jitiOptions: jitiOptions(cwd),
1001
+ cwd
1002
+ });
1003
+ if ("corsair" in config && isCorsairInstance(config.corsair)) {
1004
+ corsairInstance = config.corsair;
1005
+ } else if ("default" in config && isCorsairInstance(config.default)) {
1006
+ corsairInstance = config.default;
1007
+ } else {
1008
+ if (shouldThrowOnError) {
1009
+ throw new Error(
1010
+ `Couldn't read your Corsair instance in ${resolvedPath}. Make sure to export your Corsair instance as a variable named 'corsair' or as the default export.`
1011
+ );
1012
+ }
1013
+ console.error(
1014
+ `[#corsair]: Couldn't read your Corsair instance in ${resolvedPath}. Make sure to export your Corsair instance as a variable named 'corsair' or as the default export.`
1015
+ );
1016
+ process.exit(1);
1017
+ }
1018
+ }
1019
+ if (!corsairInstance) {
1020
+ for (const possiblePath of possiblePaths) {
1021
+ try {
1022
+ const { config } = await loadConfig({
1023
+ configFile: possiblePath,
1024
+ jitiOptions: jitiOptions(cwd),
1025
+ cwd
1026
+ });
1027
+ const hasConfig = Object.keys(config).length > 0;
1028
+ if (hasConfig) {
1029
+ if ("corsair" in config && isCorsairInstance(config.corsair)) {
1030
+ corsairInstance = config.corsair;
1031
+ } else if ("default" in config && isCorsairInstance(config.default)) {
1032
+ corsairInstance = config.default;
1033
+ }
1034
+ if (!corsairInstance) {
1035
+ if (shouldThrowOnError) {
1036
+ throw new Error(
1037
+ "Couldn't read your Corsair instance. Make sure to export your Corsair instance as a variable named 'corsair' or as the default export."
1038
+ );
1039
+ }
1040
+ console.error("[#corsair]: Couldn't read your Corsair instance.");
1041
+ console.log("");
1042
+ console.log(
1043
+ "[#corsair]: Make sure to export your Corsair instance as a variable named 'corsair' or as the default export."
1044
+ );
1045
+ process.exit(1);
1046
+ }
1047
+ break;
1048
+ }
1049
+ } catch (e) {
1050
+ if (typeof e === "object" && e && "message" in e && typeof e.message === "string" && e.message.includes(
1051
+ "This module cannot be imported from a Client Component module"
1052
+ )) {
1053
+ if (shouldThrowOnError) {
1054
+ throw new Error(
1055
+ `Please remove import 'server-only' from your corsair.ts file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`
1056
+ );
1057
+ }
1058
+ console.error(
1059
+ `Please remove import 'server-only' from your corsair.ts file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`
1060
+ );
1061
+ process.exit(1);
1062
+ }
1063
+ if (shouldThrowOnError) {
1064
+ throw e;
1065
+ }
1066
+ const msg = typeof e === "object" && e && "message" in e && typeof e.message === "string" ? e.message : String(e);
1067
+ console.error(`[#corsair]: Error loading ${possiblePath}:`, msg);
1068
+ process.exit(1);
1069
+ }
1070
+ }
1071
+ }
1072
+ if (!corsairInstance) {
1073
+ if (shouldThrowOnError) {
1074
+ throw new Error(
1075
+ "Couldn't find corsair.ts in your project. Make sure you have a corsair.ts file in your project root or in src/, lib/, or server/ directories."
1076
+ );
1077
+ }
1078
+ console.error("[#corsair]: Couldn't find corsair.ts in your project.");
1079
+ console.log("");
1080
+ console.log(
1081
+ "[#corsair]: Make sure you have a corsair.ts file in your project root or in src/, lib/, or server/ directories."
1082
+ );
1083
+ process.exit(1);
1084
+ }
1085
+ return corsairInstance;
1086
+ } catch (e) {
1087
+ if (typeof e === "object" && e && "message" in e && typeof e.message === "string" && e.message.includes(
1088
+ "This module cannot be imported from a Client Component module"
1089
+ )) {
1090
+ if (shouldThrowOnError) {
1091
+ throw new Error(
1092
+ `Please remove import 'server-only' from your corsair.ts file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`
1093
+ );
1094
+ }
1095
+ console.error(
1096
+ `Please remove import 'server-only' from your corsair.ts file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`
1097
+ );
1098
+ process.exit(1);
1099
+ }
1100
+ if (shouldThrowOnError) {
1101
+ throw e;
1102
+ }
1103
+ console.error("Couldn't read your Corsair instance.", e);
1104
+ process.exit(1);
1105
+ }
1106
+ }
1107
+ function parseAuthArgs(args) {
1108
+ let pluginId;
1109
+ let tenantId;
1110
+ let code;
1111
+ let credentials = false;
1112
+ for (const arg of args) {
1113
+ if (arg === "--credentials") {
1114
+ credentials = true;
1115
+ continue;
1116
+ }
1117
+ const eqIdx = arg.indexOf("=");
1118
+ if (arg.startsWith("--") && eqIdx !== -1) {
1119
+ const key = arg.slice(2, eqIdx);
1120
+ const value = arg.slice(eqIdx + 1);
1121
+ if (key === "plugin") {
1122
+ pluginId = value;
1123
+ continue;
1124
+ }
1125
+ if (key === "tenant") {
1126
+ tenantId = value;
1127
+ continue;
1128
+ }
1129
+ if (key === "code") {
1130
+ code = value;
1131
+ continue;
1132
+ }
1133
+ }
1134
+ }
1135
+ return { pluginId, tenantId, code, credentials };
1136
+ }
1137
+ function parseSetupArgs(args) {
1138
+ let backfill = false;
1139
+ const credentials = {};
1140
+ let currentPlugin = null;
1141
+ for (const arg of args) {
1142
+ if (arg === "--backfill" || arg === "-backfill") {
1143
+ backfill = true;
1144
+ currentPlugin = null;
1145
+ continue;
1146
+ }
1147
+ if (arg.startsWith("--")) {
1148
+ const name = arg.slice(2);
1149
+ if (RESERVED_FLAGS.has(name)) continue;
1150
+ currentPlugin = name;
1151
+ credentials[currentPlugin] = {};
1152
+ continue;
1153
+ }
1154
+ if (currentPlugin && arg.includes("=")) {
1155
+ const eqIdx = arg.indexOf("=");
1156
+ const field = arg.slice(0, eqIdx);
1157
+ const value = arg.slice(eqIdx + 1);
1158
+ credentials[currentPlugin][field] = value;
1159
+ }
1160
+ }
1161
+ return { backfill, credentials };
1162
+ }
1163
+ async function main() {
1164
+ const cwd = process.cwd();
1165
+ const args = process.argv.slice(2);
1166
+ const command = args[0];
1167
+ if (command === "help" || command === "--help" || command === "-h") {
1168
+ console.log("[#corsair]: Corsair CLI\n");
1169
+ console.log("Commands:");
1170
+ console.log(" corsair setup Initialize your Corsair instance");
1171
+ console.log(" corsair setup -backfill Initialize and backfill initial data");
1172
+ console.log(" corsair setup --<plugin> <field>=VALUE ... Set credentials for a plugin");
1173
+ console.log(" corsair auth --plugin=<id> Start OAuth flow (outputs auth URL as JSON)");
1174
+ console.log(" corsair auth --plugin=<id> --code=<code> Exchange OAuth code for tokens");
1175
+ console.log(" corsair auth --plugin=<id> --credentials Show current credential status");
1176
+ console.log(
1177
+ " corsair watch-renew Renew Google webhook watch (Gmail/Drive/Calendar)"
1178
+ );
1179
+ console.log(" corsair migrate Create a data migration script");
1180
+ console.log(" corsair migrate help Show migration help\n");
1181
+ return;
1182
+ }
1183
+ if (command === "setup") {
1184
+ const { backfill, credentials } = parseSetupArgs(args.slice(1));
1185
+ const { setupCorsair } = await import("corsair/setup");
1186
+ const instance = await getCorsairInstance({ cwd });
1187
+ await setupCorsair(instance, {
1188
+ backfill,
1189
+ credentials,
1190
+ caller: "cli"
1191
+ });
1192
+ return;
1193
+ }
1194
+ if (command === "auth") {
1195
+ const { runAuth: runAuth2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
1196
+ const authArgs = parseAuthArgs(args.slice(1));
1197
+ await runAuth2({ cwd, ...authArgs });
1198
+ return;
1199
+ }
1200
+ if (command === "watch-renew") {
1201
+ const { runWatchRenew: runWatchRenew2 } = await Promise.resolve().then(() => (init_watch_renew(), watch_renew_exports));
1202
+ await runWatchRenew2({ cwd });
1203
+ return;
1204
+ }
1205
+ console.log(`[#corsair]: Looking for Corsair instance in ${cwd}...
1206
+ `);
1207
+ try {
1208
+ const instance = await getCorsairInstance({ cwd });
1209
+ console.log("[#corsair]: \u2705 Successfully loaded Corsair instance!");
1210
+ if (instance && typeof instance === "object" && "withTenant" in instance) {
1211
+ console.log(
1212
+ "[#corsair]: Multi-tenant setup detected (has withTenant method)"
1213
+ );
1214
+ } else {
1215
+ console.log("[#corsair]: Single-tenant setup detected (direct client)");
1216
+ }
1217
+ if (instance && typeof instance === "object") {
1218
+ console.log(
1219
+ `[#corsair]: Instance keys: ${Object.keys(instance).join(", ")}`
1220
+ );
1221
+ }
1222
+ } catch (error) {
1223
+ console.error("[#corsair]: Error:", error);
1224
+ process.exit(1);
1225
+ }
1226
+ }
1227
+ var possiblePaths, jitiOptions, isCorsairInstance, RESERVED_FLAGS, isMainModule;
1228
+ var init_index = __esm({
1229
+ "index.ts"() {
1230
+ init_get_tsconfig_info();
1231
+ possiblePaths = [
1232
+ "corsair.ts",
1233
+ "corsair.tsx",
1234
+ "corsair.js",
1235
+ "corsair.jsx",
1236
+ "src/corsair.ts",
1237
+ "src/corsair.tsx",
1238
+ "src/corsair.js",
1239
+ "src/corsair.jsx",
1240
+ "lib/corsair.ts",
1241
+ "lib/corsair.tsx",
1242
+ "lib/corsair.js",
1243
+ "lib/corsair.jsx",
1244
+ "server/corsair.ts",
1245
+ "server/corsair.tsx",
1246
+ "server/corsair.js",
1247
+ "server/corsair.jsx",
1248
+ "src/server/corsair.ts",
1249
+ "src/server/corsair.tsx",
1250
+ "src/server/corsair.js",
1251
+ "src/server/corsair.jsx",
1252
+ "app/corsair.ts",
1253
+ "app/corsair.tsx",
1254
+ "app/corsair.js",
1255
+ "app/corsair.jsx",
1256
+ "corsair/index.ts",
1257
+ "corsair/index.tsx",
1258
+ "corsair/index.js",
1259
+ "corsair/index.jsx"
1260
+ ];
1261
+ jitiOptions = (cwd) => {
1262
+ const alias = getPathAliases(cwd) || {};
1263
+ return {
1264
+ transformOptions: {
1265
+ babel: {
1266
+ presets: [
1267
+ [
1268
+ babelPresetTypeScript,
1269
+ {
1270
+ isTSX: true,
1271
+ allExtensions: true
1272
+ }
1273
+ ],
1274
+ [babelPresetReact, { runtime: "automatic" }]
1275
+ ]
1276
+ }
1277
+ },
1278
+ extensions: [".ts", ".tsx", ".js", ".jsx"],
1279
+ alias
1280
+ };
1281
+ };
1282
+ isCorsairInstance = (object) => {
1283
+ if (typeof object !== "object" || object === null || Array.isArray(object)) {
1284
+ return false;
1285
+ }
1286
+ if ("withTenant" in object && typeof object.withTenant === "function") {
1287
+ return true;
1288
+ }
1289
+ return Object.keys(object).length > 0;
1290
+ };
1291
+ RESERVED_FLAGS = /* @__PURE__ */ new Set(["backfill", "help", "h"]);
1292
+ isMainModule = import.meta.url === `file://${process.argv[1]?.replace(/\\/g, "/")}` || process.argv[1]?.includes("index.ts") || process.argv[1]?.includes("index.js") || import.meta.url.endsWith("/index.ts") || import.meta.url.endsWith("/index.js");
1293
+ if (isMainModule) {
1294
+ main();
1295
+ }
1296
+ }
1297
+ });
1298
+ init_index();
1299
+ export {
1300
+ findCorsairConfigPath,
1301
+ getCorsairInstance
1302
+ };