salesprompter-cli 0.1.62 → 0.1.63
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/auth.js +50 -5
- package/package.json +1 -1
package/dist/auth.js
CHANGED
|
@@ -30,23 +30,55 @@ const AuthSessionSchema = z.object({
|
|
|
30
30
|
function buildBrowserCallbackSuccessHtml() {
|
|
31
31
|
return [
|
|
32
32
|
"<!doctype html>",
|
|
33
|
-
|
|
33
|
+
'<html lang="en">',
|
|
34
34
|
"<head>",
|
|
35
35
|
'<meta charset="utf-8">',
|
|
36
|
-
"<title>
|
|
36
|
+
"<title>Connected to Salesprompter</title>",
|
|
37
37
|
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
38
|
+
'<meta name="color-scheme" content="light dark">',
|
|
39
|
+
"<style>",
|
|
40
|
+
":root{font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;color-scheme:light dark}",
|
|
41
|
+
"*{box-sizing:border-box}",
|
|
42
|
+
"body{margin:0;min-height:100vh;display:grid;place-items:center;background:#f7f8fa;color:#172033;padding:24px}",
|
|
43
|
+
".card{width:min(420px,100%);background:#fff;border:1px solid #e4e8ef;border-radius:20px;padding:36px;box-shadow:0 20px 60px rgba(23,32,51,.10);text-align:center}",
|
|
44
|
+
".mark{width:52px;height:52px;margin:0 auto 20px;display:grid;place-items:center;border-radius:50%;background:#e9f7ee;color:#18753c;font-size:26px;font-weight:800}",
|
|
45
|
+
"h1{margin:0 0 10px;font-size:24px;line-height:1.2;letter-spacing:-.02em}",
|
|
46
|
+
"p{margin:0;color:#667085;font-size:15px;line-height:1.55}",
|
|
47
|
+
".hint{margin-top:18px;font-size:13px;color:#98a2b3}",
|
|
48
|
+
"@media(prefers-color-scheme:dark){body{background:#111318;color:#f7f8fa}.card{background:#1b1f27;border-color:#303643;box-shadow:none}p{color:#b7c0ce}.hint{color:#8791a2}.mark{background:#153b27;color:#78d89a}}",
|
|
49
|
+
"</style>",
|
|
38
50
|
"<script>",
|
|
39
51
|
"if (window.history && typeof window.history.replaceState === 'function') {",
|
|
40
52
|
" window.history.replaceState(null, document.title, window.location.pathname);",
|
|
41
53
|
"}",
|
|
54
|
+
"window.addEventListener('load', function () {",
|
|
55
|
+
" window.setTimeout(function () { window.close(); }, 700);",
|
|
56
|
+
"});",
|
|
42
57
|
"</script>",
|
|
43
58
|
"</head>",
|
|
44
59
|
"<body>",
|
|
45
|
-
|
|
60
|
+
'<main class="card" aria-live="polite">',
|
|
61
|
+
'<div class="mark" aria-hidden="true">✓</div>',
|
|
62
|
+
"<h1>You're connected</h1>",
|
|
63
|
+
"<p>Salesprompter is ready in your terminal.</p>",
|
|
64
|
+
'<p class="hint">This tab will close automatically.</p>',
|
|
65
|
+
"</main>",
|
|
46
66
|
"</body>",
|
|
47
67
|
"</html>"
|
|
48
68
|
].join("");
|
|
49
69
|
}
|
|
70
|
+
function isSpeculativeBrowserRequest(request) {
|
|
71
|
+
const purposeHeaders = [
|
|
72
|
+
request.headers.purpose,
|
|
73
|
+
request.headers["sec-purpose"],
|
|
74
|
+
request.headers["x-purpose"]
|
|
75
|
+
]
|
|
76
|
+
.flatMap((value) => (Array.isArray(value) ? value : [value]))
|
|
77
|
+
.filter((value) => typeof value === "string")
|
|
78
|
+
.join(" ")
|
|
79
|
+
.toLowerCase();
|
|
80
|
+
return purposeHeaders.includes("prefetch") || request.headers["next-router-prefetch"] === "1";
|
|
81
|
+
}
|
|
50
82
|
const DeviceStartResponseSchema = z.object({
|
|
51
83
|
deviceCode: z.string().min(1),
|
|
52
84
|
userCode: z.string().min(1),
|
|
@@ -264,17 +296,30 @@ export async function loginWithBrowserConnect(options) {
|
|
|
264
296
|
response.end("Not found");
|
|
265
297
|
return;
|
|
266
298
|
}
|
|
299
|
+
response.setHeader("Cache-Control", "no-store, max-age=0");
|
|
300
|
+
response.setHeader("Pragma", "no-cache");
|
|
301
|
+
response.setHeader("Referrer-Policy", "no-referrer");
|
|
302
|
+
response.setHeader("X-Content-Type-Options", "nosniff");
|
|
303
|
+
if (request.method !== "GET" || isSpeculativeBrowserRequest(request)) {
|
|
304
|
+
response.statusCode = 204;
|
|
305
|
+
response.end();
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
267
308
|
const accessToken = requestUrl.searchParams.get("access_token") ?? "";
|
|
268
309
|
const responseState = requestUrl.searchParams.get("state") ?? "";
|
|
269
|
-
if (accessToken.trim().length === 0 || responseState.trim().length === 0) {
|
|
310
|
+
if (accessToken.trim().length === 0 || responseState.trim().length === 0 || responseState !== state) {
|
|
270
311
|
response.statusCode = 400;
|
|
271
312
|
response.end("Invalid login response");
|
|
272
313
|
return;
|
|
273
314
|
}
|
|
274
315
|
response.statusCode = 200;
|
|
275
316
|
response.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
317
|
+
response.setHeader("Connection", "close");
|
|
318
|
+
response.setHeader("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'");
|
|
319
|
+
response.once("finish", () => {
|
|
320
|
+
resolveToken?.({ accessToken, state: responseState });
|
|
321
|
+
});
|
|
276
322
|
response.end(buildBrowserCallbackSuccessHtml());
|
|
277
|
-
resolveToken?.({ accessToken, state: responseState });
|
|
278
323
|
}
|
|
279
324
|
catch (error) {
|
|
280
325
|
response.statusCode = 500;
|
package/package.json
CHANGED