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.
Files changed (2) hide show
  1. package/dist/auth.js +50 -5
  2. 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
- "<html>",
33
+ '<html lang="en">',
34
34
  "<head>",
35
35
  '<meta charset="utf-8">',
36
- "<title>Salesprompter CLI login complete</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
- "<p>Salesprompter CLI login complete. You can return to the terminal.</p>",
60
+ '<main class="card" aria-live="polite">',
61
+ '<div class="mark" aria-hidden="true">&#10003;</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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "salesprompter-cli",
3
- "version": "0.1.62",
3
+ "version": "0.1.63",
4
4
  "description": "Sales workflow CLI for guided lead generation, enrichment, scoring, and sync.",
5
5
  "author": "Daniel Sinewe <hello@danielsinewe.com>",
6
6
  "type": "module",