gigadrive 2.3.0 → 2.4.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.cjs +232 -107
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +239 -114
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -32,7 +32,7 @@ var import_effect29 = require("effect");
|
|
|
32
32
|
// package.json
|
|
33
33
|
var package_default = {
|
|
34
34
|
name: "gigadrive",
|
|
35
|
-
version: "2.
|
|
35
|
+
version: "2.4.1",
|
|
36
36
|
repository: {
|
|
37
37
|
type: "git",
|
|
38
38
|
url: "https://github.com/Gigadrive/sdk.git",
|
|
@@ -58,7 +58,7 @@ var package_default = {
|
|
|
58
58
|
dependencies: {
|
|
59
59
|
"@effect/cli": "^0.75.0",
|
|
60
60
|
"@effect/platform": "^0.96.0",
|
|
61
|
-
"@effect/platform-node": "^0.
|
|
61
|
+
"@effect/platform-node": "^0.107.0",
|
|
62
62
|
"@gigadrive/build-utils": "workspace:*",
|
|
63
63
|
"@gigadrive/commons": "workspace:*",
|
|
64
64
|
"@gigadrive/network-config": "workspace:*",
|
|
@@ -69,8 +69,8 @@ var package_default = {
|
|
|
69
69
|
open: "^10.2.0"
|
|
70
70
|
},
|
|
71
71
|
devDependencies: {
|
|
72
|
-
"@effect/language-service": "^0.
|
|
73
|
-
"@types/archiver": "^6.0.
|
|
72
|
+
"@effect/language-service": "^0.86.0",
|
|
73
|
+
"@types/archiver": "^6.0.4",
|
|
74
74
|
esbuild: "^0.28.0",
|
|
75
75
|
typescript: "catalog:"
|
|
76
76
|
},
|
|
@@ -113,6 +113,25 @@ var TokenExchangeError = class extends import_effect.Schema.TaggedError()("Token
|
|
|
113
113
|
statusCode: import_effect.Schema.optional(import_effect.Schema.Number)
|
|
114
114
|
}) {
|
|
115
115
|
};
|
|
116
|
+
var DeviceAuthorizationError = class extends import_effect.Schema.TaggedError()(
|
|
117
|
+
"DeviceAuthorizationError",
|
|
118
|
+
{
|
|
119
|
+
message: import_effect.Schema.String,
|
|
120
|
+
cause: import_effect.Schema.optional(import_effect.Schema.String)
|
|
121
|
+
}
|
|
122
|
+
) {
|
|
123
|
+
};
|
|
124
|
+
var AuthorizationDeniedError = class extends import_effect.Schema.TaggedError()(
|
|
125
|
+
"AuthorizationDeniedError",
|
|
126
|
+
{
|
|
127
|
+
message: import_effect.Schema.String
|
|
128
|
+
}
|
|
129
|
+
) {
|
|
130
|
+
};
|
|
131
|
+
var DeviceCodeExpiredError = class extends import_effect.Schema.TaggedError()("DeviceCodeExpiredError", {
|
|
132
|
+
message: import_effect.Schema.String
|
|
133
|
+
}) {
|
|
134
|
+
};
|
|
116
135
|
var TokenRefreshError = class extends import_effect.Schema.TaggedError()("TokenRefreshError", {
|
|
117
136
|
message: import_effect.Schema.String,
|
|
118
137
|
statusCode: import_effect.Schema.optional(import_effect.Schema.Number)
|
|
@@ -310,15 +329,9 @@ var import_effect6 = require("effect");
|
|
|
310
329
|
|
|
311
330
|
// src/services/auth.ts
|
|
312
331
|
var import_effect5 = require("effect");
|
|
313
|
-
var
|
|
314
|
-
var http = __toESM(require("http"), 1);
|
|
332
|
+
var import_node_process2 = __toESM(require("process"), 1);
|
|
315
333
|
var import_open = __toESM(require("open"), 1);
|
|
316
334
|
|
|
317
|
-
// src/services/auth-storage.ts
|
|
318
|
-
var import_platform = require("@effect/platform");
|
|
319
|
-
var import_effect3 = require("effect");
|
|
320
|
-
var os = __toESM(require("os"), 1);
|
|
321
|
-
|
|
322
335
|
// src/domain.ts
|
|
323
336
|
var import_effect2 = require("effect");
|
|
324
337
|
var DeploymentId = import_effect2.Schema.String.pipe(import_effect2.Schema.brand("@Gigadrive/DeploymentId"));
|
|
@@ -336,9 +349,18 @@ var OAuthClientConfig = import_effect2.Schema.Struct({
|
|
|
336
349
|
issuer: import_effect2.Schema.String,
|
|
337
350
|
authorizeUrl: import_effect2.Schema.String,
|
|
338
351
|
tokenUrl: import_effect2.Schema.String,
|
|
352
|
+
deviceAuthorizeUrl: import_effect2.Schema.String,
|
|
339
353
|
scope: import_effect2.Schema.String,
|
|
340
354
|
userinfoUrl: import_effect2.Schema.String
|
|
341
355
|
});
|
|
356
|
+
var DeviceAuthorizationResponse = import_effect2.Schema.Struct({
|
|
357
|
+
device_code: import_effect2.Schema.String,
|
|
358
|
+
user_code: import_effect2.Schema.String,
|
|
359
|
+
verification_uri: import_effect2.Schema.String,
|
|
360
|
+
verification_uri_complete: import_effect2.Schema.optional(import_effect2.Schema.String),
|
|
361
|
+
expires_in: import_effect2.Schema.Number,
|
|
362
|
+
interval: import_effect2.Schema.optional(import_effect2.Schema.Number)
|
|
363
|
+
});
|
|
342
364
|
var StoredAuthData = import_effect2.Schema.Struct({
|
|
343
365
|
refreshToken: import_effect2.Schema.String,
|
|
344
366
|
accessToken: import_effect2.Schema.optional(import_effect2.Schema.String),
|
|
@@ -353,7 +375,47 @@ var DevKeyEntry = import_effect2.Schema.Struct({
|
|
|
353
375
|
});
|
|
354
376
|
var DevKeyStore = import_effect2.Schema.Record({ key: import_effect2.Schema.String, value: DevKeyEntry });
|
|
355
377
|
|
|
378
|
+
// src/lib/clipboard.ts
|
|
379
|
+
var import_node_child_process = require("child_process");
|
|
380
|
+
var import_node_process = __toESM(require("process"), 1);
|
|
381
|
+
var clipboardCandidates = () => {
|
|
382
|
+
switch (import_node_process.default.platform) {
|
|
383
|
+
case "darwin":
|
|
384
|
+
return [{ cmd: "pbcopy", args: [] }];
|
|
385
|
+
case "win32":
|
|
386
|
+
return [{ cmd: "clip", args: [] }];
|
|
387
|
+
default:
|
|
388
|
+
return [
|
|
389
|
+
{ cmd: "wl-copy", args: [] },
|
|
390
|
+
{ cmd: "xclip", args: ["-selection", "clipboard"] },
|
|
391
|
+
{ cmd: "xsel", args: ["--clipboard", "--input"] }
|
|
392
|
+
];
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
function writeClipboard(text) {
|
|
396
|
+
const candidates = clipboardCandidates();
|
|
397
|
+
const tryNext = (index) => {
|
|
398
|
+
if (index >= candidates.length) return;
|
|
399
|
+
const { cmd, args } = candidates[index];
|
|
400
|
+
let proc;
|
|
401
|
+
try {
|
|
402
|
+
proc = (0, import_node_child_process.spawn)(cmd, args, { stdio: ["pipe", "ignore", "ignore"] });
|
|
403
|
+
} catch {
|
|
404
|
+
tryNext(index + 1);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
proc.on("error", () => tryNext(index + 1));
|
|
408
|
+
proc.stdin?.on("error", () => {
|
|
409
|
+
});
|
|
410
|
+
proc.stdin?.end(text);
|
|
411
|
+
};
|
|
412
|
+
tryNext(0);
|
|
413
|
+
}
|
|
414
|
+
|
|
356
415
|
// src/services/auth-storage.ts
|
|
416
|
+
var import_platform = require("@effect/platform");
|
|
417
|
+
var import_effect3 = require("effect");
|
|
418
|
+
var os = __toESM(require("os"), 1);
|
|
357
419
|
var AuthStorageService = class extends import_effect3.Effect.Service()("AuthStorageService", {
|
|
358
420
|
accessors: true,
|
|
359
421
|
effect: import_effect3.Effect.gen(function* () {
|
|
@@ -442,7 +504,8 @@ var OpenIdDiscoveryDocument = import_effect4.Schema.Struct({
|
|
|
442
504
|
issuer: import_effect4.Schema.optional(import_effect4.Schema.String),
|
|
443
505
|
authorization_endpoint: import_effect4.Schema.optional(import_effect4.Schema.String),
|
|
444
506
|
token_endpoint: import_effect4.Schema.optional(import_effect4.Schema.String),
|
|
445
|
-
userinfo_endpoint: import_effect4.Schema.optional(import_effect4.Schema.String)
|
|
507
|
+
userinfo_endpoint: import_effect4.Schema.optional(import_effect4.Schema.String),
|
|
508
|
+
device_authorization_endpoint: import_effect4.Schema.optional(import_effect4.Schema.String)
|
|
446
509
|
});
|
|
447
510
|
var CLI_SCOPES = [
|
|
448
511
|
// Identity / OIDC
|
|
@@ -508,7 +571,7 @@ var OAuthConfigService = class extends import_effect4.Effect.Service()("OAuthCon
|
|
|
508
571
|
})
|
|
509
572
|
)
|
|
510
573
|
);
|
|
511
|
-
if (!decoded.authorization_endpoint || !decoded.token_endpoint || !decoded.userinfo_endpoint) {
|
|
574
|
+
if (!decoded.authorization_endpoint || !decoded.token_endpoint || !decoded.userinfo_endpoint || !decoded.device_authorization_endpoint) {
|
|
512
575
|
return yield* import_effect4.Effect.fail(
|
|
513
576
|
new OAuthDiscoveryError({
|
|
514
577
|
message: "OIDC discovery returned incomplete endpoints"
|
|
@@ -520,6 +583,7 @@ var OAuthConfigService = class extends import_effect4.Effect.Service()("OAuthCon
|
|
|
520
583
|
issuer: decoded.issuer ?? issuerBase,
|
|
521
584
|
authorizeUrl: decoded.authorization_endpoint,
|
|
522
585
|
tokenUrl: decoded.token_endpoint,
|
|
586
|
+
deviceAuthorizeUrl: decoded.device_authorization_endpoint,
|
|
523
587
|
scope: DEFAULT_SCOPE,
|
|
524
588
|
userinfoUrl: decoded.userinfo_endpoint
|
|
525
589
|
};
|
|
@@ -533,14 +597,9 @@ var OAuthConfigService = class extends import_effect4.Effect.Service()("OAuthCon
|
|
|
533
597
|
};
|
|
534
598
|
|
|
535
599
|
// src/services/auth.ts
|
|
536
|
-
var
|
|
537
|
-
var
|
|
538
|
-
var
|
|
539
|
-
const codeVerifier = base64URLEncode(crypto.randomBytes(64));
|
|
540
|
-
const challengeBytes = crypto.createHash("sha256").update(codeVerifier, "utf8").digest();
|
|
541
|
-
const codeChallenge = base64URLEncode(challengeBytes);
|
|
542
|
-
return { codeVerifier, codeChallenge };
|
|
543
|
-
};
|
|
600
|
+
var DEVICE_CODE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
601
|
+
var DEFAULT_POLL_INTERVAL_SECONDS = 5;
|
|
602
|
+
var REQUEST_TIMEOUT_SECONDS = 30;
|
|
544
603
|
var AuthService = class extends import_effect5.Effect.Service()("AuthService", {
|
|
545
604
|
accessors: true,
|
|
546
605
|
dependencies: [OAuthConfigService.Default, AuthStorageService.Default],
|
|
@@ -555,115 +614,178 @@ var AuthService = class extends import_effect5.Effect.Service()("AuthService", {
|
|
|
555
614
|
yield* storage.remove;
|
|
556
615
|
yield* import_effect5.Effect.log("Logged out");
|
|
557
616
|
});
|
|
558
|
-
const
|
|
617
|
+
const requestDeviceAuthorization = (config) => import_effect5.Effect.gen(function* () {
|
|
559
618
|
const response = yield* import_effect5.Effect.tryPromise({
|
|
560
|
-
try: () => fetch(config.
|
|
619
|
+
try: (signal) => fetch(config.deviceAuthorizeUrl, {
|
|
561
620
|
method: "POST",
|
|
562
621
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
563
|
-
body: new URLSearchParams({
|
|
564
|
-
|
|
565
|
-
client_id: config.clientId,
|
|
566
|
-
code: authCode,
|
|
567
|
-
redirect_uri: redirectUri,
|
|
568
|
-
code_verifier: codeVerifier
|
|
569
|
-
}).toString()
|
|
622
|
+
body: new URLSearchParams({ client_id: config.clientId, scope: config.scope }).toString(),
|
|
623
|
+
signal
|
|
570
624
|
}),
|
|
571
|
-
catch: (error) => new
|
|
572
|
-
message: `
|
|
625
|
+
catch: (error) => new DeviceAuthorizationError({
|
|
626
|
+
message: `Device authorization request failed: ${error instanceof Error ? error.message : String(error)}`
|
|
573
627
|
})
|
|
574
628
|
});
|
|
575
629
|
if (!response.ok) {
|
|
576
630
|
const body = yield* import_effect5.Effect.tryPromise({
|
|
577
631
|
try: () => response.text(),
|
|
578
|
-
catch: () => new
|
|
632
|
+
catch: () => new DeviceAuthorizationError({ message: "Failed to read device authorization error response" })
|
|
579
633
|
});
|
|
580
634
|
return yield* import_effect5.Effect.fail(
|
|
581
|
-
new
|
|
582
|
-
message: `
|
|
583
|
-
statusCode: response.status
|
|
635
|
+
new DeviceAuthorizationError({
|
|
636
|
+
message: `Device authorization failed: ${response.status} ${response.statusText} - ${body}`
|
|
584
637
|
})
|
|
585
638
|
);
|
|
586
639
|
}
|
|
587
|
-
const
|
|
640
|
+
const json = yield* import_effect5.Effect.tryPromise({
|
|
641
|
+
try: () => response.json(),
|
|
642
|
+
catch: () => new DeviceAuthorizationError({ message: "Failed to parse device authorization response" })
|
|
643
|
+
});
|
|
644
|
+
return yield* import_effect5.Schema.decodeUnknown(DeviceAuthorizationResponse)(json).pipe(
|
|
645
|
+
import_effect5.Effect.mapError(
|
|
646
|
+
() => new DeviceAuthorizationError({ message: "Invalid device authorization response schema" })
|
|
647
|
+
)
|
|
648
|
+
);
|
|
649
|
+
}).pipe(
|
|
650
|
+
import_effect5.Effect.timeoutFail({
|
|
651
|
+
duration: import_effect5.Duration.seconds(REQUEST_TIMEOUT_SECONDS),
|
|
652
|
+
onTimeout: () => new DeviceAuthorizationError({ message: "Device authorization request timed out" })
|
|
653
|
+
})
|
|
654
|
+
);
|
|
655
|
+
const pollTokenOnce = (config, deviceCode) => import_effect5.Effect.gen(function* () {
|
|
656
|
+
const response = yield* import_effect5.Effect.tryPromise({
|
|
657
|
+
try: (signal) => fetch(config.tokenUrl, {
|
|
658
|
+
method: "POST",
|
|
659
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
660
|
+
body: new URLSearchParams({
|
|
661
|
+
grant_type: DEVICE_CODE_GRANT_TYPE,
|
|
662
|
+
client_id: config.clientId,
|
|
663
|
+
device_code: deviceCode
|
|
664
|
+
}).toString(),
|
|
665
|
+
signal
|
|
666
|
+
}),
|
|
667
|
+
catch: (error) => new TokenExchangeError({
|
|
668
|
+
message: `Token poll request failed: ${error instanceof Error ? error.message : String(error)}`
|
|
669
|
+
})
|
|
670
|
+
});
|
|
671
|
+
if (response.ok) {
|
|
672
|
+
const tokens = yield* import_effect5.Effect.tryPromise({
|
|
673
|
+
try: () => response.json(),
|
|
674
|
+
catch: () => new TokenExchangeError({ message: "Failed to parse token response" })
|
|
675
|
+
});
|
|
676
|
+
return { kind: "tokens", tokens };
|
|
677
|
+
}
|
|
678
|
+
const errorBody = yield* import_effect5.Effect.tryPromise({
|
|
588
679
|
try: () => response.json(),
|
|
589
|
-
catch: () => new TokenExchangeError({ message: "Failed to parse token
|
|
680
|
+
catch: () => new TokenExchangeError({ message: "Failed to parse token poll error response" })
|
|
590
681
|
});
|
|
591
|
-
|
|
682
|
+
switch (errorBody.error) {
|
|
683
|
+
case "authorization_pending":
|
|
684
|
+
return { kind: "pending" };
|
|
685
|
+
case "slow_down":
|
|
686
|
+
return { kind: "slow_down" };
|
|
687
|
+
case "access_denied":
|
|
688
|
+
return { kind: "denied" };
|
|
689
|
+
case "expired_token":
|
|
690
|
+
return { kind: "expired" };
|
|
691
|
+
default:
|
|
692
|
+
return yield* import_effect5.Effect.fail(
|
|
693
|
+
new TokenExchangeError({
|
|
694
|
+
message: `Token poll failed: ${response.status} ${response.statusText} - ${errorBody.error ?? "unknown_error"}`,
|
|
695
|
+
statusCode: response.status
|
|
696
|
+
})
|
|
697
|
+
);
|
|
698
|
+
}
|
|
592
699
|
});
|
|
593
|
-
const
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
if (
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
700
|
+
const pollForToken = (config, device) => import_effect5.Effect.gen(function* () {
|
|
701
|
+
const deadline = Date.now() + device.expires_in * 1e3;
|
|
702
|
+
let interval = device.interval ?? DEFAULT_POLL_INTERVAL_SECONDS;
|
|
703
|
+
while (true) {
|
|
704
|
+
yield* import_effect5.Effect.sleep(import_effect5.Duration.seconds(interval));
|
|
705
|
+
if (Date.now() >= deadline) {
|
|
706
|
+
return yield* import_effect5.Effect.fail(
|
|
707
|
+
new DeviceCodeExpiredError({
|
|
708
|
+
message: "The login code expired before it was approved. Please run `gigadrive login` again."
|
|
709
|
+
})
|
|
710
|
+
);
|
|
603
711
|
}
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
res.writeHead(200, { "Content-Type": "text/html", Connection: "close" });
|
|
629
|
-
if (oauthError) {
|
|
630
|
-
const safeOauthError = escapeHtml(oauthError);
|
|
631
|
-
res.end(`<h1>Login Failed!</h1><p>Error: ${safeOauthError}</p><p>You can close this tab.</p>`);
|
|
632
|
-
server.close();
|
|
633
|
-
resume(import_effect5.Effect.fail(new LoginFlowError({ message: `OAuth error: ${oauthError}` })));
|
|
634
|
-
return;
|
|
635
|
-
}
|
|
636
|
-
if (receivedState !== state) {
|
|
637
|
-
res.end(
|
|
638
|
-
"<h1>Login Failed!</h1><p>State mismatch. Possible CSRF attack.</p><p>You can close this tab.</p>"
|
|
712
|
+
const result = yield* pollTokenOnce(config, device.device_code).pipe(
|
|
713
|
+
// A hung poll must not stall the loop: on timeout treat it as pending so
|
|
714
|
+
// the next iteration re-checks the expiry deadline.
|
|
715
|
+
import_effect5.Effect.timeoutTo({
|
|
716
|
+
duration: import_effect5.Duration.seconds(REQUEST_TIMEOUT_SECONDS),
|
|
717
|
+
onSuccess: (value) => value,
|
|
718
|
+
onTimeout: () => ({ kind: "pending" })
|
|
719
|
+
})
|
|
720
|
+
);
|
|
721
|
+
switch (result.kind) {
|
|
722
|
+
case "tokens":
|
|
723
|
+
return result.tokens;
|
|
724
|
+
case "pending":
|
|
725
|
+
continue;
|
|
726
|
+
case "slow_down":
|
|
727
|
+
interval += 5;
|
|
728
|
+
continue;
|
|
729
|
+
case "denied":
|
|
730
|
+
return yield* import_effect5.Effect.fail(new AuthorizationDeniedError({ message: "The login request was denied." }));
|
|
731
|
+
case "expired":
|
|
732
|
+
return yield* import_effect5.Effect.fail(
|
|
733
|
+
new DeviceCodeExpiredError({
|
|
734
|
+
message: "The login code expired before it was approved. Please run `gigadrive login` again."
|
|
735
|
+
})
|
|
639
736
|
);
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
resume(import_effect5.Effect.fail(new LoginFlowError({ message:
|
|
657
|
-
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
const watchCopyKey = (url) => import_effect5.Effect.async((resume) => {
|
|
741
|
+
const stdin = import_node_process2.default.stdin;
|
|
742
|
+
if (!stdin.isTTY) {
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
stdin.setRawMode(true);
|
|
746
|
+
stdin.resume();
|
|
747
|
+
stdin.setEncoding("utf8");
|
|
748
|
+
const onData = (key) => {
|
|
749
|
+
if (key === "c" || key === "C") {
|
|
750
|
+
writeClipboard(url);
|
|
751
|
+
import_effect5.Console.log("Copied the login URL to your clipboard.").pipe(import_effect5.Effect.runSync);
|
|
752
|
+
} else if (key === "") {
|
|
753
|
+
resume(import_effect5.Effect.fail(new LoginFlowError({ message: "Login canceled." })));
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
stdin.on("data", onData);
|
|
757
|
+
return import_effect5.Effect.sync(() => {
|
|
758
|
+
stdin.off("data", onData);
|
|
759
|
+
if (stdin.isTTY) stdin.setRawMode(false);
|
|
760
|
+
stdin.pause();
|
|
658
761
|
});
|
|
659
762
|
});
|
|
660
763
|
const login = import_effect5.Effect.gen(function* () {
|
|
661
764
|
yield* logout;
|
|
662
|
-
const { codeVerifier, codeChallenge } = generatePKCE();
|
|
663
|
-
const state = crypto.randomBytes(16).toString("hex");
|
|
664
765
|
const config = yield* oauthConfig.getConfig;
|
|
665
|
-
const
|
|
666
|
-
const
|
|
766
|
+
const device = yield* requestDeviceAuthorization(config);
|
|
767
|
+
const verificationUri = device.verification_uri;
|
|
768
|
+
const verificationUriComplete = device.verification_uri_complete ?? verificationUri;
|
|
769
|
+
const interactive = import_node_process2.default.stdin.isTTY === true;
|
|
770
|
+
yield* import_effect5.Console.log(
|
|
771
|
+
`
|
|
772
|
+
To sign in, visit:
|
|
773
|
+
|
|
774
|
+
${verificationUri}
|
|
775
|
+
|
|
776
|
+
and enter the code:
|
|
777
|
+
|
|
778
|
+
${device.user_code}
|
|
779
|
+
`
|
|
780
|
+
);
|
|
781
|
+
if (interactive) {
|
|
782
|
+
yield* import_effect5.Effect.tryPromise(() => (0, import_open.default)(verificationUriComplete)).pipe(import_effect5.Effect.catchAll(() => import_effect5.Effect.void));
|
|
783
|
+
yield* import_effect5.Console.log("We tried to open your browser automatically. If it did not open, use the URL above.");
|
|
784
|
+
yield* import_effect5.Console.log('Press "c" to copy the URL to your clipboard, or Ctrl+C to cancel.');
|
|
785
|
+
}
|
|
786
|
+
yield* import_effect5.Console.log("\nWaiting for you to approve the login\u2026");
|
|
787
|
+
const poll = pollForToken(config, device);
|
|
788
|
+
const tokens = yield* interactive ? poll.pipe(import_effect5.Effect.raceFirst(watchCopyKey(verificationUriComplete))) : poll;
|
|
667
789
|
if (!tokens.refresh_token) {
|
|
668
790
|
return yield* import_effect5.Effect.fail(
|
|
669
791
|
new TokenExchangeError({ message: "No refresh token received from identity provider" })
|
|
@@ -2160,7 +2282,10 @@ var loginCommand = import_cli9.Command.make(
|
|
|
2160
2282
|
yield* import_effect21.Console.log(`You are now logged in as ${name}.`);
|
|
2161
2283
|
}).pipe(
|
|
2162
2284
|
import_effect21.Effect.catchTags({
|
|
2163
|
-
LoginFlowError: (err) => import_effect21.Console.error(
|
|
2285
|
+
LoginFlowError: (err) => import_effect21.Console.error(err.message),
|
|
2286
|
+
DeviceAuthorizationError: (err) => import_effect21.Console.error(`Login failed: ${err.message}`),
|
|
2287
|
+
AuthorizationDeniedError: (err) => import_effect21.Console.error(err.message),
|
|
2288
|
+
DeviceCodeExpiredError: (err) => import_effect21.Console.error(err.message),
|
|
2164
2289
|
TokenExchangeError: (err) => import_effect21.Console.error(`Login failed: ${err.message}`),
|
|
2165
2290
|
OAuthDiscoveryError: (err) => import_effect21.Console.error(`Login failed: ${err.message}`),
|
|
2166
2291
|
AuthStorageWriteError: (err) => import_effect21.Console.error(`Failed to save credentials: ${err.message}`)
|