oauth.do 0.1.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 ADDED
@@ -0,0 +1,800 @@
1
+ #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __commonJS = (cb, mod) => function __require() {
12
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
13
+ };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ __defProp(target, "default", { value: mod, enumerable: true }) ,
28
+ mod
29
+ ));
30
+
31
+ // src/storage.ts
32
+ function isNode() {
33
+ return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
34
+ }
35
+ function getEnv3(key) {
36
+ if (typeof process !== "undefined" && process.env?.[key]) return process.env[key];
37
+ return void 0;
38
+ }
39
+ function createSecureStorage() {
40
+ if (isNode()) {
41
+ return new CompositeTokenStorage();
42
+ }
43
+ if (typeof localStorage !== "undefined") {
44
+ return new LocalStorageTokenStorage();
45
+ }
46
+ return new MemoryTokenStorage();
47
+ }
48
+ var KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT, KeychainTokenStorage, SecureFileTokenStorage, MemoryTokenStorage, LocalStorageTokenStorage, CompositeTokenStorage;
49
+ var init_storage = __esm({
50
+ "src/storage.ts"() {
51
+ KEYCHAIN_SERVICE = "oauth.do";
52
+ KEYCHAIN_ACCOUNT = "access_token";
53
+ KeychainTokenStorage = class {
54
+ keytar = null;
55
+ initialized = false;
56
+ /**
57
+ * Lazily load keytar module
58
+ * Returns null if keytar is not available (e.g., missing native dependencies)
59
+ */
60
+ async getKeytar() {
61
+ if (this.initialized) {
62
+ return this.keytar;
63
+ }
64
+ this.initialized = true;
65
+ try {
66
+ this.keytar = await import('keytar');
67
+ return this.keytar;
68
+ } catch (error) {
69
+ if (getEnv3("DEBUG")) {
70
+ console.warn("Keychain storage not available:", error);
71
+ }
72
+ return null;
73
+ }
74
+ }
75
+ async getToken() {
76
+ const keytar = await this.getKeytar();
77
+ if (!keytar) {
78
+ return null;
79
+ }
80
+ try {
81
+ const token = await keytar.getPassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
82
+ return token;
83
+ } catch (error) {
84
+ if (getEnv3("DEBUG")) {
85
+ console.warn("Failed to get token from keychain:", error);
86
+ }
87
+ return null;
88
+ }
89
+ }
90
+ async setToken(token) {
91
+ const keytar = await this.getKeytar();
92
+ if (!keytar) {
93
+ throw new Error("Keychain storage not available");
94
+ }
95
+ try {
96
+ await keytar.setPassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT, token);
97
+ } catch (error) {
98
+ throw new Error(`Failed to save token to keychain: ${error}`);
99
+ }
100
+ }
101
+ async removeToken() {
102
+ const keytar = await this.getKeytar();
103
+ if (!keytar) {
104
+ return;
105
+ }
106
+ try {
107
+ await keytar.deletePassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
108
+ } catch {
109
+ }
110
+ }
111
+ /**
112
+ * Check if keychain storage is available on this system
113
+ */
114
+ async isAvailable() {
115
+ const keytar = await this.getKeytar();
116
+ if (!keytar) {
117
+ return false;
118
+ }
119
+ try {
120
+ await keytar.getPassword(KEYCHAIN_SERVICE, "__test__");
121
+ return true;
122
+ } catch {
123
+ return false;
124
+ }
125
+ }
126
+ };
127
+ SecureFileTokenStorage = class {
128
+ tokenPath = null;
129
+ configDir = null;
130
+ initialized = false;
131
+ async init() {
132
+ if (this.initialized) return this.tokenPath !== null;
133
+ this.initialized = true;
134
+ if (!isNode()) return false;
135
+ try {
136
+ const os = await import('os');
137
+ const path = await import('path');
138
+ this.configDir = path.join(os.homedir(), ".oauth.do");
139
+ this.tokenPath = path.join(this.configDir, "token");
140
+ return true;
141
+ } catch {
142
+ return false;
143
+ }
144
+ }
145
+ async getToken() {
146
+ if (!await this.init() || !this.tokenPath) return null;
147
+ try {
148
+ const fs = await import('fs/promises');
149
+ const stats = await fs.stat(this.tokenPath);
150
+ const mode = stats.mode & 511;
151
+ if (mode !== 384 && getEnv3("DEBUG")) {
152
+ console.warn(
153
+ `Warning: Token file has insecure permissions (${mode.toString(8)}). Expected 600. Run: chmod 600 ${this.tokenPath}`
154
+ );
155
+ }
156
+ const token = await fs.readFile(this.tokenPath, "utf-8");
157
+ return token.trim();
158
+ } catch {
159
+ return null;
160
+ }
161
+ }
162
+ async setToken(token) {
163
+ if (!await this.init() || !this.tokenPath || !this.configDir) {
164
+ throw new Error("File storage not available");
165
+ }
166
+ try {
167
+ const fs = await import('fs/promises');
168
+ await fs.mkdir(this.configDir, { recursive: true, mode: 448 });
169
+ await fs.writeFile(this.tokenPath, token, { encoding: "utf-8", mode: 384 });
170
+ await fs.chmod(this.tokenPath, 384);
171
+ } catch (error) {
172
+ console.error("Failed to save token:", error);
173
+ throw error;
174
+ }
175
+ }
176
+ async removeToken() {
177
+ if (!await this.init() || !this.tokenPath) return;
178
+ try {
179
+ const fs = await import('fs/promises');
180
+ await fs.unlink(this.tokenPath);
181
+ } catch {
182
+ }
183
+ }
184
+ };
185
+ MemoryTokenStorage = class {
186
+ token = null;
187
+ async getToken() {
188
+ return this.token;
189
+ }
190
+ async setToken(token) {
191
+ this.token = token;
192
+ }
193
+ async removeToken() {
194
+ this.token = null;
195
+ }
196
+ };
197
+ LocalStorageTokenStorage = class {
198
+ key = "oauth.do:token";
199
+ async getToken() {
200
+ if (typeof localStorage === "undefined") {
201
+ return null;
202
+ }
203
+ return localStorage.getItem(this.key);
204
+ }
205
+ async setToken(token) {
206
+ if (typeof localStorage === "undefined") {
207
+ throw new Error("localStorage is not available");
208
+ }
209
+ localStorage.setItem(this.key, token);
210
+ }
211
+ async removeToken() {
212
+ if (typeof localStorage === "undefined") {
213
+ return;
214
+ }
215
+ localStorage.removeItem(this.key);
216
+ }
217
+ };
218
+ CompositeTokenStorage = class {
219
+ keychainStorage;
220
+ fileStorage;
221
+ preferredStorage = null;
222
+ constructor() {
223
+ this.keychainStorage = new KeychainTokenStorage();
224
+ this.fileStorage = new SecureFileTokenStorage();
225
+ }
226
+ /**
227
+ * Determine the best available storage backend
228
+ */
229
+ async getPreferredStorage() {
230
+ if (this.preferredStorage) {
231
+ return this.preferredStorage;
232
+ }
233
+ if (await this.keychainStorage.isAvailable()) {
234
+ this.preferredStorage = this.keychainStorage;
235
+ return this.preferredStorage;
236
+ }
237
+ this.preferredStorage = this.fileStorage;
238
+ return this.preferredStorage;
239
+ }
240
+ async getToken() {
241
+ const keychainToken = await this.keychainStorage.getToken();
242
+ if (keychainToken) {
243
+ return keychainToken;
244
+ }
245
+ const fileToken = await this.fileStorage.getToken();
246
+ if (fileToken) {
247
+ if (await this.keychainStorage.isAvailable()) {
248
+ try {
249
+ await this.keychainStorage.setToken(fileToken);
250
+ await this.fileStorage.removeToken();
251
+ if (getEnv3("DEBUG")) {
252
+ console.log("Migrated token from file to keychain");
253
+ }
254
+ } catch {
255
+ }
256
+ }
257
+ return fileToken;
258
+ }
259
+ return null;
260
+ }
261
+ async setToken(token) {
262
+ const storage2 = await this.getPreferredStorage();
263
+ await storage2.setToken(token);
264
+ }
265
+ async removeToken() {
266
+ await Promise.all([this.keychainStorage.removeToken(), this.fileStorage.removeToken()]);
267
+ }
268
+ /**
269
+ * Get information about the current storage backend
270
+ */
271
+ async getStorageInfo() {
272
+ if (await this.keychainStorage.isAvailable()) {
273
+ return { type: "keychain", secure: true };
274
+ }
275
+ return { type: "file", secure: true };
276
+ }
277
+ };
278
+ }
279
+ });
280
+
281
+ // package.json
282
+ var require_package = __commonJS({
283
+ "package.json"(exports$1, module) {
284
+ module.exports = {
285
+ name: "oauth.do",
286
+ version: "0.1.0",
287
+ description: "OAuth authentication SDK and CLI for .do Platform",
288
+ type: "module",
289
+ main: "./dist/index.js",
290
+ types: "./dist/index.d.ts",
291
+ bin: {
292
+ "oauth.do": "./dist/cli.js"
293
+ },
294
+ exports: {
295
+ ".": {
296
+ types: "./dist/index.d.ts",
297
+ import: "./dist/index.js"
298
+ },
299
+ "./mdx/*": "./src/mdx/*"
300
+ },
301
+ files: [
302
+ "dist",
303
+ "src/mdx",
304
+ "README.md",
305
+ "LICENSE"
306
+ ],
307
+ scripts: {
308
+ build: "tsup",
309
+ dev: "tsup --watch",
310
+ test: "vitest run",
311
+ "test:watch": "vitest",
312
+ prepublishOnly: "pnpm build && pnpm test"
313
+ },
314
+ keywords: [
315
+ "oauth",
316
+ "authentication",
317
+ "auth",
318
+ "login",
319
+ "api-key",
320
+ "cli",
321
+ "sdk",
322
+ "platform",
323
+ "workos"
324
+ ],
325
+ author: {
326
+ name: "Platform.do",
327
+ email: "npm@platform.do",
328
+ url: "https://platform.do"
329
+ },
330
+ license: "MIT",
331
+ repository: {
332
+ type: "git",
333
+ url: "git+https://github.com/dot-do/oauth.do.git"
334
+ },
335
+ bugs: {
336
+ url: "https://github.com/dot-do/oauth.do/issues"
337
+ },
338
+ homepage: "https://oauth.do",
339
+ engines: {
340
+ node: ">=18.0.0"
341
+ },
342
+ dependencies: {
343
+ keytar: "^7.9.0"
344
+ },
345
+ devDependencies: {
346
+ "@types/node": "^24.10.1",
347
+ tsup: "^8.0.0",
348
+ typescript: "^5.5.2",
349
+ vitest: "^2.1.8"
350
+ }
351
+ };
352
+ }
353
+ });
354
+
355
+ // src/config.ts
356
+ function getEnv(key) {
357
+ if (globalThis[key]) return globalThis[key];
358
+ if (typeof process !== "undefined" && process.env?.[key]) return process.env[key];
359
+ return void 0;
360
+ }
361
+ var globalConfig = {
362
+ apiUrl: getEnv("OAUTH_API_URL") || getEnv("API_URL") || "https://apis.do",
363
+ clientId: getEnv("OAUTH_CLIENT_ID") || "oauth.do",
364
+ authKitDomain: getEnv("OAUTH_AUTHKIT_DOMAIN") || "login.oauth.do",
365
+ fetch: globalThis.fetch
366
+ };
367
+ function configure(config) {
368
+ globalConfig = {
369
+ ...globalConfig,
370
+ ...config
371
+ };
372
+ }
373
+ function getConfig() {
374
+ return globalConfig;
375
+ }
376
+
377
+ // src/device.ts
378
+ async function authorizeDevice() {
379
+ const config = getConfig();
380
+ if (!config.clientId) {
381
+ throw new Error('Client ID is required for device authorization. Set OAUTH_CLIENT_ID or configure({ clientId: "..." })');
382
+ }
383
+ try {
384
+ const response = await config.fetch(`https://${config.authKitDomain}/device/authorize`, {
385
+ method: "POST",
386
+ headers: {
387
+ "Content-Type": "application/x-www-form-urlencoded"
388
+ },
389
+ body: new URLSearchParams({
390
+ client_id: config.clientId
391
+ })
392
+ });
393
+ if (!response.ok) {
394
+ throw new Error(`Device authorization failed: ${response.statusText}`);
395
+ }
396
+ const data = await response.json();
397
+ return data;
398
+ } catch (error) {
399
+ console.error("Device authorization error:", error);
400
+ throw error;
401
+ }
402
+ }
403
+ async function pollForTokens(deviceCode, interval = 5, expiresIn = 600) {
404
+ const config = getConfig();
405
+ if (!config.clientId) {
406
+ throw new Error("Client ID is required for token polling");
407
+ }
408
+ const startTime = Date.now();
409
+ const timeout = expiresIn * 1e3;
410
+ let currentInterval = interval * 1e3;
411
+ while (true) {
412
+ if (Date.now() - startTime > timeout) {
413
+ throw new Error("Device authorization expired. Please try again.");
414
+ }
415
+ await new Promise((resolve) => setTimeout(resolve, currentInterval));
416
+ try {
417
+ const response = await config.fetch(`https://${config.authKitDomain}/device/token`, {
418
+ method: "POST",
419
+ headers: {
420
+ "Content-Type": "application/x-www-form-urlencoded"
421
+ },
422
+ body: new URLSearchParams({
423
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
424
+ device_code: deviceCode,
425
+ client_id: config.clientId
426
+ })
427
+ });
428
+ if (response.ok) {
429
+ const data = await response.json();
430
+ return data;
431
+ }
432
+ const errorData = await response.json().catch(() => ({ error: "unknown" }));
433
+ const error = errorData.error || "unknown";
434
+ switch (error) {
435
+ case "authorization_pending":
436
+ continue;
437
+ case "slow_down":
438
+ currentInterval += 5e3;
439
+ continue;
440
+ case "access_denied":
441
+ throw new Error("Access denied by user");
442
+ case "expired_token":
443
+ throw new Error("Device code expired");
444
+ default:
445
+ throw new Error(`Token polling failed: ${error}`);
446
+ }
447
+ } catch (error) {
448
+ if (error instanceof Error) {
449
+ throw error;
450
+ }
451
+ continue;
452
+ }
453
+ }
454
+ }
455
+
456
+ // src/auth.ts
457
+ function getEnv2(key) {
458
+ if (globalThis[key]) return globalThis[key];
459
+ if (typeof process !== "undefined" && process.env?.[key]) return process.env[key];
460
+ return void 0;
461
+ }
462
+ async function auth(token) {
463
+ const config = getConfig();
464
+ const authToken = token || getEnv2("DO_TOKEN") || "";
465
+ if (!authToken) {
466
+ return { user: null };
467
+ }
468
+ try {
469
+ const response = await config.fetch(`${config.apiUrl}/me`, {
470
+ method: "GET",
471
+ headers: {
472
+ "Authorization": `Bearer ${authToken}`,
473
+ "Content-Type": "application/json"
474
+ }
475
+ });
476
+ if (!response.ok) {
477
+ if (response.status === 401) {
478
+ return { user: null };
479
+ }
480
+ throw new Error(`Authentication failed: ${response.statusText}`);
481
+ }
482
+ const user = await response.json();
483
+ return { user, token: authToken };
484
+ } catch (error) {
485
+ console.error("Auth error:", error);
486
+ return { user: null };
487
+ }
488
+ }
489
+ async function logout(token) {
490
+ const config = getConfig();
491
+ const authToken = token || getEnv2("DO_TOKEN") || "";
492
+ if (!authToken) {
493
+ return;
494
+ }
495
+ try {
496
+ const response = await config.fetch(`${config.apiUrl}/logout`, {
497
+ method: "POST",
498
+ headers: {
499
+ "Authorization": `Bearer ${authToken}`,
500
+ "Content-Type": "application/json"
501
+ }
502
+ });
503
+ if (!response.ok) {
504
+ console.warn(`Logout warning: ${response.statusText}`);
505
+ }
506
+ } catch (error) {
507
+ console.error("Logout error:", error);
508
+ }
509
+ }
510
+
511
+ // src/cli.ts
512
+ init_storage();
513
+ var colors = {
514
+ reset: "\x1B[0m",
515
+ bright: "\x1B[1m",
516
+ dim: "\x1B[2m",
517
+ green: "\x1B[32m",
518
+ yellow: "\x1B[33m",
519
+ red: "\x1B[31m",
520
+ cyan: "\x1B[36m",
521
+ gray: "\x1B[90m",
522
+ blue: "\x1B[34m"
523
+ };
524
+ var storage = createSecureStorage();
525
+ function configureFromEnv() {
526
+ configure({
527
+ apiUrl: process.env.OAUTH_API_URL || process.env.API_URL || "https://apis.do",
528
+ clientId: process.env.OAUTH_CLIENT_ID || "oauth.do",
529
+ authKitDomain: process.env.OAUTH_AUTHKIT_DOMAIN || "login.oauth.do"
530
+ });
531
+ }
532
+ function printError(message, error) {
533
+ console.error(`${colors.red}Error:${colors.reset} ${message}`);
534
+ if (error && error.message) {
535
+ console.error(error.message);
536
+ }
537
+ if (error && error.stack && process.env.DEBUG) {
538
+ console.error(`
539
+ ${colors.dim}Stack trace:${colors.reset}`);
540
+ console.error(`${colors.dim}${error.stack}${colors.reset}`);
541
+ }
542
+ }
543
+ function printSuccess(message) {
544
+ console.log(`${colors.green}\u2713${colors.reset} ${message}`);
545
+ }
546
+ function printInfo(message) {
547
+ console.log(`${colors.cyan}\u2139${colors.reset} ${message}`);
548
+ }
549
+ function printHelp() {
550
+ console.log(`
551
+ ${colors.bright}OAuth.do CLI${colors.reset}
552
+
553
+ ${colors.cyan}Usage:${colors.reset}
554
+ oauth.do <command> [options]
555
+
556
+ ${colors.cyan}Commands:${colors.reset}
557
+ login Login using device authorization flow
558
+ logout Logout and remove stored credentials
559
+ whoami Show current authenticated user
560
+ token Display current authentication token
561
+ status Show authentication and storage status
562
+
563
+ ${colors.cyan}Options:${colors.reset}
564
+ --help, -h Show this help message
565
+ --version, -v Show version
566
+ --debug Show debug information
567
+
568
+ ${colors.cyan}Examples:${colors.reset}
569
+ ${colors.gray}# Login to your account${colors.reset}
570
+ oauth.do login
571
+
572
+ ${colors.gray}# Check who is logged in${colors.reset}
573
+ oauth.do whoami
574
+
575
+ ${colors.gray}# Get your authentication token${colors.reset}
576
+ oauth.do token
577
+
578
+ ${colors.gray}# Logout${colors.reset}
579
+ oauth.do logout
580
+
581
+ ${colors.cyan}Environment Variables:${colors.reset}
582
+ OAUTH_CLIENT_ID Client ID for OAuth (default: oauth.do)
583
+ OAUTH_AUTHKIT_DOMAIN AuthKit domain (default: login.oauth.do)
584
+ OAUTH_API_URL API base URL (default: https://apis.do)
585
+ DEBUG Enable debug output
586
+ `);
587
+ }
588
+ function printVersion() {
589
+ try {
590
+ Promise.resolve().then(() => __toESM(require_package(), 1)).then((pkg) => {
591
+ console.log(`oauth.do v${pkg.default.version}`);
592
+ });
593
+ } catch {
594
+ console.log("oauth.do");
595
+ }
596
+ }
597
+ async function loginCommand() {
598
+ try {
599
+ console.log(`${colors.bright}Starting OAuth login...${colors.reset}
600
+ `);
601
+ printInfo("Requesting device authorization...");
602
+ const authResponse = await authorizeDevice();
603
+ console.log(`
604
+ ${colors.bright}To complete login:${colors.reset}`);
605
+ console.log(`
606
+ 1. Visit: ${colors.cyan}${authResponse.verification_uri}${colors.reset}`);
607
+ console.log(` 2. Enter code: ${colors.bright}${colors.yellow}${authResponse.user_code}${colors.reset}`);
608
+ console.log(`
609
+ ${colors.dim}Or open this URL directly:${colors.reset}`);
610
+ console.log(` ${colors.blue}${authResponse.verification_uri_complete}${colors.reset}
611
+ `);
612
+ const open = await import('open').catch(() => null);
613
+ if (open) {
614
+ try {
615
+ await open.default(authResponse.verification_uri_complete);
616
+ printInfo("Opened browser for authentication");
617
+ } catch {
618
+ }
619
+ }
620
+ console.log(`${colors.dim}Waiting for authorization...${colors.reset}
621
+ `);
622
+ const tokenResponse = await pollForTokens(
623
+ authResponse.device_code,
624
+ authResponse.interval,
625
+ authResponse.expires_in
626
+ );
627
+ await storage.setToken(tokenResponse.access_token);
628
+ const authResult = await auth(tokenResponse.access_token);
629
+ printSuccess("Login successful!");
630
+ if (authResult.user) {
631
+ console.log(`
632
+ ${colors.dim}Logged in as:${colors.reset}`);
633
+ if (authResult.user.name) {
634
+ console.log(` ${colors.bright}${authResult.user.name}${colors.reset}`);
635
+ }
636
+ if (authResult.user.email) {
637
+ console.log(` ${colors.gray}${authResult.user.email}${colors.reset}`);
638
+ }
639
+ }
640
+ const compositeStorage = storage;
641
+ if (typeof compositeStorage.getStorageInfo === "function") {
642
+ const storageInfo = await compositeStorage.getStorageInfo();
643
+ const storageLabel = storageInfo.type === "keychain" ? `${colors.green}OS Keychain${colors.reset}` : `${colors.yellow}Secure File${colors.reset}`;
644
+ console.log(`
645
+ ${colors.dim}Token stored in: ${storageLabel}${colors.reset}`);
646
+ }
647
+ } catch (error) {
648
+ printError("Login failed", error instanceof Error ? error : void 0);
649
+ process.exit(1);
650
+ }
651
+ }
652
+ async function logoutCommand() {
653
+ try {
654
+ const token = await storage.getToken();
655
+ if (!token) {
656
+ printInfo("Not logged in");
657
+ return;
658
+ }
659
+ await logout(token);
660
+ await storage.removeToken();
661
+ printSuccess("Logged out successfully");
662
+ } catch (error) {
663
+ printError("Logout failed", error instanceof Error ? error : void 0);
664
+ process.exit(1);
665
+ }
666
+ }
667
+ async function whoamiCommand() {
668
+ try {
669
+ const token = await storage.getToken();
670
+ if (!token) {
671
+ console.log(`${colors.dim}Not logged in${colors.reset}`);
672
+ console.log(`
673
+ Run ${colors.cyan}oauth.do login${colors.reset} to authenticate`);
674
+ return;
675
+ }
676
+ const authResult = await auth(token);
677
+ if (!authResult.user) {
678
+ console.log(`${colors.dim}Not authenticated${colors.reset}`);
679
+ console.log(`
680
+ Run ${colors.cyan}oauth.do login${colors.reset} to authenticate`);
681
+ return;
682
+ }
683
+ console.log(`${colors.bright}Authenticated as:${colors.reset}`);
684
+ if (authResult.user.name) {
685
+ console.log(` ${colors.green}Name:${colors.reset} ${authResult.user.name}`);
686
+ }
687
+ if (authResult.user.email) {
688
+ console.log(` ${colors.green}Email:${colors.reset} ${authResult.user.email}`);
689
+ }
690
+ if (authResult.user.id) {
691
+ console.log(` ${colors.green}ID:${colors.reset} ${authResult.user.id}`);
692
+ }
693
+ } catch (error) {
694
+ printError("Failed to get user info", error instanceof Error ? error : void 0);
695
+ process.exit(1);
696
+ }
697
+ }
698
+ async function tokenCommand() {
699
+ try {
700
+ const token = await storage.getToken();
701
+ if (!token) {
702
+ console.log(`${colors.dim}No token found${colors.reset}`);
703
+ console.log(`
704
+ Run ${colors.cyan}oauth.do login${colors.reset} to authenticate`);
705
+ return;
706
+ }
707
+ console.log(token);
708
+ } catch (error) {
709
+ printError("Failed to get token", error instanceof Error ? error : void 0);
710
+ process.exit(1);
711
+ }
712
+ }
713
+ async function statusCommand() {
714
+ try {
715
+ console.log(`${colors.bright}OAuth.do Status${colors.reset}
716
+ `);
717
+ const compositeStorage = storage;
718
+ if (typeof compositeStorage.getStorageInfo === "function") {
719
+ const storageInfo = await compositeStorage.getStorageInfo();
720
+ const storageLabel = storageInfo.type === "keychain" ? `${colors.green}OS Keychain${colors.reset}` : `${colors.yellow}Secure File${colors.reset}`;
721
+ console.log(`${colors.cyan}Storage:${colors.reset} ${storageLabel}`);
722
+ if (storageInfo.type === "keychain") {
723
+ console.log(` ${colors.dim}Using system credential manager (most secure)${colors.reset}`);
724
+ } else {
725
+ console.log(` ${colors.dim}Using ~/.oauth.do/token with 0600 permissions${colors.reset}`);
726
+ }
727
+ }
728
+ const token = await storage.getToken();
729
+ if (!token) {
730
+ console.log(`
731
+ ${colors.cyan}Auth:${colors.reset} ${colors.dim}Not authenticated${colors.reset}`);
732
+ console.log(`
733
+ Run ${colors.cyan}oauth.do login${colors.reset} to authenticate`);
734
+ return;
735
+ }
736
+ const authResult = await auth(token);
737
+ if (authResult.user) {
738
+ console.log(`
739
+ ${colors.cyan}Auth:${colors.reset} ${colors.green}Authenticated${colors.reset}`);
740
+ if (authResult.user.email) {
741
+ console.log(` ${colors.dim}${authResult.user.email}${colors.reset}`);
742
+ }
743
+ } else {
744
+ console.log(`
745
+ ${colors.cyan}Auth:${colors.reset} ${colors.yellow}Token expired or invalid${colors.reset}`);
746
+ console.log(`
747
+ Run ${colors.cyan}oauth.do login${colors.reset} to re-authenticate`);
748
+ }
749
+ } catch (error) {
750
+ printError("Failed to get status", error instanceof Error ? error : void 0);
751
+ process.exit(1);
752
+ }
753
+ }
754
+ async function main() {
755
+ configureFromEnv();
756
+ const args = process.argv.slice(2);
757
+ if (args.includes("--help") || args.includes("-h")) {
758
+ printHelp();
759
+ process.exit(0);
760
+ }
761
+ if (args.includes("--version") || args.includes("-v")) {
762
+ printVersion();
763
+ process.exit(0);
764
+ }
765
+ if (args.includes("--debug")) {
766
+ process.env.DEBUG = "true";
767
+ }
768
+ const command = args.find((arg) => !arg.startsWith("--"));
769
+ switch (command) {
770
+ case "login":
771
+ case void 0:
772
+ await loginCommand();
773
+ break;
774
+ case "logout":
775
+ await logoutCommand();
776
+ break;
777
+ case "whoami":
778
+ await whoamiCommand();
779
+ break;
780
+ case "token":
781
+ await tokenCommand();
782
+ break;
783
+ case "status":
784
+ await statusCommand();
785
+ break;
786
+ default:
787
+ printError(`Unknown command: ${command}`);
788
+ console.log(`
789
+ Run ${colors.cyan}oauth.do --help${colors.reset} for usage information`);
790
+ process.exit(1);
791
+ }
792
+ }
793
+ main().catch((error) => {
794
+ printError("Unexpected error", error);
795
+ process.exit(1);
796
+ });
797
+
798
+ export { main };
799
+ //# sourceMappingURL=cli.js.map
800
+ //# sourceMappingURL=cli.js.map