gigadrive 2.3.0 → 2.4.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/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.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import { Effect as Effect27, Layer, LogLevel, Logger } from "effect";
|
|
|
9
9
|
// package.json
|
|
10
10
|
var package_default = {
|
|
11
11
|
name: "gigadrive",
|
|
12
|
-
version: "2.
|
|
12
|
+
version: "2.4.0",
|
|
13
13
|
repository: {
|
|
14
14
|
type: "git",
|
|
15
15
|
url: "https://github.com/Gigadrive/sdk.git",
|
|
@@ -35,7 +35,7 @@ var package_default = {
|
|
|
35
35
|
dependencies: {
|
|
36
36
|
"@effect/cli": "^0.75.0",
|
|
37
37
|
"@effect/platform": "^0.96.0",
|
|
38
|
-
"@effect/platform-node": "^0.
|
|
38
|
+
"@effect/platform-node": "^0.107.0",
|
|
39
39
|
"@gigadrive/build-utils": "workspace:*",
|
|
40
40
|
"@gigadrive/commons": "workspace:*",
|
|
41
41
|
"@gigadrive/network-config": "workspace:*",
|
|
@@ -46,8 +46,8 @@ var package_default = {
|
|
|
46
46
|
open: "^10.2.0"
|
|
47
47
|
},
|
|
48
48
|
devDependencies: {
|
|
49
|
-
"@effect/language-service": "^0.
|
|
50
|
-
"@types/archiver": "^6.0.
|
|
49
|
+
"@effect/language-service": "^0.86.0",
|
|
50
|
+
"@types/archiver": "^6.0.4",
|
|
51
51
|
esbuild: "^0.28.0",
|
|
52
52
|
typescript: "catalog:"
|
|
53
53
|
},
|
|
@@ -90,6 +90,25 @@ var TokenExchangeError = class extends Schema.TaggedError()("TokenExchangeError"
|
|
|
90
90
|
statusCode: Schema.optional(Schema.Number)
|
|
91
91
|
}) {
|
|
92
92
|
};
|
|
93
|
+
var DeviceAuthorizationError = class extends Schema.TaggedError()(
|
|
94
|
+
"DeviceAuthorizationError",
|
|
95
|
+
{
|
|
96
|
+
message: Schema.String,
|
|
97
|
+
cause: Schema.optional(Schema.String)
|
|
98
|
+
}
|
|
99
|
+
) {
|
|
100
|
+
};
|
|
101
|
+
var AuthorizationDeniedError = class extends Schema.TaggedError()(
|
|
102
|
+
"AuthorizationDeniedError",
|
|
103
|
+
{
|
|
104
|
+
message: Schema.String
|
|
105
|
+
}
|
|
106
|
+
) {
|
|
107
|
+
};
|
|
108
|
+
var DeviceCodeExpiredError = class extends Schema.TaggedError()("DeviceCodeExpiredError", {
|
|
109
|
+
message: Schema.String
|
|
110
|
+
}) {
|
|
111
|
+
};
|
|
93
112
|
var TokenRefreshError = class extends Schema.TaggedError()("TokenRefreshError", {
|
|
94
113
|
message: Schema.String,
|
|
95
114
|
statusCode: Schema.optional(Schema.Number)
|
|
@@ -286,16 +305,10 @@ import { ApiError, GigadriveClient } from "@gigadrive/sdk";
|
|
|
286
305
|
import { Config as Config2, Effect as Effect4 } from "effect";
|
|
287
306
|
|
|
288
307
|
// src/services/auth.ts
|
|
289
|
-
import { Console, Effect as Effect3, Option as Option2 } from "effect";
|
|
290
|
-
import
|
|
291
|
-
import * as http from "http";
|
|
308
|
+
import { Console, Duration, Effect as Effect3, Option as Option2, Schema as Schema5 } from "effect";
|
|
309
|
+
import process3 from "process";
|
|
292
310
|
import open from "open";
|
|
293
311
|
|
|
294
|
-
// src/services/auth-storage.ts
|
|
295
|
-
import { FileSystem, Path } from "@effect/platform";
|
|
296
|
-
import { Effect, Option, Schema as Schema3 } from "effect";
|
|
297
|
-
import * as os from "os";
|
|
298
|
-
|
|
299
312
|
// src/domain.ts
|
|
300
313
|
import { Schema as Schema2 } from "effect";
|
|
301
314
|
var DeploymentId = Schema2.String.pipe(Schema2.brand("@Gigadrive/DeploymentId"));
|
|
@@ -313,9 +326,18 @@ var OAuthClientConfig = Schema2.Struct({
|
|
|
313
326
|
issuer: Schema2.String,
|
|
314
327
|
authorizeUrl: Schema2.String,
|
|
315
328
|
tokenUrl: Schema2.String,
|
|
329
|
+
deviceAuthorizeUrl: Schema2.String,
|
|
316
330
|
scope: Schema2.String,
|
|
317
331
|
userinfoUrl: Schema2.String
|
|
318
332
|
});
|
|
333
|
+
var DeviceAuthorizationResponse = Schema2.Struct({
|
|
334
|
+
device_code: Schema2.String,
|
|
335
|
+
user_code: Schema2.String,
|
|
336
|
+
verification_uri: Schema2.String,
|
|
337
|
+
verification_uri_complete: Schema2.optional(Schema2.String),
|
|
338
|
+
expires_in: Schema2.Number,
|
|
339
|
+
interval: Schema2.optional(Schema2.Number)
|
|
340
|
+
});
|
|
319
341
|
var StoredAuthData = Schema2.Struct({
|
|
320
342
|
refreshToken: Schema2.String,
|
|
321
343
|
accessToken: Schema2.optional(Schema2.String),
|
|
@@ -330,7 +352,47 @@ var DevKeyEntry = Schema2.Struct({
|
|
|
330
352
|
});
|
|
331
353
|
var DevKeyStore = Schema2.Record({ key: Schema2.String, value: DevKeyEntry });
|
|
332
354
|
|
|
355
|
+
// src/lib/clipboard.ts
|
|
356
|
+
import { spawn } from "child_process";
|
|
357
|
+
import process2 from "process";
|
|
358
|
+
var clipboardCandidates = () => {
|
|
359
|
+
switch (process2.platform) {
|
|
360
|
+
case "darwin":
|
|
361
|
+
return [{ cmd: "pbcopy", args: [] }];
|
|
362
|
+
case "win32":
|
|
363
|
+
return [{ cmd: "clip", args: [] }];
|
|
364
|
+
default:
|
|
365
|
+
return [
|
|
366
|
+
{ cmd: "wl-copy", args: [] },
|
|
367
|
+
{ cmd: "xclip", args: ["-selection", "clipboard"] },
|
|
368
|
+
{ cmd: "xsel", args: ["--clipboard", "--input"] }
|
|
369
|
+
];
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
function writeClipboard(text) {
|
|
373
|
+
const candidates = clipboardCandidates();
|
|
374
|
+
const tryNext = (index) => {
|
|
375
|
+
if (index >= candidates.length) return;
|
|
376
|
+
const { cmd, args } = candidates[index];
|
|
377
|
+
let proc;
|
|
378
|
+
try {
|
|
379
|
+
proc = spawn(cmd, args, { stdio: ["pipe", "ignore", "ignore"] });
|
|
380
|
+
} catch {
|
|
381
|
+
tryNext(index + 1);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
proc.on("error", () => tryNext(index + 1));
|
|
385
|
+
proc.stdin?.on("error", () => {
|
|
386
|
+
});
|
|
387
|
+
proc.stdin?.end(text);
|
|
388
|
+
};
|
|
389
|
+
tryNext(0);
|
|
390
|
+
}
|
|
391
|
+
|
|
333
392
|
// src/services/auth-storage.ts
|
|
393
|
+
import { FileSystem, Path } from "@effect/platform";
|
|
394
|
+
import { Effect, Option, Schema as Schema3 } from "effect";
|
|
395
|
+
import * as os from "os";
|
|
334
396
|
var AuthStorageService = class extends Effect.Service()("AuthStorageService", {
|
|
335
397
|
accessors: true,
|
|
336
398
|
effect: Effect.gen(function* () {
|
|
@@ -419,7 +481,8 @@ var OpenIdDiscoveryDocument = Schema4.Struct({
|
|
|
419
481
|
issuer: Schema4.optional(Schema4.String),
|
|
420
482
|
authorization_endpoint: Schema4.optional(Schema4.String),
|
|
421
483
|
token_endpoint: Schema4.optional(Schema4.String),
|
|
422
|
-
userinfo_endpoint: Schema4.optional(Schema4.String)
|
|
484
|
+
userinfo_endpoint: Schema4.optional(Schema4.String),
|
|
485
|
+
device_authorization_endpoint: Schema4.optional(Schema4.String)
|
|
423
486
|
});
|
|
424
487
|
var CLI_SCOPES = [
|
|
425
488
|
// Identity / OIDC
|
|
@@ -485,7 +548,7 @@ var OAuthConfigService = class extends Effect2.Service()("OAuthConfigService", {
|
|
|
485
548
|
})
|
|
486
549
|
)
|
|
487
550
|
);
|
|
488
|
-
if (!decoded.authorization_endpoint || !decoded.token_endpoint || !decoded.userinfo_endpoint) {
|
|
551
|
+
if (!decoded.authorization_endpoint || !decoded.token_endpoint || !decoded.userinfo_endpoint || !decoded.device_authorization_endpoint) {
|
|
489
552
|
return yield* Effect2.fail(
|
|
490
553
|
new OAuthDiscoveryError({
|
|
491
554
|
message: "OIDC discovery returned incomplete endpoints"
|
|
@@ -497,6 +560,7 @@ var OAuthConfigService = class extends Effect2.Service()("OAuthConfigService", {
|
|
|
497
560
|
issuer: decoded.issuer ?? issuerBase,
|
|
498
561
|
authorizeUrl: decoded.authorization_endpoint,
|
|
499
562
|
tokenUrl: decoded.token_endpoint,
|
|
563
|
+
deviceAuthorizeUrl: decoded.device_authorization_endpoint,
|
|
500
564
|
scope: DEFAULT_SCOPE,
|
|
501
565
|
userinfoUrl: decoded.userinfo_endpoint
|
|
502
566
|
};
|
|
@@ -510,14 +574,9 @@ var OAuthConfigService = class extends Effect2.Service()("OAuthConfigService", {
|
|
|
510
574
|
};
|
|
511
575
|
|
|
512
576
|
// src/services/auth.ts
|
|
513
|
-
var
|
|
514
|
-
var
|
|
515
|
-
var
|
|
516
|
-
const codeVerifier = base64URLEncode(crypto.randomBytes(64));
|
|
517
|
-
const challengeBytes = crypto.createHash("sha256").update(codeVerifier, "utf8").digest();
|
|
518
|
-
const codeChallenge = base64URLEncode(challengeBytes);
|
|
519
|
-
return { codeVerifier, codeChallenge };
|
|
520
|
-
};
|
|
577
|
+
var DEVICE_CODE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
578
|
+
var DEFAULT_POLL_INTERVAL_SECONDS = 5;
|
|
579
|
+
var REQUEST_TIMEOUT_SECONDS = 30;
|
|
521
580
|
var AuthService = class extends Effect3.Service()("AuthService", {
|
|
522
581
|
accessors: true,
|
|
523
582
|
dependencies: [OAuthConfigService.Default, AuthStorageService.Default],
|
|
@@ -532,115 +591,178 @@ var AuthService = class extends Effect3.Service()("AuthService", {
|
|
|
532
591
|
yield* storage.remove;
|
|
533
592
|
yield* Effect3.log("Logged out");
|
|
534
593
|
});
|
|
535
|
-
const
|
|
594
|
+
const requestDeviceAuthorization = (config) => Effect3.gen(function* () {
|
|
536
595
|
const response = yield* Effect3.tryPromise({
|
|
537
|
-
try: () => fetch(config.
|
|
596
|
+
try: (signal) => fetch(config.deviceAuthorizeUrl, {
|
|
538
597
|
method: "POST",
|
|
539
598
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
540
|
-
body: new URLSearchParams({
|
|
541
|
-
|
|
542
|
-
client_id: config.clientId,
|
|
543
|
-
code: authCode,
|
|
544
|
-
redirect_uri: redirectUri,
|
|
545
|
-
code_verifier: codeVerifier
|
|
546
|
-
}).toString()
|
|
599
|
+
body: new URLSearchParams({ client_id: config.clientId, scope: config.scope }).toString(),
|
|
600
|
+
signal
|
|
547
601
|
}),
|
|
548
|
-
catch: (error) => new
|
|
549
|
-
message: `
|
|
602
|
+
catch: (error) => new DeviceAuthorizationError({
|
|
603
|
+
message: `Device authorization request failed: ${error instanceof Error ? error.message : String(error)}`
|
|
550
604
|
})
|
|
551
605
|
});
|
|
552
606
|
if (!response.ok) {
|
|
553
607
|
const body = yield* Effect3.tryPromise({
|
|
554
608
|
try: () => response.text(),
|
|
555
|
-
catch: () => new
|
|
609
|
+
catch: () => new DeviceAuthorizationError({ message: "Failed to read device authorization error response" })
|
|
556
610
|
});
|
|
557
611
|
return yield* Effect3.fail(
|
|
558
|
-
new
|
|
559
|
-
message: `
|
|
560
|
-
statusCode: response.status
|
|
612
|
+
new DeviceAuthorizationError({
|
|
613
|
+
message: `Device authorization failed: ${response.status} ${response.statusText} - ${body}`
|
|
561
614
|
})
|
|
562
615
|
);
|
|
563
616
|
}
|
|
564
|
-
const
|
|
617
|
+
const json = yield* Effect3.tryPromise({
|
|
618
|
+
try: () => response.json(),
|
|
619
|
+
catch: () => new DeviceAuthorizationError({ message: "Failed to parse device authorization response" })
|
|
620
|
+
});
|
|
621
|
+
return yield* Schema5.decodeUnknown(DeviceAuthorizationResponse)(json).pipe(
|
|
622
|
+
Effect3.mapError(
|
|
623
|
+
() => new DeviceAuthorizationError({ message: "Invalid device authorization response schema" })
|
|
624
|
+
)
|
|
625
|
+
);
|
|
626
|
+
}).pipe(
|
|
627
|
+
Effect3.timeoutFail({
|
|
628
|
+
duration: Duration.seconds(REQUEST_TIMEOUT_SECONDS),
|
|
629
|
+
onTimeout: () => new DeviceAuthorizationError({ message: "Device authorization request timed out" })
|
|
630
|
+
})
|
|
631
|
+
);
|
|
632
|
+
const pollTokenOnce = (config, deviceCode) => Effect3.gen(function* () {
|
|
633
|
+
const response = yield* Effect3.tryPromise({
|
|
634
|
+
try: (signal) => fetch(config.tokenUrl, {
|
|
635
|
+
method: "POST",
|
|
636
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
637
|
+
body: new URLSearchParams({
|
|
638
|
+
grant_type: DEVICE_CODE_GRANT_TYPE,
|
|
639
|
+
client_id: config.clientId,
|
|
640
|
+
device_code: deviceCode
|
|
641
|
+
}).toString(),
|
|
642
|
+
signal
|
|
643
|
+
}),
|
|
644
|
+
catch: (error) => new TokenExchangeError({
|
|
645
|
+
message: `Token poll request failed: ${error instanceof Error ? error.message : String(error)}`
|
|
646
|
+
})
|
|
647
|
+
});
|
|
648
|
+
if (response.ok) {
|
|
649
|
+
const tokens = yield* Effect3.tryPromise({
|
|
650
|
+
try: () => response.json(),
|
|
651
|
+
catch: () => new TokenExchangeError({ message: "Failed to parse token response" })
|
|
652
|
+
});
|
|
653
|
+
return { kind: "tokens", tokens };
|
|
654
|
+
}
|
|
655
|
+
const errorBody = yield* Effect3.tryPromise({
|
|
565
656
|
try: () => response.json(),
|
|
566
|
-
catch: () => new TokenExchangeError({ message: "Failed to parse token
|
|
657
|
+
catch: () => new TokenExchangeError({ message: "Failed to parse token poll error response" })
|
|
567
658
|
});
|
|
568
|
-
|
|
659
|
+
switch (errorBody.error) {
|
|
660
|
+
case "authorization_pending":
|
|
661
|
+
return { kind: "pending" };
|
|
662
|
+
case "slow_down":
|
|
663
|
+
return { kind: "slow_down" };
|
|
664
|
+
case "access_denied":
|
|
665
|
+
return { kind: "denied" };
|
|
666
|
+
case "expired_token":
|
|
667
|
+
return { kind: "expired" };
|
|
668
|
+
default:
|
|
669
|
+
return yield* Effect3.fail(
|
|
670
|
+
new TokenExchangeError({
|
|
671
|
+
message: `Token poll failed: ${response.status} ${response.statusText} - ${errorBody.error ?? "unknown_error"}`,
|
|
672
|
+
statusCode: response.status
|
|
673
|
+
})
|
|
674
|
+
);
|
|
675
|
+
}
|
|
569
676
|
});
|
|
570
|
-
const
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
if (
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
677
|
+
const pollForToken = (config, device) => Effect3.gen(function* () {
|
|
678
|
+
const deadline = Date.now() + device.expires_in * 1e3;
|
|
679
|
+
let interval = device.interval ?? DEFAULT_POLL_INTERVAL_SECONDS;
|
|
680
|
+
while (true) {
|
|
681
|
+
yield* Effect3.sleep(Duration.seconds(interval));
|
|
682
|
+
if (Date.now() >= deadline) {
|
|
683
|
+
return yield* Effect3.fail(
|
|
684
|
+
new DeviceCodeExpiredError({
|
|
685
|
+
message: "The login code expired before it was approved. Please run `gigadrive login` again."
|
|
686
|
+
})
|
|
687
|
+
);
|
|
580
688
|
}
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
res.writeHead(200, { "Content-Type": "text/html", Connection: "close" });
|
|
606
|
-
if (oauthError) {
|
|
607
|
-
const safeOauthError = escapeHtml(oauthError);
|
|
608
|
-
res.end(`<h1>Login Failed!</h1><p>Error: ${safeOauthError}</p><p>You can close this tab.</p>`);
|
|
609
|
-
server.close();
|
|
610
|
-
resume(Effect3.fail(new LoginFlowError({ message: `OAuth error: ${oauthError}` })));
|
|
611
|
-
return;
|
|
612
|
-
}
|
|
613
|
-
if (receivedState !== state) {
|
|
614
|
-
res.end(
|
|
615
|
-
"<h1>Login Failed!</h1><p>State mismatch. Possible CSRF attack.</p><p>You can close this tab.</p>"
|
|
689
|
+
const result = yield* pollTokenOnce(config, device.device_code).pipe(
|
|
690
|
+
// A hung poll must not stall the loop: on timeout treat it as pending so
|
|
691
|
+
// the next iteration re-checks the expiry deadline.
|
|
692
|
+
Effect3.timeoutTo({
|
|
693
|
+
duration: Duration.seconds(REQUEST_TIMEOUT_SECONDS),
|
|
694
|
+
onSuccess: (value) => value,
|
|
695
|
+
onTimeout: () => ({ kind: "pending" })
|
|
696
|
+
})
|
|
697
|
+
);
|
|
698
|
+
switch (result.kind) {
|
|
699
|
+
case "tokens":
|
|
700
|
+
return result.tokens;
|
|
701
|
+
case "pending":
|
|
702
|
+
continue;
|
|
703
|
+
case "slow_down":
|
|
704
|
+
interval += 5;
|
|
705
|
+
continue;
|
|
706
|
+
case "denied":
|
|
707
|
+
return yield* Effect3.fail(new AuthorizationDeniedError({ message: "The login request was denied." }));
|
|
708
|
+
case "expired":
|
|
709
|
+
return yield* Effect3.fail(
|
|
710
|
+
new DeviceCodeExpiredError({
|
|
711
|
+
message: "The login code expired before it was approved. Please run `gigadrive login` again."
|
|
712
|
+
})
|
|
616
713
|
);
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
resume(Effect3.fail(new LoginFlowError({ message:
|
|
634
|
-
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
const watchCopyKey = (url) => Effect3.async((resume) => {
|
|
718
|
+
const stdin = process3.stdin;
|
|
719
|
+
if (!stdin.isTTY) {
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
stdin.setRawMode(true);
|
|
723
|
+
stdin.resume();
|
|
724
|
+
stdin.setEncoding("utf8");
|
|
725
|
+
const onData = (key) => {
|
|
726
|
+
if (key === "c" || key === "C") {
|
|
727
|
+
writeClipboard(url);
|
|
728
|
+
Console.log("Copied the login URL to your clipboard.").pipe(Effect3.runSync);
|
|
729
|
+
} else if (key === "") {
|
|
730
|
+
resume(Effect3.fail(new LoginFlowError({ message: "Login canceled." })));
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
stdin.on("data", onData);
|
|
734
|
+
return Effect3.sync(() => {
|
|
735
|
+
stdin.off("data", onData);
|
|
736
|
+
if (stdin.isTTY) stdin.setRawMode(false);
|
|
737
|
+
stdin.pause();
|
|
635
738
|
});
|
|
636
739
|
});
|
|
637
740
|
const login = Effect3.gen(function* () {
|
|
638
741
|
yield* logout;
|
|
639
|
-
const { codeVerifier, codeChallenge } = generatePKCE();
|
|
640
|
-
const state = crypto.randomBytes(16).toString("hex");
|
|
641
742
|
const config = yield* oauthConfig.getConfig;
|
|
642
|
-
const
|
|
643
|
-
const
|
|
743
|
+
const device = yield* requestDeviceAuthorization(config);
|
|
744
|
+
const verificationUri = device.verification_uri;
|
|
745
|
+
const verificationUriComplete = device.verification_uri_complete ?? verificationUri;
|
|
746
|
+
const interactive = process3.stdin.isTTY === true;
|
|
747
|
+
yield* Console.log(
|
|
748
|
+
`
|
|
749
|
+
To sign in, visit:
|
|
750
|
+
|
|
751
|
+
${verificationUri}
|
|
752
|
+
|
|
753
|
+
and enter the code:
|
|
754
|
+
|
|
755
|
+
${device.user_code}
|
|
756
|
+
`
|
|
757
|
+
);
|
|
758
|
+
if (interactive) {
|
|
759
|
+
yield* Effect3.tryPromise(() => open(verificationUriComplete)).pipe(Effect3.catchAll(() => Effect3.void));
|
|
760
|
+
yield* Console.log("We tried to open your browser automatically. If it did not open, use the URL above.");
|
|
761
|
+
yield* Console.log('Press "c" to copy the URL to your clipboard, or Ctrl+C to cancel.');
|
|
762
|
+
}
|
|
763
|
+
yield* Console.log("\nWaiting for you to approve the login\u2026");
|
|
764
|
+
const poll = pollForToken(config, device);
|
|
765
|
+
const tokens = yield* interactive ? poll.pipe(Effect3.raceFirst(watchCopyKey(verificationUriComplete))) : poll;
|
|
644
766
|
if (!tokens.refresh_token) {
|
|
645
767
|
return yield* Effect3.fail(
|
|
646
768
|
new TokenExchangeError({ message: "No refresh token received from identity provider" })
|
|
@@ -835,7 +957,7 @@ var ApiClientService = class extends Effect4.Service()("ApiClientService", {
|
|
|
835
957
|
|
|
836
958
|
// src/services/project-link.ts
|
|
837
959
|
import { FileSystem as FileSystem2, Path as Path2 } from "@effect/platform";
|
|
838
|
-
import { Effect as Effect5, Option as Option3, Schema as
|
|
960
|
+
import { Effect as Effect5, Option as Option3, Schema as Schema6 } from "effect";
|
|
839
961
|
var LINK_DIR = ".gigadrive";
|
|
840
962
|
var LINK_FILE = "project.json";
|
|
841
963
|
var ProjectLinkService = class extends Effect5.Service()("ProjectLinkService", {
|
|
@@ -854,7 +976,7 @@ var ProjectLinkService = class extends Effect5.Service()("ProjectLinkService", {
|
|
|
854
976
|
try: () => JSON.parse(data),
|
|
855
977
|
catch: () => new ProjectLinkReadError({ message: `${LINK_DIR}/${LINK_FILE} contains invalid JSON` })
|
|
856
978
|
});
|
|
857
|
-
const link = yield*
|
|
979
|
+
const link = yield* Schema6.decodeUnknown(ProjectLink)(json).pipe(
|
|
858
980
|
Effect5.mapError(() => new ProjectLinkReadError({ message: `${LINK_DIR}/${LINK_FILE} has an invalid schema` }))
|
|
859
981
|
);
|
|
860
982
|
return Option3.some(link);
|
|
@@ -1721,7 +1843,7 @@ import * as os3 from "os";
|
|
|
1721
1843
|
|
|
1722
1844
|
// src/services/dev-credentials-store.ts
|
|
1723
1845
|
import { FileSystem as FileSystem7, Path as Path6 } from "@effect/platform";
|
|
1724
|
-
import { Effect as Effect15, Option as Option8, Schema as
|
|
1846
|
+
import { Effect as Effect15, Option as Option8, Schema as Schema7 } from "effect";
|
|
1725
1847
|
import * as os2 from "os";
|
|
1726
1848
|
var DevCredentialsStore = class extends Effect15.Service()("DevCredentialsStore", {
|
|
1727
1849
|
accessors: true,
|
|
@@ -1754,7 +1876,7 @@ var DevCredentialsStore = class extends Effect15.Service()("DevCredentialsStore"
|
|
|
1754
1876
|
try: () => JSON.parse(data),
|
|
1755
1877
|
catch: () => new DevCredentialsStoreReadError({ message: "dev-keys.json contains invalid JSON" })
|
|
1756
1878
|
});
|
|
1757
|
-
return yield*
|
|
1879
|
+
return yield* Schema7.decodeUnknown(DevKeyStore)(json).pipe(
|
|
1758
1880
|
Effect15.mapError(() => new DevCredentialsStoreReadError({ message: "dev-keys.json has an invalid schema" }))
|
|
1759
1881
|
);
|
|
1760
1882
|
});
|
|
@@ -2145,7 +2267,10 @@ var loginCommand = Command9.make(
|
|
|
2145
2267
|
yield* Console9.log(`You are now logged in as ${name}.`);
|
|
2146
2268
|
}).pipe(
|
|
2147
2269
|
Effect19.catchTags({
|
|
2148
|
-
LoginFlowError: (err) => Console9.error(
|
|
2270
|
+
LoginFlowError: (err) => Console9.error(err.message),
|
|
2271
|
+
DeviceAuthorizationError: (err) => Console9.error(`Login failed: ${err.message}`),
|
|
2272
|
+
AuthorizationDeniedError: (err) => Console9.error(err.message),
|
|
2273
|
+
DeviceCodeExpiredError: (err) => Console9.error(err.message),
|
|
2149
2274
|
TokenExchangeError: (err) => Console9.error(`Login failed: ${err.message}`),
|
|
2150
2275
|
OAuthDiscoveryError: (err) => Console9.error(`Login failed: ${err.message}`),
|
|
2151
2276
|
AuthStorageWriteError: (err) => Console9.error(`Failed to save credentials: ${err.message}`)
|
|
@@ -2178,7 +2303,7 @@ import { Effect as Effect24 } from "effect";
|
|
|
2178
2303
|
import { Command as Command11, Options as Options6 } from "@effect/cli";
|
|
2179
2304
|
import { FileSystem as FileSystem9, Path as Path8 } from "@effect/platform";
|
|
2180
2305
|
import { formatFileSize } from "@gigadrive/commons";
|
|
2181
|
-
import { Console as Console11, Duration, Effect as Effect23, Option as Option12, Schedule, Stream } from "effect";
|
|
2306
|
+
import { Console as Console11, Duration as Duration2, Effect as Effect23, Option as Option12, Schedule, Stream } from "effect";
|
|
2182
2307
|
import * as os4 from "os";
|
|
2183
2308
|
|
|
2184
2309
|
// src/services/archive.ts
|
|
@@ -2578,7 +2703,7 @@ var pollDeployment = (deploymentId) => Effect23.gen(function* () {
|
|
|
2578
2703
|
);
|
|
2579
2704
|
}
|
|
2580
2705
|
}),
|
|
2581
|
-
Schedule.addDelay(Schedule.forever, () =>
|
|
2706
|
+
Schedule.addDelay(Schedule.forever, () => Duration2.seconds(1))
|
|
2582
2707
|
).pipe(
|
|
2583
2708
|
Effect23.catchAll((err) => {
|
|
2584
2709
|
if (err === "done") return Effect23.void;
|