( connection: ExecutorServerConnection, )
| 754 | const OAUTH_REFRESH_SKEW_SECONDS = 60; |
| 755 | |
| 756 | const refreshOAuthConnection = ( |
| 757 | connection: ExecutorServerConnection, |
| 758 | ): Effect.Effect<ExecutorServerConnection, Error, FileSystem.FileSystem | PlatformPath.Path> => |
| 759 | Effect.gen(function* () { |
| 760 | const auth = connection.auth; |
| 761 | if (!auth || auth.kind !== "oauth") return connection; |
| 762 | const now = Math.floor(Date.now() / 1000); |
| 763 | if (auth.expiresAt && auth.expiresAt - now > OAUTH_REFRESH_SKEW_SECONDS) return connection; |
| 764 | // Destructure so the narrowed string types survive into the deferred |
| 765 | // `tryPromise` callback (where TS would otherwise re-widen the fields). |
| 766 | const { refreshToken, tokenEndpoint, clientId } = auth; |
| 767 | if (!refreshToken || !tokenEndpoint || !clientId) return connection; |
| 768 | |
| 769 | const headers = yield* resolveExecutorServerConfiguredHeaders(connection, process.env).pipe( |
| 770 | Effect.mapError(toError), |
| 771 | ); |
| 772 | |
| 773 | const refreshed = yield* Effect.tryPromise({ |
| 774 | try: () => |
| 775 | refreshDeviceTokens({ |
| 776 | tokenEndpoint, |
| 777 | clientId, |
| 778 | refreshToken, |
| 779 | serverOrigin: connection.origin, |
| 780 | headers, |
| 781 | }), |
| 782 | catch: toError, |
| 783 | // On a failed refresh, keep the existing token and let the eventual 401 |
| 784 | // surface, better than blocking the command on a transient hiccup. |
| 785 | }).pipe(Effect.option); |
| 786 | if (Option.isNone(refreshed)) return connection; |
| 787 | |
| 788 | const next = refreshed.value; |
| 789 | const nextConnection = normalizeExecutorServerConnection({ |
| 790 | ...connection, |
| 791 | auth: { |
| 792 | kind: "oauth", |
| 793 | accessToken: next.accessToken, |
| 794 | refreshToken: next.refreshToken ?? refreshToken, |
| 795 | ...(next.expiresAt ? { expiresAt: next.expiresAt } : {}), |
| 796 | tokenEndpoint, |
| 797 | clientId, |
| 798 | }, |
| 799 | }); |
| 800 | |
| 801 | const profileName = profileNameFromConnectionKey(connection.key); |
| 802 | if (profileName) { |
| 803 | yield* upsertCliServerConnectionProfile({ |
| 804 | name: profileName, |
| 805 | connection: nextConnection, |
| 806 | makeDefault: false, |
| 807 | }).pipe(Effect.ignore); |
| 808 | } |
| 809 | return nextConnection; |
| 810 | }); |
| 811 | |
| 812 | const resolveExecutorServerConnection = ( |
| 813 | target: ServerTarget, |
no test coverage detected