webcake-landing-mcp 1.0.12 → 1.0.13

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/login.js +83 -4
  2. package/package.json +1 -1
@@ -18,7 +18,7 @@
18
18
  */
19
19
  import { createServer } from "node:http";
20
20
  import { randomBytes } from "node:crypto";
21
- import { spawn } from "node:child_process";
21
+ import { spawn, execFile } from "node:child_process";
22
22
  import { saveSavedConfig, resolveEnv, ENVIRONMENTS } from "../persistence/config.js";
23
23
  function parseArgs(argv) {
24
24
  const get = (name) => {
@@ -44,9 +44,83 @@ function openBrowser(url) {
44
44
  /* ignore — the URL is also printed */
45
45
  }
46
46
  }
47
- const SUCCESS_HTML = `<!doctype html><meta charset="utf-8"><title>Connected</title>
48
- <body style="font-family:system-ui;text-align:center;padding:48px">
49
- <h2>✓ Connected to Webcake</h2><p>You can close this tab and return to your terminal.</p></body>`;
47
+ // Chrome won't let a page close a tab it didn't open (window.close() is blocked),
48
+ // so instead we bring the user's terminal back to the foreground from Node. We
49
+ // snapshot whatever app is frontmost just before opening the browser (that's the
50
+ // terminal that ran the command) and re-activate it once the token arrives.
51
+ // macOS only (AppleScript); a no-op elsewhere — the success page still shows.
52
+ function captureFrontmostApp() {
53
+ if (process.platform !== "darwin")
54
+ return Promise.resolve(undefined);
55
+ return new Promise((resolve) => {
56
+ let done = false;
57
+ const finish = (v) => {
58
+ if (done)
59
+ return;
60
+ done = true;
61
+ resolve(v);
62
+ };
63
+ const child = execFile("osascript", ["-e", 'tell application "System Events" to get name of first application process whose frontmost is true'], (err, stdout) => finish(err ? undefined : stdout.trim() || undefined));
64
+ // Never let login stall: the first run may hang on the macOS Automation
65
+ // permission prompt. Give up after 2s (re-focus is just a nicety).
66
+ setTimeout(() => {
67
+ try {
68
+ child.kill();
69
+ }
70
+ catch { /* noop */ }
71
+ finish(undefined);
72
+ }, 2000).unref();
73
+ });
74
+ }
75
+ function activateApp(name) {
76
+ if (!name || process.platform !== "darwin")
77
+ return;
78
+ // Re-focus by process name via System Events (works for terminals whose app
79
+ // name differs from the process, e.g. iTerm/Terminal/Warp/VS Code).
80
+ execFile("osascript", ["-e", `tell application "System Events" to set frontmost of (first application process whose name is "${name.replace(/"/g, '\\"')}") to true`], () => { });
81
+ }
82
+ const SUCCESS_HTML = `<!doctype html><html lang="en"><head><meta charset="utf-8">
83
+ <meta name="viewport" content="width=device-width,initial-scale=1"><title>Connected to Webcake</title>
84
+ <style>
85
+ :root{color-scheme:light dark}
86
+ *{box-sizing:border-box}
87
+ body{margin:0;min-height:100vh;display:flex;align-items:center;justify-content:center;
88
+ font-family:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
89
+ background:linear-gradient(135deg,#eef2ff 0%,#faf5ff 100%);color:#1e293b}
90
+ @media(prefers-color-scheme:dark){body{background:linear-gradient(135deg,#0f172a 0%,#1e1b4b 100%);color:#e2e8f0}}
91
+ .card{background:#fff;border-radius:20px;padding:48px 40px;max-width:420px;width:calc(100% - 32px);
92
+ text-align:center;box-shadow:0 20px 60px rgba(79,70,229,.18);animation:rise .5s cubic-bezier(.2,.8,.2,1)}
93
+ @media(prefers-color-scheme:dark){.card{background:#1e293b;box-shadow:0 20px 60px rgba(0,0,0,.5)}}
94
+ @keyframes rise{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}
95
+ .badge{width:84px;height:84px;margin:0 auto 24px;border-radius:50%;display:flex;align-items:center;justify-content:center;
96
+ background:linear-gradient(135deg,#22c55e,#16a34a);box-shadow:0 8px 24px rgba(34,197,94,.4);animation:pop .45s .15s both cubic-bezier(.2,1.4,.4,1)}
97
+ @keyframes pop{from{transform:scale(0)}to{transform:scale(1)}}
98
+ .badge svg{width:44px;height:44px;stroke:#fff;stroke-width:3.5;fill:none;stroke-linecap:round;stroke-linejoin:round}
99
+ .badge path{stroke-dasharray:32;stroke-dashoffset:32;animation:draw .4s .4s forwards ease-out}
100
+ @keyframes draw{to{stroke-dashoffset:0}}
101
+ h1{margin:0 0 10px;font-size:1.55rem;font-weight:700}
102
+ p{margin:0;font-size:1rem;line-height:1.6;color:#64748b}
103
+ @media(prefers-color-scheme:dark){p{color:#94a3b8}}
104
+ .hint{margin-top:24px;display:inline-flex;align-items:center;gap:8px;padding:10px 16px;border-radius:10px;
105
+ background:#f1f5f9;font-size:.9rem;color:#475569}
106
+ @media(prefers-color-scheme:dark){.hint{background:#0f172a;color:#94a3b8}}
107
+ .hint code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:600;color:#4f46e5}
108
+ @media(prefers-color-scheme:dark){.hint code{color:#a5b4fc}}
109
+ </style></head>
110
+ <body>
111
+ <main class="card">
112
+ <div class="badge"><svg viewBox="0 0 24 24"><path d="M5 13l4 4L19 7"/></svg></div>
113
+ <h1>Connected to Webcake</h1>
114
+ <p>Your account is linked. You can close this tab now.</p>
115
+ <div class="hint">👉 Return to your <code>terminal</code> to continue</div>
116
+ </main>
117
+ <script>
118
+ // The terminal is re-focused from the CLI side. Still try window.close() for
119
+ // browsers that allow it (no-op in Chrome for tabs it didn't open) — no alert,
120
+ // which would only steal focus back from the terminal.
121
+ setTimeout(function(){ try { window.close(); } catch (e) {} }, 800);
122
+ </script>
123
+ </body></html>`;
50
124
  function resolveConnectUrl(opts, appBase) {
51
125
  if (opts.connectUrl)
52
126
  return opts.connectUrl; // explicit --connect-url override
@@ -62,6 +136,9 @@ export async function runLogin(argv) {
62
136
  const appBase = process.env.WEBCAKE_APP_BASE || preset.appBase;
63
137
  const connectUrl = resolveConnectUrl(opts, appBase);
64
138
  const state = randomBytes(16).toString("hex");
139
+ // Remember the terminal that's frontmost now, so we can re-focus it once the
140
+ // browser hands the token back (Chrome can't auto-close its own tab).
141
+ const terminalApp = await captureFrontmostApp();
65
142
  await new Promise((resolve, reject) => {
66
143
  const server = createServer((req, res) => {
67
144
  const url = new URL(req.url || "/", "http://127.0.0.1");
@@ -83,6 +160,8 @@ export async function runLogin(argv) {
83
160
  });
84
161
  res.writeHead(200, { "content-type": "text/html" }).end(SUCCESS_HTML);
85
162
  console.error(`\n✓ Connected. Token saved to ${path} (api ${apiBase}).`);
163
+ // Pull the terminal back to the front (best-effort; no-op off macOS).
164
+ activateApp(terminalApp);
86
165
  server.close();
87
166
  resolve();
88
167
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webcake-landing-mcp",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
4
4
  "description": "MCP server exposing Webcake landing-page element schemas + AI usage hints, and persisting LLM-generated page sources to a Webcake backend.",
5
5
  "type": "module",
6
6
  "bin": {