@knpkv/atlassian-common 1.4.1 → 1.6.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/CHANGELOG.md +14 -0
- package/README.md +32 -1
- package/dist/Brand.d.ts +2 -2
- package/dist/Brand.d.ts.map +1 -1
- package/dist/Brand.js.map +1 -1
- package/dist/Hash.js +2 -2
- package/dist/Hash.js.map +1 -1
- package/dist/auth/OAuthEndpoints.js +3 -3
- package/dist/auth/OAuthEndpoints.js.map +1 -1
- package/dist/auth/OAuthOperations.d.ts +1 -1
- package/dist/auth/OAuthOperations.d.ts.map +1 -1
- package/dist/auth/OAuthOperations.js +8 -8
- package/dist/auth/OAuthOperations.js.map +1 -1
- package/dist/bin.js +2 -2
- package/dist/bin.js.map +1 -1
- package/dist/cli-auth/AtlassianCliAuth.d.ts +102 -0
- package/dist/cli-auth/AtlassianCliAuth.d.ts.map +1 -0
- package/dist/cli-auth/AtlassianCliAuth.js +298 -0
- package/dist/cli-auth/AtlassianCliAuth.js.map +1 -0
- package/dist/cli-auth/index.d.ts +13 -0
- package/dist/cli-auth/index.d.ts.map +1 -0
- package/dist/cli-auth/index.js +13 -0
- package/dist/cli-auth/index.js.map +1 -0
- package/dist/cli-auth/internal/NodeLayers.d.ts +5 -0
- package/dist/cli-auth/internal/NodeLayers.d.ts.map +1 -0
- package/dist/cli-auth/internal/NodeLayers.js +17 -0
- package/dist/cli-auth/internal/NodeLayers.js.map +1 -0
- package/dist/cli-auth/internal/oauthServer.d.ts +54 -0
- package/dist/cli-auth/internal/oauthServer.d.ts.map +1 -0
- package/dist/cli-auth/internal/oauthServer.js +94 -0
- package/dist/cli-auth/internal/oauthServer.js.map +1 -0
- package/dist/cli-auth/openBrowser.d.ts +16 -0
- package/dist/cli-auth/openBrowser.d.ts.map +1 -0
- package/dist/cli-auth/openBrowser.js +30 -0
- package/dist/cli-auth/openBrowser.js.map +1 -0
- package/dist/config/ConfigPaths.js +2 -2
- package/dist/config/ProfileManager.d.ts +1 -1
- package/dist/config/ProfileManager.d.ts.map +1 -1
- package/dist/config/ProfileManager.js +3 -3
- package/dist/config/ProfileManager.js.map +1 -1
- package/package.json +11 -7
- package/src/Brand.ts +5 -5
- package/src/Hash.ts +2 -2
- package/src/auth/OAuthEndpoints.ts +3 -3
- package/src/auth/OAuthOperations.ts +14 -24
- package/src/bin.ts +2 -2
- package/src/cli-auth/AtlassianCliAuth.ts +530 -0
- package/src/cli-auth/index.ts +18 -0
- package/src/cli-auth/internal/NodeLayers.ts +25 -0
- package/src/cli-auth/internal/oauthServer.ts +168 -0
- package/src/cli-auth/openBrowser.ts +47 -0
- package/src/config/ConfigPaths.ts +3 -3
- package/src/config/ProfileManager.ts +3 -3
- package/test/ApiUpdateWorkflows.test.ts +286 -10
- package/test/OAuthOperations.test.ts +2 -2
- package/test/ProfileManager.test.ts +1 -1
- package/test/cliAuth.guardrails.test.ts +76 -0
- package/test/cliAuth.login.test.ts +319 -0
- package/test/cliAuth.oauthServer.test.ts +276 -0
- package/test/cliAuth.openBrowser.test.ts +70 -0
- package/test/cliAuth.refresh.test.ts +304 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth2 login, token refresh and profile management for an Atlassian CLI.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **One flow, many CLIs**: `jira` and `confluence` differ only in the storage
|
|
7
|
+
* namespace, scopes, the names in their messages and their "not logged in"
|
|
8
|
+
* error. {@link makeAtlassianCliAuth} takes those as options; each CLI keeps
|
|
9
|
+
* its own service tag and public interface and delegates to this.
|
|
10
|
+
* - **Refresh lock**: a `Ref<Option<Deferred>>` prevents concurrent refreshes —
|
|
11
|
+
* the first caller refreshes, the others await the same Deferred.
|
|
12
|
+
* - **Browser-based login**: starts a local callback server, opens the browser,
|
|
13
|
+
* and awaits the OAuth code with a 5-minute timeout. A browser that will not
|
|
14
|
+
* open is reported on stderr and the wait continues: the URL is printed first.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const layer = Layer.effect(
|
|
19
|
+
* JiraAuth,
|
|
20
|
+
* makeAtlassianCliAuth({ toolName: "jira-cli", commandName: "jira", productName: "Jira", scopes, authMissing })
|
|
21
|
+
* ).pipe(Layer.provide(NodeCliAuthLive))
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @module
|
|
25
|
+
*/
|
|
26
|
+
import * as Clock from "effect/Clock";
|
|
27
|
+
import * as Console from "effect/Console";
|
|
28
|
+
import * as Crypto from "effect/Crypto";
|
|
29
|
+
import * as Deferred from "effect/Deferred";
|
|
30
|
+
import * as Effect from "effect/Effect";
|
|
31
|
+
import * as FileSystem from "effect/FileSystem";
|
|
32
|
+
import * as HttpClient from "effect/http/HttpClient";
|
|
33
|
+
import * as Option from "effect/Option";
|
|
34
|
+
import * as Path from "effect/Path";
|
|
35
|
+
import { ChildProcessSpawner } from "effect/process";
|
|
36
|
+
import * as Redacted from "effect/Redacted";
|
|
37
|
+
import * as Ref from "effect/Ref";
|
|
38
|
+
import { buildAuthUrl, computeCodeChallenge, generateCodeVerifier } from "../auth/OAuthEndpoints.js";
|
|
39
|
+
import { OAuthError } from "../auth/OAuthErrors.js";
|
|
40
|
+
import { buildOAuthTokenAt, exchangeCodeForTokens, getAccessibleResources, getUserInfo, refreshToken, revokeToken } from "../auth/OAuthOperations.js";
|
|
41
|
+
import { generateUUID } from "../auth/uuid.js";
|
|
42
|
+
import { deleteActiveProfile, deleteProfileBySelector, loadActiveProfile, loadActiveProfileToken, loadProfiles, saveProfileToken, setActiveProfileBySelector } from "../config/AuthProfiles.js";
|
|
43
|
+
import { HomeDirectoryTag } from "../config/ConfigPaths.js";
|
|
44
|
+
import { isTokenExpiredAt, loadOAuthConfig, saveOAuthConfig } from "../config/TokenStorage.js";
|
|
45
|
+
import { callbackUrl, HttpServerFactoryTag, startCallbackServer } from "./internal/oauthServer.js";
|
|
46
|
+
import { openBrowser } from "./openBrowser.js";
|
|
47
|
+
// Atlassian rotates refresh tokens: the response carries a replacement and the
|
|
48
|
+
// one we sent is consumed server-side. Interrupting between the round-trip and
|
|
49
|
+
// the save therefore destroys the credential — the next refresh fails and
|
|
50
|
+
// `getAccessToken` reacts by deleting the token file, so the user is silently
|
|
51
|
+
// logged out and has to log in again.
|
|
52
|
+
//
|
|
53
|
+
// Interruption is routine here, not hypothetical: this runs during layer
|
|
54
|
+
// construction on every CLI invocation, and jcf's nvim statusline kills the
|
|
55
|
+
// process on `VimLeave`. So the grant and the persist are atomic.
|
|
56
|
+
//
|
|
57
|
+
// The region carries its own deadline rather than relying on a caller's.
|
|
58
|
+
// `Effect.timeout` is a race, and racing an uninterruptible loser means waiting
|
|
59
|
+
// for it — an outer bound would go inert and, worse, an uninterruptible region
|
|
60
|
+
// with no deadline of its own absorbs SIGINT/SIGTERM entirely
|
|
61
|
+
// (`NodeRuntime.runMain`'s signal handlers only interrupt the main fiber),
|
|
62
|
+
// leaving a process that ignores Ctrl-C. The deadline forked inside the region
|
|
63
|
+
// is itself interruptible, so it does bound this.
|
|
64
|
+
//
|
|
65
|
+
// Abandoning the round-trip cannot prove the grant did not land: Atlassian may
|
|
66
|
+
// consume the refresh token and rotate it after we have stopped listening, and
|
|
67
|
+
// no client-side deadline changes that. So the deadline is paired with the rule
|
|
68
|
+
// in `getAccessToken` — a refresh that fails without a verdict never deletes the
|
|
69
|
+
// stored token. The credential survives to be retried, and only a real
|
|
70
|
+
// rejection ends the session.
|
|
71
|
+
const REFRESH_TIMEOUT = "30 seconds";
|
|
72
|
+
/**
|
|
73
|
+
* Build an Atlassian CLI's auth operations.
|
|
74
|
+
*
|
|
75
|
+
* Requires an `HttpClient`, a `ChildProcessSpawner` (browser launch), `Crypto`
|
|
76
|
+
* (PKCE), the callback-server factory, and the file system and home directory the
|
|
77
|
+
* tokens are stored under. `NodeCliAuthLive` supplies the last three on Node.
|
|
78
|
+
*
|
|
79
|
+
* @category Constructors
|
|
80
|
+
*/
|
|
81
|
+
export const makeAtlassianCliAuth = Effect.fn("AtlassianCliAuth.make")((options) => Effect.gen(function* () {
|
|
82
|
+
const { authMissing, commandName, productName, scopes, toolName } = options;
|
|
83
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
84
|
+
const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
85
|
+
const cryptoService = yield* Crypto.Crypto;
|
|
86
|
+
const serverFactory = yield* HttpServerFactoryTag;
|
|
87
|
+
const withHttp = Effect.provideService(HttpClient.HttpClient, httpClient);
|
|
88
|
+
const withCrypto = Effect.provideService(Crypto.Crypto, cryptoService);
|
|
89
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
90
|
+
const path = yield* Path.Path;
|
|
91
|
+
const homeDirectory = yield* HomeDirectoryTag;
|
|
92
|
+
const withStorage = (effect) => effect.pipe(Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path), Effect.provideService(HomeDirectoryTag, homeDirectory));
|
|
93
|
+
const loadToken = () => loadActiveProfileToken(toolName).pipe(withStorage);
|
|
94
|
+
const saveToken = (token) => saveProfileToken(toolName, token).pipe(withStorage);
|
|
95
|
+
const deleteToken = () => deleteActiveProfile(toolName).pipe(withStorage);
|
|
96
|
+
const saveConfig = (config) => saveOAuthConfig(toolName, config).pipe(withStorage);
|
|
97
|
+
const legacyOAuthConfig = options.legacyOAuthConfig;
|
|
98
|
+
const loadConfig = Effect.fn("AtlassianCliAuth.loadConfig")(() => Effect.gen(function* () {
|
|
99
|
+
const config = yield* loadOAuthConfig(toolName);
|
|
100
|
+
if (config !== null || legacyOAuthConfig === undefined)
|
|
101
|
+
return config;
|
|
102
|
+
const legacy = yield* legacyOAuthConfig;
|
|
103
|
+
if (legacy !== null) {
|
|
104
|
+
yield* saveOAuthConfig(toolName, legacy);
|
|
105
|
+
}
|
|
106
|
+
return legacy;
|
|
107
|
+
}).pipe(withStorage));
|
|
108
|
+
const refreshLock = yield* Ref.make(Option.none());
|
|
109
|
+
// The URL is printed before this runs, so a browser that will not open is
|
|
110
|
+
// reported and the wait for the callback continues.
|
|
111
|
+
const openBrowserOrWarn = Effect.fn("AtlassianCliAuth.openBrowserOrWarn")((url) => openBrowser(url).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, childProcessSpawner), Effect.catchTags({
|
|
112
|
+
BrowserOpenError: (error) => Console.error(`Could not open a browser (${error.command} exited ${error.exitCode}); visit the URL above.`),
|
|
113
|
+
PlatformError: () => Console.error("Could not open a browser; visit the URL above.")
|
|
114
|
+
})));
|
|
115
|
+
const getConfig = Effect.fn("AtlassianCliAuth.getConfig")(() => Effect.gen(function* () {
|
|
116
|
+
const config = yield* loadConfig();
|
|
117
|
+
if (config === null) {
|
|
118
|
+
return yield* new OAuthError({
|
|
119
|
+
step: "authorize",
|
|
120
|
+
cause: `OAuth not configured. Run '${commandName} auth configure' first.`
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
return config;
|
|
124
|
+
}));
|
|
125
|
+
const refreshAndPersist = Effect.fn("AtlassianCliAuth.refreshAndPersist")((token, config) => Effect.uninterruptible(Effect.gen(function* () {
|
|
126
|
+
const updated = yield* refreshToken(token, config).pipe(withHttp, Effect.timeout(REFRESH_TIMEOUT), Effect.catchTag("TimeoutError", () => Effect.fail(new OAuthError({ step: "refresh", cause: `no response within ${REFRESH_TIMEOUT}` }))));
|
|
127
|
+
yield* saveToken(updated);
|
|
128
|
+
return updated;
|
|
129
|
+
})));
|
|
130
|
+
const isConfigured = Effect.fn("AtlassianCliAuth.isConfigured")(() => loadConfig().pipe(Effect.map((config) => config !== null)));
|
|
131
|
+
const login = Effect.fn("AtlassianCliAuth.login")((loginOptions) => Effect.gen(function* () {
|
|
132
|
+
const config = yield* getConfig();
|
|
133
|
+
const state = yield* generateUUID().pipe(withCrypto);
|
|
134
|
+
const codeVerifier = yield* generateCodeVerifier().pipe(withCrypto);
|
|
135
|
+
const codeChallenge = yield* computeCodeChallenge(codeVerifier).pipe(withCrypto);
|
|
136
|
+
const { code, port } = yield* Effect.scoped(Effect.gen(function* () {
|
|
137
|
+
const { codePromise, port } = yield* startCallbackServer(state).pipe(Effect.provideService(HttpServerFactoryTag, serverFactory));
|
|
138
|
+
const authUrl = buildAuthUrl({
|
|
139
|
+
clientId: config.clientId,
|
|
140
|
+
state,
|
|
141
|
+
port,
|
|
142
|
+
redirectUri: callbackUrl(port),
|
|
143
|
+
scopes: [...scopes],
|
|
144
|
+
codeChallenge
|
|
145
|
+
});
|
|
146
|
+
yield* Console.log(`Opening browser for Atlassian login (callback on port ${port})...`);
|
|
147
|
+
yield* Console.log(`If browser doesn't open, visit: ${authUrl}`);
|
|
148
|
+
yield* openBrowserOrWarn(authUrl);
|
|
149
|
+
yield* Console.log("Waiting for authorization (press Ctrl+C to cancel)...");
|
|
150
|
+
const code = yield* codePromise.pipe(Effect.timeout("5 minutes"), Effect.catchTag("TimeoutError", () => Effect.fail(new OAuthError({ step: "authorize", cause: "Authorization timed out" }))));
|
|
151
|
+
return { code, port };
|
|
152
|
+
}));
|
|
153
|
+
yield* Console.log("Exchanging code for tokens...");
|
|
154
|
+
const tokens = yield* exchangeCodeForTokens(code, config, {
|
|
155
|
+
port,
|
|
156
|
+
redirectUri: callbackUrl(port),
|
|
157
|
+
codeVerifier
|
|
158
|
+
}).pipe(withHttp);
|
|
159
|
+
yield* Console.log("Fetching accessible sites...");
|
|
160
|
+
const sites = yield* getAccessibleResources(tokens.access_token).pipe(withHttp);
|
|
161
|
+
const [first, ...others] = sites;
|
|
162
|
+
if (first === undefined) {
|
|
163
|
+
return yield* new OAuthError({
|
|
164
|
+
step: "authorize",
|
|
165
|
+
cause: `No ${productName} sites found for this account`
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
let site = first;
|
|
169
|
+
if (others.length > 0) {
|
|
170
|
+
const siteUrl = loginOptions?.siteUrl;
|
|
171
|
+
if (siteUrl === undefined || siteUrl === "") {
|
|
172
|
+
yield* Console.log(`Multiple ${productName} sites found. Please select one:`);
|
|
173
|
+
for (const s of sites) {
|
|
174
|
+
yield* Console.log(` - ${s.name}: ${s.url}`);
|
|
175
|
+
}
|
|
176
|
+
yield* Console.log(`\nRun '${commandName} auth login --site <url>' to select a site`);
|
|
177
|
+
return sites.map((s) => ({ id: s.id, name: s.name, url: s.url }));
|
|
178
|
+
}
|
|
179
|
+
const matched = sites.find((s) => s.url === siteUrl);
|
|
180
|
+
if (matched === undefined) {
|
|
181
|
+
const available = sites.map((s) => ` - ${s.name}: ${s.url}`).join("\n");
|
|
182
|
+
return yield* new OAuthError({
|
|
183
|
+
step: "authorize",
|
|
184
|
+
cause: `Site '${siteUrl}' not found. Available sites:\n${available}`
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
site = matched;
|
|
188
|
+
}
|
|
189
|
+
yield* Console.log("Fetching user info...");
|
|
190
|
+
const user = yield* getUserInfo(tokens.access_token).pipe(withHttp);
|
|
191
|
+
const nowMs = yield* Clock.currentTimeMillis;
|
|
192
|
+
yield* saveToken(buildOAuthTokenAt(tokens, site, user, nowMs));
|
|
193
|
+
yield* Console.log(`Logged in as ${user.name} (${user.email})`);
|
|
194
|
+
return undefined;
|
|
195
|
+
}));
|
|
196
|
+
const logout = Effect.fn("AtlassianCliAuth.logout")(() => Effect.gen(function* () {
|
|
197
|
+
const token = yield* loadToken();
|
|
198
|
+
if (token === null) {
|
|
199
|
+
yield* Console.log("Not logged in");
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const config = yield* loadConfig();
|
|
203
|
+
if (config !== null) {
|
|
204
|
+
yield* revokeToken(token, config).pipe(withHttp, Effect.tap(() => Effect.log("Token revoked with Atlassian")), Effect.catch((error) => Effect.log(`Warning: Failed to revoke token: ${error.message}`)));
|
|
205
|
+
}
|
|
206
|
+
yield* deleteToken();
|
|
207
|
+
}));
|
|
208
|
+
const getAccessToken = Effect.fn("AtlassianCliAuth.getAccessToken")(() => Effect.gen(function* () {
|
|
209
|
+
const token = yield* loadToken();
|
|
210
|
+
if (token === null) {
|
|
211
|
+
return yield* Effect.fail(authMissing());
|
|
212
|
+
}
|
|
213
|
+
const nowMs = yield* Clock.currentTimeMillis;
|
|
214
|
+
if (!isTokenExpiredAt(token, nowMs)) {
|
|
215
|
+
return Redacted.make(token.access_token);
|
|
216
|
+
}
|
|
217
|
+
// Atomically check-then-set refresh lock to avoid TOCTOU race
|
|
218
|
+
const deferred = yield* Deferred.make();
|
|
219
|
+
const existing = yield* Ref.modify(refreshLock, (current) => Option.isSome(current)
|
|
220
|
+
? [current.value, current]
|
|
221
|
+
: [deferred, Option.some(deferred)]);
|
|
222
|
+
// Another fiber is already refreshing — just await its result
|
|
223
|
+
if (existing !== deferred) {
|
|
224
|
+
const refreshed = yield* Deferred.await(existing);
|
|
225
|
+
return Redacted.make(refreshed.access_token);
|
|
226
|
+
}
|
|
227
|
+
const refresh = Effect.gen(function* () {
|
|
228
|
+
const config = yield* getConfig();
|
|
229
|
+
// stderr, not stdout: this fires from inside layer construction, so it
|
|
230
|
+
// lands ahead of whatever the command prints. On `--json` that used to
|
|
231
|
+
// put a line of prose in front of the document, and the release
|
|
232
|
+
// automation parsing it failed *after* the remote writes had happened.
|
|
233
|
+
yield* Console.error("Token expired, refreshing...");
|
|
234
|
+
return yield* refreshAndPersist(token, config);
|
|
235
|
+
}).pipe(Effect.catchTag("OAuthError", (error) => {
|
|
236
|
+
// Discard the stored credential only when Atlassian actually rejected
|
|
237
|
+
// it. A refresh can fail without saying anything about the token —
|
|
238
|
+
// transport error, timeout, interruption — and in those cases the
|
|
239
|
+
// grant may even have been consumed server-side, so the one thing we
|
|
240
|
+
// must not do is delete the replacement's only trail. Deleting on any
|
|
241
|
+
// `step === "refresh"` failure turned a flaky network into a silent
|
|
242
|
+
// logout, which is worse than retrying and is unrecoverable.
|
|
243
|
+
//
|
|
244
|
+
// Not every 4xx is a verdict either. `429` is the one this most needs
|
|
245
|
+
// to survive — several `jira`/`jcf` processes on an expired token hit
|
|
246
|
+
// the endpoint together, one wins the rotation and the rest are
|
|
247
|
+
// rate-limited — while `408`/`425` restate the timeout case and `407`
|
|
248
|
+
// and other middlebox replies never came from Atlassian at all.
|
|
249
|
+
//
|
|
250
|
+
// Status alone is still too coarse. `refreshToken` sends the client
|
|
251
|
+
// credentials in the body, so a wrong or rotated `clientSecret` comes
|
|
252
|
+
// back as `400 invalid_client`; deleting the token there destroys a
|
|
253
|
+
// working credential over a config problem, and the re-login it forces
|
|
254
|
+
// would fail the same way until `auth configure` is run. Only
|
|
255
|
+
// `invalid_grant` means the stored token itself is spent, and that is
|
|
256
|
+
// the only thing that ends the session — on a `403` too, since a bare
|
|
257
|
+
// 403 is just as likely to come from a proxy or WAF as from Atlassian
|
|
258
|
+
// revoking anything. An unparseable body leaves `errorCode` absent and
|
|
259
|
+
// the token in place.
|
|
260
|
+
const { errorCode, status } = error;
|
|
261
|
+
const rejected = errorCode === "invalid_grant" && (status === 400 || status === 403);
|
|
262
|
+
if (error.step === "refresh" && rejected) {
|
|
263
|
+
return Effect.gen(function* () {
|
|
264
|
+
yield* deleteToken();
|
|
265
|
+
return yield* new OAuthError({
|
|
266
|
+
step: "refresh",
|
|
267
|
+
cause: `Refresh token expired. Please run '${commandName} auth login' to re-authenticate.`,
|
|
268
|
+
status,
|
|
269
|
+
errorCode
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
return Effect.fail(error);
|
|
274
|
+
}));
|
|
275
|
+
// This fiber owns the refresh. Publish every exit to the waiters —
|
|
276
|
+
// success, failure, and interruption of this fiber alike — so none of
|
|
277
|
+
// them blocks on a result that would otherwise never arrive.
|
|
278
|
+
const result = yield* refresh.pipe(Effect.onExit((exit) => Ref.set(refreshLock, Option.none()).pipe(Effect.andThen(Deferred.done(deferred, exit)))));
|
|
279
|
+
return Redacted.make(result.access_token);
|
|
280
|
+
}));
|
|
281
|
+
const storedTokenField = (read) => () => loadToken().pipe(Effect.flatMap((token) => token === null ? Effect.fail(authMissing()) : Effect.succeed(read(token))));
|
|
282
|
+
return {
|
|
283
|
+
configure: Effect.fn("AtlassianCliAuth.configure")(saveConfig),
|
|
284
|
+
isConfigured,
|
|
285
|
+
login,
|
|
286
|
+
logout,
|
|
287
|
+
getAccessToken,
|
|
288
|
+
getCloudId: Effect.fn("AtlassianCliAuth.getCloudId")(storedTokenField((token) => token.cloud_id)),
|
|
289
|
+
getSiteUrl: Effect.fn("AtlassianCliAuth.getSiteUrl")(storedTokenField((token) => token.site_url)),
|
|
290
|
+
getCurrentUser: Effect.fn("AtlassianCliAuth.getCurrentUser")(() => loadToken().pipe(Effect.map((token) => token?.user ?? null))),
|
|
291
|
+
getActiveProfile: Effect.fn("AtlassianCliAuth.getActiveProfile")(() => loadActiveProfile(toolName).pipe(withStorage)),
|
|
292
|
+
listProfiles: Effect.fn("AtlassianCliAuth.listProfiles")(() => loadProfiles(toolName).pipe(Effect.map((store) => store.profiles), withStorage)),
|
|
293
|
+
switchProfile: Effect.fn("AtlassianCliAuth.switchProfile")((selector) => setActiveProfileBySelector(toolName, selector).pipe(withStorage)),
|
|
294
|
+
removeProfile: Effect.fn("AtlassianCliAuth.removeProfile")((selector) => deleteProfileBySelector(toolName, selector).pipe(withStorage)),
|
|
295
|
+
isLoggedIn: Effect.fn("AtlassianCliAuth.isLoggedIn")(() => loadToken().pipe(Effect.map((token) => token !== null)))
|
|
296
|
+
};
|
|
297
|
+
}));
|
|
298
|
+
//# sourceMappingURL=AtlassianCliAuth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AtlassianCliAuth.js","sourceRoot":"","sources":["../../src/cli-auth/AtlassianCliAuth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth2 login and token refresh for Atlassian command-line tools.
|
|
3
|
+
*
|
|
4
|
+
* Separate from `./auth` because it carries a Node callback server and a
|
|
5
|
+
* browser launcher; `./auth` stays platform-neutral for server-side consumers.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
export { type AccessibleSite, type AtlassianCliAuth, type AtlassianCliAuthOptions, type LoginOptions, makeAtlassianCliAuth } from "./AtlassianCliAuth.js";
|
|
10
|
+
export { HttpServerFactoryLive, NodeCliAuthLive } from "./internal/NodeLayers.js";
|
|
11
|
+
export { type HttpServerFactory, HttpServerFactoryTag, makeHttpServerFactory } from "./internal/oauthServer.js";
|
|
12
|
+
export { BrowserOpenError, openBrowser } from "./openBrowser.js";
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli-auth/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EACjB,oBAAoB,EACrB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AACjF,OAAO,EAAE,KAAK,iBAAiB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAA;AAC/G,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth2 login and token refresh for Atlassian command-line tools.
|
|
3
|
+
*
|
|
4
|
+
* Separate from `./auth` because it carries a Node callback server and a
|
|
5
|
+
* browser launcher; `./auth` stays platform-neutral for server-side consumers.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
export { makeAtlassianCliAuth } from "./AtlassianCliAuth.js";
|
|
10
|
+
export { HttpServerFactoryLive, NodeCliAuthLive } from "./internal/NodeLayers.js";
|
|
11
|
+
export { HttpServerFactoryTag, makeHttpServerFactory } from "./internal/oauthServer.js";
|
|
12
|
+
export { BrowserOpenError, openBrowser } from "./openBrowser.js";
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli-auth/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAKL,oBAAoB,EACrB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AACjF,OAAO,EAA0B,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAA;AAC/G,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as Layer from "effect/Layer";
|
|
2
|
+
export declare const HttpServerFactoryLive: Layer.Layer<import("./oauthServer.js").HttpServerFactoryTag, never, never>;
|
|
3
|
+
/** Everything `makeAtlassianCliAuth` needs on Node besides HTTP client, spawner and Crypto. */
|
|
4
|
+
export declare const NodeCliAuthLive: Layer.Layer<import("effect/FileSystem").FileSystem | import("../../config/ConfigPaths.js").HomeDirectoryTag | import("./oauthServer.js").HttpServerFactoryTag | import("effect/Path").Path, never, never>;
|
|
5
|
+
//# sourceMappingURL=NodeLayers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeLayers.d.ts","sourceRoot":"","sources":["../../../src/cli-auth/internal/NodeLayers.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAKrC,eAAO,MAAM,qBAAqB,4EAEjC,CAAA;AAED,+FAA+F;AAC/F,eAAO,MAAM,eAAe,2MAK3B,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js adapters for cli-auth: the OAuth callback server and token storage — the
|
|
3
|
+
* only cli-auth file importing `@effect/platform-node`.
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
|
|
8
|
+
import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer";
|
|
9
|
+
import * as NodePath from "@effect/platform-node/NodePath";
|
|
10
|
+
import * as Layer from "effect/Layer";
|
|
11
|
+
import { createServer } from "node:http";
|
|
12
|
+
import { HomeDirectoryLive } from "../../config/ConfigPaths.js";
|
|
13
|
+
import { makeHttpServerFactory } from "./oauthServer.js";
|
|
14
|
+
export const HttpServerFactoryLive = makeHttpServerFactory((options) => NodeHttpServer.layerServer(createServer, options));
|
|
15
|
+
/** Everything `makeAtlassianCliAuth` needs on Node besides HTTP client, spawner and Crypto. */
|
|
16
|
+
export const NodeCliAuthLive = Layer.mergeAll(HttpServerFactoryLive, NodeFileSystem.layer, NodePath.layer, HomeDirectoryLive);
|
|
17
|
+
//# sourceMappingURL=NodeLayers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeLayers.js","sourceRoot":"","sources":["../../../src/cli-auth/internal/NodeLayers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,cAAc,MAAM,sCAAsC,CAAA;AACtE,OAAO,KAAK,cAAc,MAAM,sCAAsC,CAAA;AACtE,OAAO,KAAK,QAAQ,MAAM,gCAAgC,CAAA;AAC1D,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAExD,MAAM,CAAC,MAAM,qBAAqB,GAAG,qBAAqB,CACxD,CAAC,OAAO,EAAE,EAAE,CAAC,cAAc,CAAC,WAAW,CAAC,YAAY,EAAE,OAAO,CAAC,CAC/D,CAAA;AAED,+FAA+F;AAC/F,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,CAAC,QAAQ,CAC3C,qBAAqB,EACrB,cAAc,CAAC,KAAK,EACpB,QAAQ,CAAC,KAAK,EACd,iBAAiB,CAClB,CAAA"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local HTTP callback server for OAuth2 authorization code capture.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Scope-owned lifecycle**: {@link startCallbackServer} returns a `codePromise`
|
|
7
|
+
* (Deferred). The server validates the CSRF `state` parameter, resolves the
|
|
8
|
+
* Deferred with the authorization code, and stops when its enclosing scope closes.
|
|
9
|
+
* - **Port auto-discovery**: Tries default port 8585, increments on conflict up to 8594.
|
|
10
|
+
* - **Ready before return**: the handler is installed before the port is handed back,
|
|
11
|
+
* so the caller never advertises a callback URL nothing answers.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
import * as Context from "effect/Context";
|
|
16
|
+
import * as Effect from "effect/Effect";
|
|
17
|
+
import { HttpServer } from "effect/http";
|
|
18
|
+
import type * as HttpServerError from "effect/http/HttpServerError";
|
|
19
|
+
import * as Layer from "effect/Layer";
|
|
20
|
+
import * as Scope from "effect/Scope";
|
|
21
|
+
import { OAuthError } from "../../auth/OAuthErrors.js";
|
|
22
|
+
/**
|
|
23
|
+
* Creates the platform HTTP server for one listen attempt. The Node adapter is
|
|
24
|
+
* {@link HttpServerFactoryLive}; tests substitute ephemeral or failing servers.
|
|
25
|
+
*/
|
|
26
|
+
export interface HttpServerFactory {
|
|
27
|
+
readonly createServerLayer: (options: CallbackServerListenOptions) => Layer.Layer<HttpServer.HttpServer, HttpServerError.ServeError, never>;
|
|
28
|
+
}
|
|
29
|
+
export interface CallbackServerListenOptions {
|
|
30
|
+
readonly host: "localhost";
|
|
31
|
+
readonly port: number;
|
|
32
|
+
}
|
|
33
|
+
export declare const callbackServerListenOptions: (port: number) => CallbackServerListenOptions;
|
|
34
|
+
export declare const callbackUrl: (port: number) => string;
|
|
35
|
+
declare const HttpServerFactoryTag_base: Context.ServiceClass<HttpServerFactoryTag, "@knpkv/atlassian-common/cli-auth/HttpServerFactory", HttpServerFactory>;
|
|
36
|
+
export declare class HttpServerFactoryTag extends HttpServerFactoryTag_base {
|
|
37
|
+
}
|
|
38
|
+
/** Builds a {@link HttpServerFactoryTag} layer from a per-attempt server layer. */
|
|
39
|
+
export declare const makeHttpServerFactory: (createLayerFn: (options: CallbackServerListenOptions) => Layer.Layer<HttpServer.HttpServer, HttpServerError.ServeError, never>) => Layer.Layer<HttpServerFactoryTag>;
|
|
40
|
+
export interface CallbackServerResult {
|
|
41
|
+
/** Resolves with the authorization code, or fails with the provider's error. */
|
|
42
|
+
readonly codePromise: Effect.Effect<string, OAuthError>;
|
|
43
|
+
/** The port the server is listening on */
|
|
44
|
+
readonly port: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Start a local HTTP server to receive the OAuth callback for `expectedState`.
|
|
48
|
+
*
|
|
49
|
+
* Fails with one `OAuthError` (step `authorize`) when no port in range can be
|
|
50
|
+
* bound; the server stops when the enclosing scope closes.
|
|
51
|
+
*/
|
|
52
|
+
export declare const startCallbackServer: (expectedState: string) => Effect.Effect<CallbackServerResult, OAuthError, HttpServerFactoryTag | Scope.Scope>;
|
|
53
|
+
export {};
|
|
54
|
+
//# sourceMappingURL=oauthServer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauthServer.d.ts","sourceRoot":"","sources":["../../../src/cli-auth/internal/oauthServer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AAEzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EAAc,UAAU,EAAyC,MAAM,aAAa,CAAA;AAC3F,OAAO,KAAK,KAAK,eAAe,MAAM,6BAA6B,CAAA;AACnE,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAGrC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAMtD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,iBAAiB,EAAE,CAAC,OAAO,EAAE,2BAA2B,KAAK,KAAK,CAAC,KAAK,CAC/E,UAAU,CAAC,UAAU,EACrB,eAAe,CAAC,UAAU,EAC1B,KAAK,CACN,CAAA;CACF;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,eAAO,MAAM,2BAA2B,SAAU,MAAM,KAAG,2BAGzD,CAAA;AAEF,eAAO,MAAM,WAAW,SAAU,MAAM,KAAG,MAA6C,CAAA;;AAExF,qBAAa,oBAAqB,SAAQ,yBAGe;CAAG;AAE5D,mFAAmF;AACnF,eAAO,MAAM,qBAAqB,kBACjB,CACb,OAAO,EAAE,2BAA2B,KACjC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,EAAE,eAAe,CAAC,UAAU,EAAE,KAAK,CAAC,KACzE,KAAK,CAAC,KAAK,CAAC,oBAAoB,CAG/B,CAAA;AAEJ,MAAM,WAAW,oBAAoB;IACnC,gFAAgF;IAChF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACvD,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAWD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,kBACf,MAAM,KACpB,MAAM,CAAC,MAAM,CAAC,oBAAoB,EAAE,UAAU,EAAE,oBAAoB,GAAG,KAAK,CAAC,KAAK,CA2EjF,CAAA"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local HTTP callback server for OAuth2 authorization code capture.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Scope-owned lifecycle**: {@link startCallbackServer} returns a `codePromise`
|
|
7
|
+
* (Deferred). The server validates the CSRF `state` parameter, resolves the
|
|
8
|
+
* Deferred with the authorization code, and stops when its enclosing scope closes.
|
|
9
|
+
* - **Port auto-discovery**: Tries default port 8585, increments on conflict up to 8594.
|
|
10
|
+
* - **Ready before return**: the handler is installed before the port is handed back,
|
|
11
|
+
* so the caller never advertises a callback URL nothing answers.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
import * as Context from "effect/Context";
|
|
16
|
+
import * as Deferred from "effect/Deferred";
|
|
17
|
+
import * as Effect from "effect/Effect";
|
|
18
|
+
import { HttpRouter, HttpServer, HttpServerRequest, HttpServerResponse } from "effect/http";
|
|
19
|
+
import * as Layer from "effect/Layer";
|
|
20
|
+
import { NetAddress } from "effect/net";
|
|
21
|
+
import * as Schema from "effect/Schema";
|
|
22
|
+
import * as Scope from "effect/Scope";
|
|
23
|
+
import { OAuthError } from "../../auth/OAuthErrors.js";
|
|
24
|
+
const DEFAULT_PORT = 8585;
|
|
25
|
+
const MAX_PORT = 8594;
|
|
26
|
+
export const callbackServerListenOptions = (port) => ({
|
|
27
|
+
host: "localhost",
|
|
28
|
+
port
|
|
29
|
+
});
|
|
30
|
+
export const callbackUrl = (port) => `http://localhost:${port}/callback`;
|
|
31
|
+
export class HttpServerFactoryTag extends Context.Service()("@knpkv/atlassian-common/cli-auth/HttpServerFactory") {
|
|
32
|
+
}
|
|
33
|
+
/** Builds a {@link HttpServerFactoryTag} layer from a per-attempt server layer. */
|
|
34
|
+
export const makeHttpServerFactory = (createLayerFn) => Layer.succeed(HttpServerFactoryTag, {
|
|
35
|
+
createServerLayer: createLayerFn
|
|
36
|
+
});
|
|
37
|
+
const AddressInUseCause = Schema.Struct({
|
|
38
|
+
code: Schema.Literal("EADDRINUSE")
|
|
39
|
+
});
|
|
40
|
+
const isAddressInUse = (error) => Schema.is(AddressInUseCause)(error.cause);
|
|
41
|
+
const page = (heading, body) => HttpServerResponse.html(`<html><body><h1>${heading}</h1><p>${body}</p></body></html>`);
|
|
42
|
+
/**
|
|
43
|
+
* Start a local HTTP server to receive the OAuth callback for `expectedState`.
|
|
44
|
+
*
|
|
45
|
+
* Fails with one `OAuthError` (step `authorize`) when no port in range can be
|
|
46
|
+
* bound; the server stops when the enclosing scope closes.
|
|
47
|
+
*/
|
|
48
|
+
export const startCallbackServer = (expectedState) => Effect.gen(function* () {
|
|
49
|
+
const factory = yield* HttpServerFactoryTag;
|
|
50
|
+
const deferred = yield* Deferred.make();
|
|
51
|
+
const scope = yield* Effect.scope;
|
|
52
|
+
// Map to OAuthError once, outside the retry, so a run of occupied ports
|
|
53
|
+
// does not nest one OAuthError inside another.
|
|
54
|
+
const buildServer = (port) => Layer.build(factory.createServerLayer(callbackServerListenOptions(port))).pipe(Scope.provide(scope), Effect.map((context) => Context.get(context, HttpServer.HttpServer)), Effect.catchIf((error) => isAddressInUse(error) && port < MAX_PORT, () => buildServer(port + 1)));
|
|
55
|
+
const server = yield* buildServer(DEFAULT_PORT).pipe(Effect.mapError((cause) => new OAuthError({ step: "authorize", cause })));
|
|
56
|
+
if (!NetAddress.isInetAddress(server.address)) {
|
|
57
|
+
return yield* new OAuthError({ step: "authorize", cause: "OAuth callback server must listen on a TCP port" });
|
|
58
|
+
}
|
|
59
|
+
const port = server.address.port;
|
|
60
|
+
const router = yield* HttpRouter.make;
|
|
61
|
+
yield* router.add("GET", "/callback", Effect.gen(function* () {
|
|
62
|
+
const req = yield* HttpServerRequest.HttpServerRequest;
|
|
63
|
+
const url = new URL(req.url, callbackUrl(port));
|
|
64
|
+
const code = url.searchParams.get("code");
|
|
65
|
+
const state = url.searchParams.get("state");
|
|
66
|
+
const error = url.searchParams.get("error");
|
|
67
|
+
const errorDescription = url.searchParams.get("error_description");
|
|
68
|
+
if (state !== expectedState) {
|
|
69
|
+
return page("Security Error", "State verification failed.").pipe(HttpServerResponse.setStatus(403));
|
|
70
|
+
}
|
|
71
|
+
if (error !== null && error !== "") {
|
|
72
|
+
// An empty description is no description: report the error code instead.
|
|
73
|
+
yield* Deferred.fail(deferred, new OAuthError({
|
|
74
|
+
step: "authorize",
|
|
75
|
+
cause: errorDescription === null || errorDescription === "" ? error : errorDescription
|
|
76
|
+
}));
|
|
77
|
+
return page("Authorization Failed", "You can close this window.");
|
|
78
|
+
}
|
|
79
|
+
if (code === null || code === "") {
|
|
80
|
+
yield* Deferred.fail(deferred, new OAuthError({ step: "authorize", cause: "No authorization code received" }));
|
|
81
|
+
return page("Error", "No authorization code received.");
|
|
82
|
+
}
|
|
83
|
+
yield* Deferred.succeed(deferred, code);
|
|
84
|
+
return page("Success!", "You can close this window and return to the terminal.");
|
|
85
|
+
}));
|
|
86
|
+
// Installs the handler in this scope and returns; a failure to install
|
|
87
|
+
// surfaces here rather than leaving the caller waiting on a dead server.
|
|
88
|
+
yield* HttpServer.serveEffect(router.asHttpEffect()).pipe(Effect.provideService(HttpServer.HttpServer, server));
|
|
89
|
+
return {
|
|
90
|
+
codePromise: Deferred.await(deferred),
|
|
91
|
+
port
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
//# sourceMappingURL=oauthServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauthServer.js","sourceRoot":"","sources":["../../../src/cli-auth/internal/oauthServer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import type * as PlatformError from "effect/PlatformError";
|
|
3
|
+
import { ChildProcessSpawner } from "effect/process";
|
|
4
|
+
declare const BrowserOpenError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
5
|
+
readonly _tag: "BrowserOpenError";
|
|
6
|
+
} & Readonly<A>;
|
|
7
|
+
/** A launcher ran but reported failure (for example `xdg-open` with no browser on a headless host). */
|
|
8
|
+
export declare class BrowserOpenError extends BrowserOpenError_base<{
|
|
9
|
+
readonly command: string;
|
|
10
|
+
readonly exitCode: number;
|
|
11
|
+
}> {
|
|
12
|
+
}
|
|
13
|
+
/** Open `url` in the user's browser. */
|
|
14
|
+
export declare const openBrowser: (url: string) => Effect.Effect<void, BrowserOpenError | PlatformError.PlatformError, ChildProcessSpawner.ChildProcessSpawner>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=openBrowser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openBrowser.d.ts","sourceRoot":"","sources":["../../src/cli-auth/openBrowser.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,aAAa,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAgB,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;;;;AAElE,uGAAuG;AACvG,qBAAa,gBAAiB,SAAQ,sBAAqC;IACzE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B,CAAC;CAAG;AAoBL,wCAAwC;AACxC,eAAO,MAAM,WAAW,QACjB,MAAM,KACV,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,gBAAgB,GAAG,aAAa,CAAC,aAAa,EAAE,mBAAmB,CAAC,mBAAmB,CAI3G,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform browser launcher backed by Effect child process services.
|
|
3
|
+
*
|
|
4
|
+
* Tries `open`, then `xdg-open`, then `rundll32.exe`, stopping at the first that
|
|
5
|
+
* exits 0. A launcher that is missing or exits non-zero falls through to the next;
|
|
6
|
+
* when none succeeds the last failure is reported, typed, so the caller decides
|
|
7
|
+
* whether a browser that never opened is fatal.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import * as Data from "effect/Data";
|
|
12
|
+
import * as Effect from "effect/Effect";
|
|
13
|
+
import { ChildProcess, ChildProcessSpawner } from "effect/process";
|
|
14
|
+
/** A launcher ran but reported failure (for example `xdg-open` with no browser on a headless host). */
|
|
15
|
+
export class BrowserOpenError extends Data.TaggedError("BrowserOpenError") {
|
|
16
|
+
}
|
|
17
|
+
const run = (command, args) => Effect.gen(function* () {
|
|
18
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
19
|
+
const exitCode = yield* spawner.exitCode(ChildProcess.make(command, args, {
|
|
20
|
+
stdin: "ignore",
|
|
21
|
+
stdout: "ignore",
|
|
22
|
+
stderr: "ignore"
|
|
23
|
+
}));
|
|
24
|
+
if (exitCode !== 0) {
|
|
25
|
+
return yield* new BrowserOpenError({ command, exitCode });
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
/** Open `url` in the user's browser. */
|
|
29
|
+
export const openBrowser = (url) => run("open", [url]).pipe(Effect.catch(() => run("xdg-open", [url])), Effect.catch(() => run("rundll32.exe", ["url.dll,FileProtocolHandler", url])));
|
|
30
|
+
//# sourceMappingURL=openBrowser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openBrowser.js","sourceRoot":"","sources":["../../src/cli-auth/openBrowser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,IAAI,MAAM,aAAa,CAAA;AACnC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAElE,uGAAuG;AACvG,MAAM,OAAO,gBAAiB,SAAQ,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAGvE;CAAG;AAEL,MAAM,GAAG,GAAG,CACV,OAAe,EACf,IAA2B,EACmF,EAAE,CAChH,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,mBAAmB,CAAC,mBAAmB,CAAA;IAC9D,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,CACtC,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE;QAC/B,KAAK,EAAE,QAAQ;QACf,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,QAAQ;KACjB,CAAC,CACH,CAAA;IACD,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;QACnB,OAAO,KAAK,CAAC,CAAC,IAAI,gBAAgB,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC3D,CAAC;AACH,CAAC,CAAC,CAAA;AAEJ,wCAAwC;AACxC,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,GAAW,EACmG,EAAE,CAChH,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CACrB,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAC1C,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC,CAAC,CAC9E,CAAA"}
|