@allocator-one/harness-cli 1.8.0 → 1.9.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 +283 -52
- package/dist/login-page-UVUKFFTT.js +292 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -93,31 +93,15 @@ import { accessSync, constants as fsConstants } from "fs";
|
|
|
93
93
|
import { createServer } from "http";
|
|
94
94
|
import { delimiter, join } from "path";
|
|
95
95
|
import { createInterface } from "readline";
|
|
96
|
-
|
|
97
|
-
// src/core/login-page.ts
|
|
98
|
-
var LOGO = '<svg width="210" height="34" viewBox="0 0 210 34" fill="none" role="img" aria-label="Infra One" xmlns="http://www.w3.org/2000/svg">\n <style>\n path {\n fill: #353535\n }\n\n @media (prefers-color-scheme: dark) {\n path {\n fill: white\n }\n }\n </style>\n <g id="I1_logo_full">\n <g id="Infra One Logo">\n <path fill-rule="evenodd" clip-rule="evenodd" d="M44.2202 29.5139V4.04761H49.617V29.5139H44.2202Z" />\n <path fill-rule="evenodd" clip-rule="evenodd" d="M121.159 11.873V13.998C119.877 12.4464 117.853 11.4345 115.29 11.4345C110.23 11.4345 106.284 15.3809 106.284 20.7103C106.284 26.0397 110.23 29.9861 115.29 29.9861C118.157 29.9861 119.877 28.9742 121.159 27.3214V29.4464H125.948V11.9067H121.159V11.873ZM116.099 25.8373C113.063 25.8373 110.938 23.7123 110.938 20.6091C110.938 17.5059 113.063 15.4484 116.099 15.4484C119.135 15.4484 121.327 17.5734 121.327 20.6091C121.327 23.6448 119.034 25.8373 116.099 25.8373Z" />\n <path d="M15.6508 0H27.8611V34H15.6508V0ZM0 11.131C0 7.92659 2.66468 5.2619 5.86905 5.2619H6.34127C9.54564 5.2619 12.2103 7.92659 12.2103 11.131C12.2103 14.3353 9.54564 17 6.34127 17H5.86905C2.66468 17 0 14.3353 0 11.131Z" />\n <path fill-rule="evenodd" clip-rule="evenodd" d="M88.4405 10.4901C87.6984 10.2203 87.3611 10.0179 86.3155 10.0179C84.8651 10.0179 84.123 10.7599 84.123 12.2103V13.627H89.4524V17.6746H84.123V29.4802H79.3333V17.6746H76.2976V13.627H79.3333V12.2103C79.3333 8.36511 81.1548 5.86908 85.4048 5.86908C87.631 5.86908 88.1706 6.13892 89.621 6.71233L88.4405 10.4901Z" />\n <path fill-rule="evenodd" clip-rule="evenodd" d="M98.0199 14.4365C99.133 12.4127 101.224 11.2321 104.091 11.6032V15.9206C100.246 15.381 98.0199 17.0337 98.0199 21.7897V29.4127H93.2302V11.873H98.0199V14.4365Z" />\n <path fill-rule="evenodd" clip-rule="evenodd" d="M65.5715 11.4008C70.3611 11.4008 72.756 14.5377 72.756 19.2262V29.5139H67.9663V20.0357C67.9663 17.371 66.6845 15.6171 64.1211 15.6171C61.5576 15.6171 59.7024 17.6409 59.7024 20.7778V29.5139H54.9127V11.873H59.7024V14.0655C60.9842 12.5139 62.9068 11.4008 65.5715 11.4008Z" />\n <path fill-rule="evenodd" clip-rule="evenodd" d="M151.01 4.621C143.387 4.621 137.687 10.0516 137.687 17.3035C137.687 24.5555 143.286 29.9861 151.01 29.9861C158.734 29.9861 164.232 24.4881 164.232 17.3035C164.232 10.119 158.633 4.621 151.01 4.621ZM151.01 25.5674C145.95 25.5674 142.746 21.992 142.746 17.3035C142.746 12.6151 145.95 9.10712 151.01 9.10712C156.069 9.10712 159.274 12.7837 159.274 17.3035C159.274 21.8234 155.968 25.5674 151.01 25.5674Z" />\n <path fill-rule="evenodd" clip-rule="evenodd" d="M179.309 11.4008C184.099 11.4008 186.494 14.5377 186.494 19.2262V29.5139H181.704V20.0357C181.704 17.371 180.423 15.6171 177.859 15.6171C175.296 15.6171 173.44 17.6409 173.44 20.7778V29.5139H168.651V11.873H173.44V14.0655C174.722 12.5139 176.645 11.4008 179.309 11.4008Z" />\n <path fill-rule="evenodd" clip-rule="evenodd" d="M209.565 20.2381C209.565 15.1786 206.26 11.3333 200.188 11.3333C194.117 11.3333 190.643 15.3809 190.643 20.6091C190.643 25.8373 194.69 29.8849 200.559 29.8849C206.429 29.8849 208.284 27.119 209.397 24.8254L205.079 23.6448C204.337 24.9266 202.954 25.9385 200.661 25.9385C197.355 25.9385 195.702 24.1845 195.331 21.8909H209.498C209.599 21.3512 209.599 20.7778 209.599 20.2381H209.565ZM195.298 19.125C195.567 17.1012 197.321 15.2797 200.155 15.2797C202.988 15.2797 204.742 17.0337 204.944 19.125H195.298Z" />\n </g>\n </g>\n</svg>';
|
|
99
|
-
function escapeHtml(text) {
|
|
100
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
101
|
-
}
|
|
102
|
-
function loginPage(message) {
|
|
103
|
-
return `<!doctype html>
|
|
104
|
-
<html lang="en"><head><meta charset="utf-8">
|
|
105
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
106
|
-
<!-- The logo paints itself white under a dark preference; without this the
|
|
107
|
-
canvas stays white and the logo disappears. -->
|
|
108
|
-
<meta name="color-scheme" content="light dark">
|
|
109
|
-
<title>harness login</title></head>
|
|
110
|
-
<body style="margin: 0;">
|
|
111
|
-
<main style="min-height: 100vh; padding: 1.5rem; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2rem; text-align: center; font-family: system-ui, sans-serif;">
|
|
112
|
-
${LOGO}
|
|
113
|
-
<p style="margin: 0;">${escapeHtml(message)}</p>
|
|
114
|
-
</main>
|
|
115
|
-
</body></html>
|
|
116
|
-
`;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// src/core/auth.ts
|
|
120
96
|
var DEFAULT_TIMEOUT_MS = 18e4;
|
|
97
|
+
var RESPONSE_DRAIN_GRACE_MS = 100;
|
|
98
|
+
var RESPONSE_FLUSH_TIMEOUT_MS = 2e3;
|
|
99
|
+
var MAX_PAGE_RESPONSES = 4;
|
|
100
|
+
var loginPageImport;
|
|
101
|
+
function loadLoginPage() {
|
|
102
|
+
loginPageImport ??= import("./login-page-UVUKFFTT.js");
|
|
103
|
+
return loginPageImport;
|
|
104
|
+
}
|
|
121
105
|
function generateState() {
|
|
122
106
|
return randomBytes(24).toString("base64url");
|
|
123
107
|
}
|
|
@@ -183,72 +167,313 @@ async function loopbackLogin(baseUrl, options) {
|
|
|
183
167
|
});
|
|
184
168
|
});
|
|
185
169
|
if (port === null) {
|
|
186
|
-
return {
|
|
170
|
+
return {
|
|
171
|
+
ok: false,
|
|
172
|
+
message: "could not open a loopback port for the browser handoff"
|
|
173
|
+
};
|
|
187
174
|
}
|
|
188
175
|
try {
|
|
189
176
|
const query = new URLSearchParams({ port: String(port), state: state2, challenge });
|
|
190
177
|
const url = `${baseUrl}/cli/login?${query.toString()}`;
|
|
178
|
+
const callbackPromise = awaitCallback(server, state2, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
179
|
+
void loadLoginPage().catch(() => {
|
|
180
|
+
loginPageImport = void 0;
|
|
181
|
+
});
|
|
191
182
|
const openBrowser = options.openBrowser ?? defaultOpenBrowser;
|
|
192
|
-
|
|
183
|
+
try {
|
|
184
|
+
openBrowser(url);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
187
|
+
return { ok: false, message: `could not open a browser: ${detail}` };
|
|
188
|
+
}
|
|
193
189
|
notify(`Opening ${url}`);
|
|
194
190
|
notify("Waiting for the browser authorization (Ctrl-C to abort)\u2026");
|
|
195
|
-
const
|
|
196
|
-
if (!
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
191
|
+
const callback = await callbackPromise;
|
|
192
|
+
if (!callback.ok) return callback;
|
|
193
|
+
try {
|
|
194
|
+
const token = await exchangeCode(baseUrl, callback.code, verifier, options);
|
|
195
|
+
if (!token.ok) {
|
|
196
|
+
await callback.respond(
|
|
197
|
+
400,
|
|
198
|
+
"Harness could not exchange the browser authorization. Return to the terminal for details.",
|
|
199
|
+
"error"
|
|
200
|
+
);
|
|
201
|
+
return token;
|
|
202
|
+
}
|
|
203
|
+
const result = await verifyAndStore(baseUrl, token.token, options);
|
|
204
|
+
if (result.ok) {
|
|
205
|
+
await callback.respond(200, "You can close this browser and return to the terminal.", "success");
|
|
206
|
+
} else {
|
|
207
|
+
await callback.respond(
|
|
208
|
+
400,
|
|
209
|
+
"Harness could not verify and store this login. Return to the terminal for details.",
|
|
210
|
+
"error"
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
return result;
|
|
214
|
+
} catch (error) {
|
|
215
|
+
await callback.respond(500, "Harness could not finish the login. Return to the terminal for details.", "error");
|
|
216
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
217
|
+
return { ok: false, message: `could not finish the browser login: ${detail}` };
|
|
218
|
+
}
|
|
200
219
|
} finally {
|
|
201
|
-
server.closeAllConnections();
|
|
202
220
|
server.close();
|
|
221
|
+
const forceClose = setTimeout(
|
|
222
|
+
() => server.closeAllConnections(),
|
|
223
|
+
RESPONSE_FLUSH_TIMEOUT_MS + RESPONSE_DRAIN_GRACE_MS
|
|
224
|
+
);
|
|
225
|
+
forceClose.unref();
|
|
203
226
|
}
|
|
204
227
|
}
|
|
205
228
|
function awaitCallback(server, state2, timeoutMs) {
|
|
206
229
|
return new Promise((resolve) => {
|
|
230
|
+
let phase = "waiting";
|
|
231
|
+
let finalResponse = null;
|
|
232
|
+
let callbackPageResponses = 0;
|
|
233
|
+
const rootPageResponses = /* @__PURE__ */ new Map();
|
|
234
|
+
const acceptedResponders = [];
|
|
235
|
+
const inFlightResponses = /* @__PURE__ */ new Set();
|
|
236
|
+
const trackResponse = (responsePromise) => {
|
|
237
|
+
inFlightResponses.add(responsePromise);
|
|
238
|
+
void responsePromise.finally(() => inFlightResponses.delete(responsePromise));
|
|
239
|
+
return responsePromise;
|
|
240
|
+
};
|
|
241
|
+
const drainTrackedResponses = async () => {
|
|
242
|
+
await new Promise((settle) => setTimeout(settle, RESPONSE_DRAIN_GRACE_MS));
|
|
243
|
+
const deadline = Date.now() + RESPONSE_FLUSH_TIMEOUT_MS;
|
|
244
|
+
while (inFlightResponses.size > 0 && Date.now() < deadline) {
|
|
245
|
+
const remaining = deadline - Date.now();
|
|
246
|
+
await Promise.race([
|
|
247
|
+
Promise.allSettled([...inFlightResponses]),
|
|
248
|
+
new Promise((settle) => setTimeout(settle, remaining))
|
|
249
|
+
]);
|
|
250
|
+
}
|
|
251
|
+
};
|
|
207
252
|
const timer = setTimeout(() => {
|
|
208
|
-
|
|
253
|
+
if (phase !== "waiting") return;
|
|
254
|
+
const timeoutResponse = {
|
|
255
|
+
status: 410,
|
|
256
|
+
message: "This login attempt has ended \u2014 run harness login again if needed.",
|
|
257
|
+
pageState: "error"
|
|
258
|
+
};
|
|
259
|
+
phase = "terminal";
|
|
260
|
+
finalResponse = timeoutResponse;
|
|
261
|
+
resolve({
|
|
262
|
+
ok: false,
|
|
263
|
+
message: "timed out waiting for the browser authorization"
|
|
264
|
+
});
|
|
265
|
+
void drainTrackedResponses();
|
|
209
266
|
}, timeoutMs);
|
|
267
|
+
server.once("close", () => {
|
|
268
|
+
clearTimeout(timer);
|
|
269
|
+
if (phase !== "waiting") return;
|
|
270
|
+
phase = "terminal";
|
|
271
|
+
resolve({ ok: false, message: "the browser login listener closed before authorization completed" });
|
|
272
|
+
});
|
|
210
273
|
server.on("request", (request, response) => {
|
|
211
|
-
const respond = (
|
|
212
|
-
|
|
213
|
-
|
|
274
|
+
const respond = callbackResponder(response);
|
|
275
|
+
const respondWithPage = (status, message, pageState) => {
|
|
276
|
+
if (callbackPageResponses >= MAX_PAGE_RESPONSES) {
|
|
277
|
+
respondEmpty(response, 429);
|
|
278
|
+
return Promise.resolve();
|
|
279
|
+
}
|
|
280
|
+
callbackPageResponses += 1;
|
|
281
|
+
return respond(status, message, pageState);
|
|
282
|
+
};
|
|
283
|
+
const respondAtRoot = (status, message, pageState) => {
|
|
284
|
+
const rootPhase = phase === "terminal" ? "terminal" : "waiting";
|
|
285
|
+
const responses = rootPageResponses.get(rootPhase) ?? 0;
|
|
286
|
+
if (responses >= MAX_PAGE_RESPONSES) {
|
|
287
|
+
respondEmpty(response, 429);
|
|
288
|
+
return Promise.resolve();
|
|
289
|
+
}
|
|
290
|
+
rootPageResponses.set(rootPhase, responses + 1);
|
|
291
|
+
return respond(status, message, pageState);
|
|
214
292
|
};
|
|
215
293
|
let url;
|
|
216
294
|
try {
|
|
217
295
|
url = new URL(request.url ?? "/", "http://127.0.0.1");
|
|
218
296
|
} catch {
|
|
219
|
-
|
|
297
|
+
respondEmpty(response, 404);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const path = url.pathname.replace(/\/+$/, "") || "/";
|
|
301
|
+
if (path !== "/callback") {
|
|
302
|
+
if (path === "/") {
|
|
303
|
+
const rootResponse = phase === "terminal" && finalResponse ? finalResponse : {
|
|
304
|
+
status: 200,
|
|
305
|
+
message: "Waiting for the Harness browser authorization to finish.",
|
|
306
|
+
pageState: "waiting"
|
|
307
|
+
};
|
|
308
|
+
void trackResponse(
|
|
309
|
+
respondAtRoot(rootResponse.status, rootResponse.message, rootResponse.pageState)
|
|
310
|
+
);
|
|
311
|
+
} else {
|
|
312
|
+
respondEmpty(response, path === "/favicon.ico" ? 204 : 404);
|
|
313
|
+
}
|
|
220
314
|
return;
|
|
221
315
|
}
|
|
222
|
-
|
|
223
|
-
|
|
316
|
+
const matchingState = url.searchParams.get("state") === state2;
|
|
317
|
+
if (!matchingState) {
|
|
318
|
+
respondEmpty(response, 400);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (phase === "terminal") {
|
|
322
|
+
if (!url.searchParams.has("code") && !url.searchParams.has("error")) {
|
|
323
|
+
respondEmpty(response, 400);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const result = finalResponse ?? {
|
|
327
|
+
status: 410,
|
|
328
|
+
message: "This login attempt has ended \u2014 run harness login again if needed.",
|
|
329
|
+
pageState: "error"
|
|
330
|
+
};
|
|
331
|
+
void trackResponse(respondWithPage(result.status, result.message, result.pageState));
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
if (phase === "processing") {
|
|
335
|
+
if (url.searchParams.has("error")) {
|
|
336
|
+
respondEmpty(response, 409);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
const duplicateCode = url.searchParams.get("code");
|
|
340
|
+
if (duplicateCode === null || duplicateCode === "") {
|
|
341
|
+
respondEmpty(response, 400);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
if (acceptedResponders.length >= MAX_PAGE_RESPONSES) {
|
|
345
|
+
respondEmpty(response, 429);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
acceptedResponders.push(respondWithPage);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
if (url.searchParams.has("error")) {
|
|
352
|
+
const denialResponse = {
|
|
353
|
+
status: 400,
|
|
354
|
+
message: "Browser authorization was denied. Return to the terminal to try again.",
|
|
355
|
+
pageState: "error"
|
|
356
|
+
};
|
|
357
|
+
phase = "terminal";
|
|
358
|
+
finalResponse = denialResponse;
|
|
359
|
+
clearTimeout(timer);
|
|
360
|
+
void (async () => {
|
|
361
|
+
await trackResponse(respondWithPage(denialResponse.status, denialResponse.message, denialResponse.pageState));
|
|
362
|
+
await drainTrackedResponses();
|
|
363
|
+
resolve({ ok: false, message: "browser authorization was denied" });
|
|
364
|
+
})();
|
|
224
365
|
return;
|
|
225
366
|
}
|
|
226
367
|
const code = url.searchParams.get("code");
|
|
227
|
-
if (
|
|
228
|
-
|
|
368
|
+
if (code !== null && code !== "") {
|
|
369
|
+
phase = "processing";
|
|
370
|
+
acceptedResponders.push(respondWithPage);
|
|
229
371
|
clearTimeout(timer);
|
|
230
|
-
resolve({
|
|
372
|
+
resolve({
|
|
373
|
+
ok: true,
|
|
374
|
+
code,
|
|
375
|
+
respond: async (status, message, pageState) => {
|
|
376
|
+
finalResponse = { status, message, pageState };
|
|
377
|
+
phase = "terminal";
|
|
378
|
+
const responders = acceptedResponders.splice(0);
|
|
379
|
+
await Promise.all(
|
|
380
|
+
responders.map((acceptedRespond) => trackResponse(acceptedRespond(status, message, pageState)))
|
|
381
|
+
);
|
|
382
|
+
await drainTrackedResponses();
|
|
383
|
+
}
|
|
384
|
+
});
|
|
231
385
|
} else {
|
|
232
|
-
|
|
386
|
+
respondEmpty(response, 400);
|
|
233
387
|
}
|
|
234
388
|
});
|
|
235
389
|
});
|
|
236
390
|
}
|
|
391
|
+
function callbackResponder(response) {
|
|
392
|
+
return async (status, message, state2) => {
|
|
393
|
+
if (response.writableEnded || response.destroyed) return;
|
|
394
|
+
return new Promise((resolve) => {
|
|
395
|
+
let flushTimer;
|
|
396
|
+
const armFlushTimeout = () => {
|
|
397
|
+
if (flushTimer !== void 0) clearTimeout(flushTimer);
|
|
398
|
+
flushTimer = setTimeout(() => {
|
|
399
|
+
response.destroy();
|
|
400
|
+
settle();
|
|
401
|
+
}, RESPONSE_FLUSH_TIMEOUT_MS);
|
|
402
|
+
flushTimer.unref();
|
|
403
|
+
};
|
|
404
|
+
const settle = () => {
|
|
405
|
+
if (flushTimer !== void 0) clearTimeout(flushTimer);
|
|
406
|
+
response.off("finish", settle);
|
|
407
|
+
response.off("close", settle);
|
|
408
|
+
response.off("error", settle);
|
|
409
|
+
response.off("drain", armFlushTimeout);
|
|
410
|
+
resolve();
|
|
411
|
+
};
|
|
412
|
+
response.once("finish", settle);
|
|
413
|
+
response.once("close", settle);
|
|
414
|
+
response.once("error", settle);
|
|
415
|
+
response.on("drain", armFlushTimeout);
|
|
416
|
+
void loadLoginPage().then(({ loginPage }) => {
|
|
417
|
+
if (response.writableEnded || response.destroyed) {
|
|
418
|
+
settle();
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
const html = loginPage(message, state2);
|
|
423
|
+
response.writeHead(status, {
|
|
424
|
+
"cache-control": "no-store",
|
|
425
|
+
connection: "close",
|
|
426
|
+
"content-length": String(Buffer.byteLength(html)),
|
|
427
|
+
"content-type": "text/html; charset=utf-8",
|
|
428
|
+
"x-content-type-options": "nosniff"
|
|
429
|
+
});
|
|
430
|
+
armFlushTimeout();
|
|
431
|
+
response.end(html);
|
|
432
|
+
} catch {
|
|
433
|
+
response.destroy();
|
|
434
|
+
settle();
|
|
435
|
+
}
|
|
436
|
+
}).catch(() => {
|
|
437
|
+
response.destroy();
|
|
438
|
+
settle();
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
function respondEmpty(response, status) {
|
|
444
|
+
response.once("error", () => void 0);
|
|
445
|
+
try {
|
|
446
|
+
response.writeHead(status, {
|
|
447
|
+
"cache-control": "no-store",
|
|
448
|
+
connection: "close",
|
|
449
|
+
"x-content-type-options": "nosniff"
|
|
450
|
+
});
|
|
451
|
+
response.end();
|
|
452
|
+
} catch {
|
|
453
|
+
response.destroy();
|
|
454
|
+
}
|
|
455
|
+
}
|
|
237
456
|
async function exchangeCode(baseUrl, code, verifier, options) {
|
|
238
457
|
try {
|
|
239
|
-
const body = await bareClient(baseUrl, "", options).post(
|
|
240
|
-
"/api/v1/cli/token-exchange",
|
|
241
|
-
{ code, code_verifier: verifier }
|
|
242
|
-
);
|
|
458
|
+
const body = await bareClient(baseUrl, "", options).post("/api/v1/cli/token-exchange", { code, code_verifier: verifier });
|
|
243
459
|
const token = body.data?.token;
|
|
244
460
|
if (typeof token === "string" && token !== "") return { ok: true, token };
|
|
245
|
-
return {
|
|
461
|
+
return {
|
|
462
|
+
ok: false,
|
|
463
|
+
message: "the login-code exchange returned no token \u2014 aborting"
|
|
464
|
+
};
|
|
246
465
|
} catch (error) {
|
|
247
466
|
if (error instanceof HttpError) {
|
|
248
|
-
return {
|
|
467
|
+
return {
|
|
468
|
+
ok: false,
|
|
469
|
+
message: `the login-code exchange failed: ${error.message}`
|
|
470
|
+
};
|
|
249
471
|
}
|
|
250
472
|
if (error instanceof TransportError) {
|
|
251
|
-
return {
|
|
473
|
+
return {
|
|
474
|
+
ok: false,
|
|
475
|
+
message: `the login-code exchange failed: cannot reach the API (${error.message})`
|
|
476
|
+
};
|
|
252
477
|
}
|
|
253
478
|
throw error;
|
|
254
479
|
}
|
|
@@ -288,7 +513,10 @@ async function verifyAndStore(baseUrl, token, options) {
|
|
|
288
513
|
} catch (error) {
|
|
289
514
|
await client.delete("/api/v1/session").catch(() => void 0);
|
|
290
515
|
const detail = error instanceof Error ? error.message : String(error);
|
|
291
|
-
return {
|
|
516
|
+
return {
|
|
517
|
+
ok: false,
|
|
518
|
+
message: `could not store credentials at ${path}: ${detail}`
|
|
519
|
+
};
|
|
292
520
|
}
|
|
293
521
|
if (superseded !== null) {
|
|
294
522
|
await bareClient(superseded.url, superseded.token, options).delete("/api/v1/session").catch(() => void 0);
|
|
@@ -378,7 +606,10 @@ function defaultNotify(line) {
|
|
|
378
606
|
process.stderr.write(line + "\n");
|
|
379
607
|
}
|
|
380
608
|
function defaultPrompt() {
|
|
381
|
-
const readline = createInterface({
|
|
609
|
+
const readline = createInterface({
|
|
610
|
+
input: process.stdin,
|
|
611
|
+
output: process.stderr
|
|
612
|
+
});
|
|
382
613
|
return new Promise((resolve) => {
|
|
383
614
|
readline.question("Token: ", (answer) => {
|
|
384
615
|
readline.close();
|