rubric-chat 0.2.2 → 0.3.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.
@@ -1,110 +1,68 @@
1
1
  import kleur from "kleur";
2
- import openBrowser from "open";
3
2
  import prompts from "prompts";
4
- import { fetchMe, requestMagicLink, verifyMagicLink } from "../lib/api.js";
5
- import { getWebBaseUrl, storeToken } from "../lib/config.js";
6
- import { startLocalCallback } from "../lib/local-server.js";
3
+ import { fetchMe, requestMagicLink, verifyOtp } from "../lib/api.js";
4
+ import { storeToken } from "../lib/config.js";
7
5
  /**
8
- * Runs the local-callback login flow with a manual paste-token fallback.
9
- * Returns true if the user ends up signed in, false otherwise.
6
+ * OTP login. Pure terminal no browser, no local server, no port races.
7
+ *
8
+ * 1. Ask for email (or use --email).
9
+ * 2. Backend sends an email with a 6-digit code.
10
+ * 3. User types the code into the terminal.
11
+ * 4. CLI exchanges it for a JWT and stores it in the keychain.
10
12
  */
11
13
  export async function runLogin(options = {}) {
12
14
  const email = await ensureEmail(options.email);
13
15
  if (!email) {
14
- // User hit Escape or Ctrl-C at the prompt — exit cleanly, no error.
15
16
  console.log(kleur.dim("Cancelled. Run `rubric-chat login` when you're ready."));
16
17
  return false;
17
18
  }
18
- if (!options.noBrowser) {
19
- try {
20
- const ok = await loginViaBrowserCallback(email);
21
- if (ok)
22
- return true;
23
- }
24
- catch (err) {
25
- console.error(kleur.yellow(`Browser flow couldn't complete: ${err.message}`));
26
- console.error(kleur.dim("Falling back to manual token entry…"));
27
- }
28
- }
29
- return loginViaManualToken(email);
30
- }
31
- async function ensureEmail(provided) {
32
- if (provided)
33
- return provided;
34
- const response = await prompts({
35
- type: "text",
36
- name: "email",
37
- message: "Email"
38
- });
39
- return response.email;
40
- }
41
- async function loginViaBrowserCallback(email) {
42
- const callback = startLocalCallback();
43
- try {
44
- await requestMagicLink(email, {
45
- callbackUrl: callback.callbackUrl,
46
- callbackState: callback.state
47
- });
48
- }
49
- catch (err) {
50
- callback.close();
51
- throw err;
52
- }
53
- console.log(kleur.cyan("\nOpening browser to ") +
54
- kleur.bold(email) +
55
- kleur.cyan("'s magic link…"));
56
- console.log(kleur.dim(`Listening on ${callback.callbackUrl} (state ${callback.state.slice(0, 8)}…)`));
57
- // The magic-link email is the actual entry point; we open the inbox-agnostic
58
- // login page so the user knows what to expect while they wait for the email.
59
- void openBrowser(`${getWebBaseUrl()}/auth/login`).catch(() => undefined);
60
- try {
61
- const result = await callback.wait();
62
- await storeToken(result.jwt);
63
- const me = await fetchMe();
64
- console.log(kleur.green(`\nSigned in as ${me.email}.`));
65
- return true;
66
- }
67
- catch (err) {
68
- if (err.message.includes("Timed out")) {
69
- console.error(kleur.yellow("\nBrowser sign-in timed out."));
70
- return false;
71
- }
72
- throw err;
73
- }
74
- }
75
- async function loginViaManualToken(email) {
76
- console.log(kleur.cyan("\nWe'll send a magic link to ") + kleur.bold(email) + kleur.cyan("."));
77
19
  try {
78
- await requestMagicLink(email);
20
+ await requestMagicLink(email, { authType: "otp" });
79
21
  }
80
22
  catch (err) {
81
- console.error(kleur.red(`Could not request magic link: ${err.message}`));
23
+ console.error(kleur.red(`Couldn't send the sign-in code: ${err.message}`));
82
24
  process.exitCode = 1;
83
25
  return false;
84
26
  }
85
- console.log(kleur.dim("Open it, then paste the `token:` value back here."));
86
- const tokenAnswer = await prompts({
27
+ console.log("\n " + kleur.cyan("Sent a 6-digit code to ") + kleur.bold(email) + kleur.cyan("."));
28
+ console.log(kleur.dim(" The code expires in 30 minutes.\n"));
29
+ const codeAnswer = await prompts({
87
30
  type: "text",
88
- name: "token",
89
- message: "Magic-link token"
31
+ name: "code",
32
+ message: "Sign-in code",
33
+ validate: (value) => {
34
+ const cleaned = (value ?? "").replace(/\D/g, "");
35
+ return cleaned.length === 6 || "Code must be 6 digits.";
36
+ }
90
37
  });
91
- const token = tokenAnswer.token?.trim();
92
- if (!token) {
93
- console.error(kleur.red("Token is required."));
94
- process.exitCode = 1;
38
+ const code = codeAnswer.code?.replace(/\D/g, "");
39
+ if (!code) {
40
+ console.log(kleur.dim("Cancelled. Run `rubric-chat login` to request a new code."));
95
41
  return false;
96
42
  }
97
43
  try {
98
- const session = await verifyMagicLink(token);
44
+ const session = await verifyOtp(email, code);
99
45
  await storeToken(session.jwt);
100
46
  const me = await fetchMe();
101
- console.log(kleur.green(`Signed in as ${me.email}.`));
47
+ console.log(kleur.green(`\nSigned in as ${me.email}.\n`));
102
48
  return true;
103
49
  }
104
50
  catch (err) {
105
- console.error(kleur.red(`Verification failed: ${err.message}`));
51
+ console.error(kleur.red(`\nVerification failed: ${err.message}`));
52
+ console.error(kleur.dim("Double-check the code, or run `rubric-chat login` again for a fresh one."));
106
53
  process.exitCode = 1;
107
54
  return false;
108
55
  }
109
56
  }
57
+ async function ensureEmail(provided) {
58
+ if (provided)
59
+ return provided;
60
+ const response = await prompts({
61
+ type: "text",
62
+ name: "email",
63
+ message: "Email",
64
+ validate: (value) => /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test((value ?? "").trim()) || "That doesn't look like an email."
65
+ });
66
+ return response.email?.trim();
67
+ }
110
68
  //# sourceMappingURL=login.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
package/dist/lib/api.js CHANGED
@@ -16,14 +16,17 @@ export async function requestMagicLink(email, opts = {}) {
16
16
  headers: { "content-type": "application/json" },
17
17
  body: JSON.stringify({
18
18
  email,
19
- callback_url: opts.callbackUrl ?? null,
20
- callback_state: opts.callbackState ?? null
19
+ auth_type: opts.authType ?? "link"
21
20
  })
22
21
  });
23
22
  await unwrap(response);
24
23
  }
25
- export async function verifyMagicLink(token) {
26
- const response = await fetch(`${getApiBaseUrl()}/auth/verify?token=${encodeURIComponent(token)}`);
24
+ export async function verifyOtp(email, code) {
25
+ const response = await fetch(`${getApiBaseUrl()}/auth/verify-otp`, {
26
+ method: "POST",
27
+ headers: { "content-type": "application/json" },
28
+ body: JSON.stringify({ email, code })
29
+ });
27
30
  return unwrap(response);
28
31
  }
29
32
  export async function fetchMe() {
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEvD,KAAK,UAAU,WAAW;IACxB,MAAM,GAAG,GAAG,MAAM,SAAS,EAAE,CAAC;IAC9B,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACvD,CAAC;AAED,KAAK,UAAU,MAAM,CAAI,QAAkB;IACzC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,MAAM,IAAI,mBAAmB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAM,CAAC;AACtC,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAa,EAAE,OAAsB,EAAE;IAC5E,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,aAAa,EAAE,kBAAkB,EAAE;QACjE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,KAAK;YACL,YAAY,EAAE,IAAI,CAAC,WAAW,IAAI,IAAI;YACtC,cAAc,EAAE,IAAI,CAAC,aAAa,IAAI,IAAI;SAC3C,CAAC;KACH,CAAC,CAAC;IACH,MAAM,MAAM,CAAkB,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAa;IACjD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAC1B,GAAG,aAAa,EAAE,sBAAsB,kBAAkB,CAAC,KAAK,CAAC,EAAE,CACpE,CAAC;IACF,OAAO,MAAM,CAAsB,QAAQ,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO;IAC3B,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,aAAa,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IACxE,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAA0B;IAC7D,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,aAAa,EAAE,UAAU,EAAE;QACzD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,GAAG,OAAO,EAAE;QAC3D,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KAC9B,CAAC,CAAC;IACH,OAAO,MAAM,CAAkB,QAAQ,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAAU;IAC1C,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,aAAa,EAAE,YAAY,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC9E,OAAO,MAAM,CAAS,QAAQ,CAAC,CAAC;AAClC,CAAC;AAUD,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,aAAa,EAAE,iBAAiB,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC/E,OAAO,MAAM,CAAgB,QAAQ,CAAC,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEvD,KAAK,UAAU,WAAW;IACxB,MAAM,GAAG,GAAG,MAAM,SAAS,EAAE,CAAC;IAC9B,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACvD,CAAC;AAED,KAAK,UAAU,MAAM,CAAI,QAAkB;IACzC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,MAAM,IAAI,mBAAmB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAM,CAAC;AACtC,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAa,EAAE,OAAsB,EAAE;IAC5E,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,aAAa,EAAE,kBAAkB,EAAE;QACjE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,KAAK;YACL,SAAS,EAAE,IAAI,CAAC,QAAQ,IAAI,MAAM;SACnC,CAAC;KACH,CAAC,CAAC;IACH,MAAM,MAAM,CAAkB,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAa,EAAE,IAAY;IACzD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,aAAa,EAAE,kBAAkB,EAAE;QACjE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;KACtC,CAAC,CAAC;IACH,OAAO,MAAM,CAAsB,QAAQ,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO;IAC3B,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,aAAa,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IACxE,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAA0B;IAC7D,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,aAAa,EAAE,UAAU,EAAE;QACzD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,GAAG,OAAO,EAAE;QAC3D,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KAC9B,CAAC,CAAC;IACH,OAAO,MAAM,CAAkB,QAAQ,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAAU;IAC1C,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,aAAa,EAAE,YAAY,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC9E,OAAO,MAAM,CAAS,QAAQ,CAAC,CAAC;AAClC,CAAC;AAUD,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,aAAa,EAAE,iBAAiB,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC/E,OAAO,MAAM,CAAgB,QAAQ,CAAC,CAAC;AACzC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rubric-chat",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "A strict 0–100 score for AI conversations. Auto-discovers Claude Code, Codex CLI, and Cursor sessions; also accepts ChatGPT and Claude.ai exports. Six dimensions, eight archetypes, shareable score card.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,126 +0,0 @@
1
- import { createServer } from "node:http";
2
- import { randomBytes } from "node:crypto";
3
- const SUCCESS_HTML = `<!doctype html><html><head><meta charset="utf-8"><title>Rubric — Signed in</title>
4
- <style>
5
- :root { color-scheme: dark; }
6
- html, body { margin: 0; height: 100%; font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; background: #0C0B0A; color: #F2EEE3; }
7
- main { min-height: 100%; display: grid; place-items: center; padding: 24px; }
8
- .card { max-width: 420px; padding: 28px 30px; border: 1px solid #2A271F; border-radius: 16px; background: #17150F; box-shadow: 0 12px 28px -16px rgba(0,0,0,.65); }
9
- .eyebrow { font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: #857E73; margin: 0 0 8px; }
10
- h1 { font-family: ui-serif, Georgia, serif; font-weight: 500; font-size: 26px; margin: 0 0 14px; }
11
- p { color: #B7B1A1; line-height: 1.55; margin: 0 0 12px; font-size: 15px; }
12
- code { background: #1F1C16; border: 1px solid #2A271F; border-radius: 6px; padding: 2px 6px; font-size: 12px; color: #F2EEE3; }
13
- </style></head><body><main><div class="card">
14
- <p class="eyebrow">Rubric CLI</p>
15
- <h1>You can close this tab.</h1>
16
- <p>Your terminal session has picked up the credentials. Head back to <code>rubric-chat</code>.</p>
17
- </div></main></body></html>`;
18
- const FAILURE_HTML = `<!doctype html><html><head><meta charset="utf-8"><title>Rubric — Bad request</title></head>
19
- <body style="margin:0;font-family:system-ui;background:#0C0B0A;color:#F2EEE3;display:grid;place-items:center;height:100vh">
20
- <div style="max-width:420px;padding:24px;border:1px solid #9B2C2C;border-radius:12px;background:#17150F">
21
- <h1 style="font-size:18px;margin:0 0 10px">Something looked off.</h1>
22
- <p style="color:#B7B1A1;font-size:14px;line-height:1.55;margin:0">The CLI couldn't verify this response. Try running <code>rubric-chat login</code> again.</p>
23
- </div></body></html>`;
24
- function setCors(res) {
25
- res.setHeader("Access-Control-Allow-Origin", "*");
26
- res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
27
- res.setHeader("Access-Control-Allow-Headers", "Content-Type");
28
- }
29
- async function readJson(req) {
30
- return new Promise((resolve, reject) => {
31
- const chunks = [];
32
- req.on("data", (chunk) => chunks.push(chunk));
33
- req.on("end", () => {
34
- const raw = Buffer.concat(chunks).toString("utf8") || "{}";
35
- try {
36
- resolve(JSON.parse(raw));
37
- }
38
- catch (err) {
39
- reject(err);
40
- }
41
- });
42
- req.on("error", reject);
43
- });
44
- }
45
- /**
46
- * Spawn a one-shot HTTP server on a random localhost port. The CLI passes its
47
- * URL + state to the backend with the magic-link request; the verify page in
48
- * the browser POSTs `{jwt, state, user}` here after a successful sign-in.
49
- */
50
- export function startLocalCallback(timeoutMs = 5 * 60 * 1000) {
51
- const state = randomBytes(16).toString("hex");
52
- let resolveResult = null;
53
- let rejectResult = null;
54
- const resultPromise = new Promise((resolve, reject) => {
55
- resolveResult = resolve;
56
- rejectResult = reject;
57
- });
58
- const server = createServer((req, res) => {
59
- if (req.method === "OPTIONS") {
60
- setCors(res);
61
- res.writeHead(204);
62
- res.end();
63
- return;
64
- }
65
- if (req.method !== "POST" || !req.url?.startsWith("/auth")) {
66
- res.writeHead(404, { "content-type": "text/plain" });
67
- res.end("not found");
68
- return;
69
- }
70
- setCors(res);
71
- readJson(req)
72
- .then((payload) => {
73
- if (!payload || typeof payload !== "object") {
74
- res.writeHead(400, { "content-type": "text/html" });
75
- res.end(FAILURE_HTML);
76
- return;
77
- }
78
- const body = payload;
79
- if (body.state !== state) {
80
- res.writeHead(400, { "content-type": "text/html" });
81
- res.end(FAILURE_HTML);
82
- rejectResult?.(new Error("CSRF state mismatch"));
83
- return;
84
- }
85
- if (typeof body.jwt !== "string" || !body.jwt) {
86
- res.writeHead(400, { "content-type": "text/html" });
87
- res.end(FAILURE_HTML);
88
- rejectResult?.(new Error("Missing jwt in callback"));
89
- return;
90
- }
91
- res.writeHead(200, { "content-type": "text/html" });
92
- res.end(SUCCESS_HTML);
93
- resolveResult?.({
94
- jwt: body.jwt,
95
- user: body.user
96
- });
97
- })
98
- .catch((err) => {
99
- res.writeHead(400, { "content-type": "text/html" });
100
- res.end(FAILURE_HTML);
101
- rejectResult?.(err instanceof Error ? err : new Error(String(err)));
102
- });
103
- });
104
- server.listen(0, "127.0.0.1");
105
- const port = server.address().port;
106
- const callbackUrl = `http://127.0.0.1:${port}/auth`;
107
- const timer = setTimeout(() => {
108
- rejectResult?.(new Error("Timed out waiting for browser callback"));
109
- server.close();
110
- }, timeoutMs);
111
- const close = () => {
112
- clearTimeout(timer);
113
- server.close();
114
- };
115
- const wait = async () => {
116
- try {
117
- const result = await resultPromise;
118
- return result;
119
- }
120
- finally {
121
- close();
122
- }
123
- };
124
- return { port, callbackUrl, state, wait, close };
125
- }
126
- //# sourceMappingURL=local-server.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"local-server.js","sourceRoot":"","sources":["../../src/lib/local-server.ts"],"names":[],"mappings":"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"}