rhai-mcp 0.1.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.
@@ -0,0 +1,361 @@
1
+ import { chromium } from "playwright";
2
+ import { config, debug, clearProfileLock, isProfileLockError } from "./config.js";
3
+ /**
4
+ * Wraps a single persistent Chromium context. The profile dir holds cookies and
5
+ * logged-in sessions, so once a human logs into a service once (via `npm run login`),
6
+ * the agent reuses that session forever after.
7
+ */
8
+ export class BrowserController {
9
+ context = null;
10
+ page = null;
11
+ cdpBrowser = null;
12
+ async ensureStarted() {
13
+ if (this.page && !this.page.isClosed())
14
+ return this.page;
15
+ if (config.cdpUrl) {
16
+ // Attach to a Chrome the user already launched (their real profile + passwords).
17
+ debug(`attaching to Chrome over CDP at ${config.cdpUrl}`);
18
+ this.cdpBrowser = await chromium.connectOverCDP(config.cdpUrl);
19
+ this.context = this.cdpBrowser.contexts()[0] ?? (await this.cdpBrowser.newContext());
20
+ this.page = this.context.pages()[0] ?? (await this.context.newPage());
21
+ this.page.setDefaultTimeout(15_000);
22
+ return this.page;
23
+ }
24
+ debug(`launching browser (profile: ${config.profileDir}, headless: ${config.headless}` +
25
+ `${config.channel ? `, channel: ${config.channel}` : ""})`);
26
+ const launchOpts = {
27
+ headless: config.headless,
28
+ channel: config.channel || undefined,
29
+ viewport: { width: 1280, height: 900 },
30
+ args: ["--disable-blink-features=AutomationControlled"],
31
+ };
32
+ try {
33
+ this.context = await chromium.launchPersistentContext(config.profileDir, launchOpts);
34
+ }
35
+ catch (err) {
36
+ if (!isProfileLockError(err))
37
+ throw err;
38
+ // A previous browser didn't shut down cleanly — clear the stale lock and retry once.
39
+ debug("stale profile lock detected; clearing and retrying");
40
+ clearProfileLock();
41
+ this.context = await chromium.launchPersistentContext(config.profileDir, launchOpts);
42
+ }
43
+ // Reuse the first tab the persistent context opens with.
44
+ this.page = this.context.pages()[0] ?? (await this.context.newPage());
45
+ this.page.setDefaultTimeout(15_000);
46
+ return this.page;
47
+ }
48
+ async close() {
49
+ // In CDP mode, disconnect but leave the user's Chrome running.
50
+ if (this.cdpBrowser) {
51
+ await this.cdpBrowser.close().catch(() => { });
52
+ this.cdpBrowser = null;
53
+ }
54
+ else {
55
+ await this.context?.close().catch(() => { });
56
+ }
57
+ this.context = null;
58
+ this.page = null;
59
+ }
60
+ async navigate(url) {
61
+ const page = await this.ensureStarted();
62
+ await page.goto(url, { waitUntil: "domcontentloaded" });
63
+ // No settle here — the snapshot that follows does the single, capped wait.
64
+ }
65
+ /**
66
+ * The ONE place we wait for the page to settle (called by snapshot). Low-level
67
+ * actions don't wait, so each action incurs exactly one short wait, not two.
68
+ * Busy SPA dashboards rarely reach "networkidle", so the cap is tight.
69
+ */
70
+ async settle() {
71
+ await this.page?.waitForLoadState("domcontentloaded").catch(() => { });
72
+ await this.page?.waitForLoadState("networkidle", { timeout: 700 }).catch(() => { });
73
+ await this.page?.waitForTimeout(120).catch(() => { });
74
+ }
75
+ /**
76
+ * Deterministic auth check. Looks at the SETTLED final URL and the DOM for
77
+ * unambiguous signs of a sign-in screen. Errs toward reporting "not authed"
78
+ * so the agent never proceeds (or claims success) on a page it isn't logged
79
+ * into. Returns the final URL so callers can see exactly where it landed.
80
+ */
81
+ async checkAuth() {
82
+ const page = await this.ensureStarted();
83
+ await this.settle();
84
+ const finalUrl = page.url();
85
+ const authRoute = /(\/sign[-_]?in|\/sign[-_]?up|\/log[-_]?in|\/signin|\/login|\/auth(\/|\b)|accounts\.google\.com|appleid\.apple\.com|login\.microsoftonline|github\.com\/login|[?&](returnto|redirect_to|redirect_uri|next)=)/i.test(finalUrl);
86
+ const probe = await page.evaluate(() => {
87
+ const hasPassword = document.querySelectorAll('input[type="password"]').length > 0;
88
+ const buttons = Array.from(document.querySelectorAll("a,button,[role=button]"));
89
+ const hasAuthButton = buttons.some((e) => /^(continue with |sign in with |log ?in with |sign in|log ?in)\b/i.test((e.textContent ?? "").trim()));
90
+ return { hasPassword, hasAuthButton };
91
+ });
92
+ const loggedOut = authRoute || (probe.hasPassword && probe.hasAuthButton);
93
+ const reason = authRoute
94
+ ? "redirected to a sign-in URL"
95
+ : probe.hasPassword && probe.hasAuthButton
96
+ ? "a sign-in form is on the page"
97
+ : "authenticated";
98
+ return { authed: !loggedOut, finalUrl, reason };
99
+ }
100
+ async goBack() {
101
+ const page = await this.ensureStarted();
102
+ await page.goBack({ waitUntil: "domcontentloaded" }).catch(() => { });
103
+ }
104
+ /**
105
+ * Tags every visible, interactive (or text-bearing) element with a stable ref
106
+ * attribute, then returns a compact list. Refs are only valid until the next
107
+ * snapshot — the agent is told to re-snapshot after any action.
108
+ */
109
+ async snapshot() {
110
+ const page = await this.ensureStarted();
111
+ await page.waitForLoadState("domcontentloaded").catch(() => { });
112
+ await this.settle();
113
+ const elements = await page.evaluate(() => {
114
+ const ATTR = "data-rhai-ref";
115
+ document.querySelectorAll(`[${ATTR}]`).forEach((el) => el.removeAttribute(ATTR));
116
+ const isVisible = (el) => {
117
+ const rect = el.getBoundingClientRect();
118
+ if (rect.width === 0 || rect.height === 0)
119
+ return false;
120
+ const style = window.getComputedStyle(el);
121
+ return style.visibility !== "hidden" && style.display !== "none" && style.opacity !== "0";
122
+ };
123
+ const selector = 'a, button, input, select, textarea, [role="button"], [role="link"], ' +
124
+ '[role="tab"], [role="menuitem"], [role="checkbox"], [role="switch"], ' +
125
+ '[contenteditable="true"], [onclick], summary, label';
126
+ const out = [];
127
+ let ref = 0;
128
+ const MAX = 150; // cap to keep prompts small/fast on dense dashboards
129
+ document.querySelectorAll(selector).forEach((el) => {
130
+ if (ref >= MAX)
131
+ return;
132
+ if (!isVisible(el))
133
+ return;
134
+ const tag = el.tagName.toLowerCase();
135
+ const role = el.getAttribute("role") ?? tag;
136
+ const input = el;
137
+ const name = (el.getAttribute("aria-label") ||
138
+ el.getAttribute("placeholder") ||
139
+ el.getAttribute("name") ||
140
+ el.getAttribute("title") ||
141
+ el.innerText ||
142
+ el.getAttribute("value") ||
143
+ "")
144
+ .replace(/\s+/g, " ")
145
+ .trim()
146
+ .slice(0, 120);
147
+ // Skip noise: invisible labels with no text and no association.
148
+ if (!name && tag !== "input" && tag !== "textarea" && tag !== "select")
149
+ return;
150
+ el.setAttribute(ATTR, String(ref));
151
+ const entry = {
152
+ ref,
153
+ role: input.type ? `${role}[${input.type}]` : role,
154
+ name,
155
+ };
156
+ if (input.value && (tag === "input" || tag === "textarea"))
157
+ entry.value = input.value.slice(0, 80);
158
+ out.push(entry);
159
+ ref++;
160
+ });
161
+ const text = (document.body?.innerText ?? "").replace(/\s+\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim().slice(0, 1200);
162
+ return { elements: out, text };
163
+ });
164
+ return { url: page.url(), title: await page.title(), elements: elements.elements, text: elements.text };
165
+ }
166
+ locator(ref) {
167
+ if (!this.page)
168
+ throw new Error("browser not started");
169
+ return this.page.locator(`[data-rhai-ref="${ref}"]`);
170
+ }
171
+ async click(ref) {
172
+ // No post-wait — the snapshot that follows does the single settle.
173
+ await this.locator(ref).click({ timeout: 15_000 });
174
+ }
175
+ async type(ref, text, submit = false) {
176
+ const loc = this.locator(ref);
177
+ await loc.click({ timeout: 15_000 }).catch(() => { });
178
+ await loc.fill(text);
179
+ if (submit)
180
+ await loc.press("Enter");
181
+ }
182
+ /**
183
+ * Types text via the keyboard into the currently focused element (optionally
184
+ * focusing `ref` first by clicking it). Use for code editors like Monaco /
185
+ * CodeMirror (e.g. the Supabase SQL editor) where `.fill()` doesn't work.
186
+ */
187
+ async typeKeys(text, ref) {
188
+ if (!this.page)
189
+ throw new Error("browser not started");
190
+ if (ref != null)
191
+ await this.locator(ref).click({ timeout: 15_000 }).catch(() => { });
192
+ const mod = process.platform === "darwin" ? "Meta" : "Control";
193
+ // Clear existing content first so re-runs replace rather than append/duplicate.
194
+ await this.page.keyboard.press(`${mod}+A`).catch(() => { });
195
+ await this.page.keyboard.press("Delete").catch(() => { });
196
+ // insertText pastes verbatim (single input event) — no per-key events, so code
197
+ // editors like Monaco/CodeMirror won't auto-close brackets or autocomplete and
198
+ // corrupt the text. This is the reliable way to put SQL into the Supabase editor.
199
+ await this.page.keyboard.insertText(text);
200
+ }
201
+ async select(ref, value) {
202
+ await this.locator(ref).selectOption(value);
203
+ }
204
+ async pressKey(key) {
205
+ if (!this.page)
206
+ throw new Error("browser not started");
207
+ await this.page.keyboard.press(key);
208
+ }
209
+ async wait(ms) {
210
+ await this.page?.waitForTimeout(Math.min(ms, 15_000));
211
+ }
212
+ async waitForText(text, timeoutMs = 120_000) {
213
+ if (!this.page)
214
+ throw new Error("browser not started");
215
+ try {
216
+ // Polls in-browser until the text appears — one model round-trip covers a
217
+ // long operation (e.g. project provisioning) instead of many `wait` steps.
218
+ await this.page.getByText(text, { exact: false }).first().waitFor({ timeout: timeoutMs });
219
+ return true;
220
+ }
221
+ catch {
222
+ return false;
223
+ }
224
+ }
225
+ /** Visible, human-readable text of the page body (trimmed). */
226
+ async readText() {
227
+ const page = await this.ensureStarted();
228
+ const text = await page.evaluate(() => document.body.innerText ?? "");
229
+ return text.replace(/\n{3,}/g, "\n\n").trim().slice(0, 8000);
230
+ }
231
+ async screenshot() {
232
+ const page = await this.ensureStarted();
233
+ const buf = await page.screenshot({ type: "png", fullPage: false });
234
+ return buf.toString("base64");
235
+ }
236
+ // ── Recipes: capture a durable locator for a ref, and replay by it ──────────
237
+ /** Capture a session-stable descriptor for the element at `ref` (for recipes). */
238
+ async captureLocator(ref) {
239
+ if (!this.page)
240
+ return null;
241
+ return (await this.page.evaluate((r) => {
242
+ const el = document.querySelector(`[data-rhai-ref="${r}"]`);
243
+ if (!el)
244
+ return null;
245
+ const tag = el.tagName.toLowerCase();
246
+ const a = (n) => el.getAttribute(n) ?? "";
247
+ // Structural CSS path — always re-resolvable on the same view, even for
248
+ // attribute-less elements (Monaco editor, icon buttons).
249
+ const pathOf = (node) => {
250
+ const parts = [];
251
+ let cur = node;
252
+ let depth = 0;
253
+ while (cur && cur.nodeType === 1 && depth < 8) {
254
+ if (cur.id) {
255
+ parts.unshift(`#${CSS.escape(cur.id)}`);
256
+ break;
257
+ }
258
+ let sel = cur.tagName.toLowerCase();
259
+ const parent = cur.parentElement;
260
+ if (parent) {
261
+ const sameTag = Array.from(parent.children).filter((c) => c.tagName === cur.tagName);
262
+ if (sameTag.length > 1)
263
+ sel += `:nth-of-type(${sameTag.indexOf(cur) + 1})`;
264
+ }
265
+ parts.unshift(sel);
266
+ cur = cur.parentElement;
267
+ depth++;
268
+ }
269
+ return parts.join(" > ");
270
+ };
271
+ let css = null;
272
+ if (el.id)
273
+ css = `#${CSS.escape(el.id)}`;
274
+ else if (a("data-testid"))
275
+ css = `[data-testid="${a("data-testid")}"]`;
276
+ else if (a("name"))
277
+ css = `${tag}[name="${a("name")}"]`;
278
+ else if (a("aria-label"))
279
+ css = `[aria-label="${a("aria-label").replace(/"/g, '\\"')}"]`;
280
+ else
281
+ css = pathOf(el); // structural fallback so replay can always re-locate
282
+ const name = (a("aria-label") || el.innerText || a("placeholder") || a("name") || a("title") || a("value") || "")
283
+ .replace(/\s+/g, " ")
284
+ .trim()
285
+ .slice(0, 80);
286
+ return { css, name, tag };
287
+ }, ref));
288
+ }
289
+ /** Candidate locators to try in order: CSS path, visible text, then aria/placeholder. */
290
+ candidates(loc) {
291
+ if (!this.page || !loc)
292
+ return [];
293
+ const out = [];
294
+ if (loc.css)
295
+ out.push(this.page.locator(loc.css).first());
296
+ if (loc.name) {
297
+ const q = loc.name.replace(/"/g, '\\"');
298
+ out.push(this.page.getByText(loc.name, { exact: false }).first());
299
+ out.push(this.page.locator(`[aria-label="${q}"], [placeholder="${q}"]`).first());
300
+ }
301
+ return out;
302
+ }
303
+ async replayClick(loc) {
304
+ for (const l of this.candidates(loc)) {
305
+ try {
306
+ await l.click({ timeout: 4000 });
307
+ return true;
308
+ }
309
+ catch {
310
+ /* try next candidate */
311
+ }
312
+ }
313
+ return false;
314
+ }
315
+ async replayType(loc, text, submit) {
316
+ for (const l of this.candidates(loc)) {
317
+ try {
318
+ await l.click({ timeout: 4000 }).catch(() => { });
319
+ await l.fill(text);
320
+ if (submit)
321
+ await l.press("Enter");
322
+ return true;
323
+ }
324
+ catch {
325
+ /* try next */
326
+ }
327
+ }
328
+ return false;
329
+ }
330
+ async replayTypeKeys(loc, text) {
331
+ if (!this.page)
332
+ return false;
333
+ for (const l of this.candidates(loc)) {
334
+ try {
335
+ await l.click({ timeout: 4000 });
336
+ const mod = process.platform === "darwin" ? "Meta" : "Control";
337
+ await this.page.keyboard.press(`${mod}+A`).catch(() => { });
338
+ await this.page.keyboard.press("Delete").catch(() => { });
339
+ await this.page.keyboard.insertText(text);
340
+ return true;
341
+ }
342
+ catch {
343
+ /* try next */
344
+ }
345
+ }
346
+ return false;
347
+ }
348
+ async replaySelect(loc, value) {
349
+ for (const l of this.candidates(loc)) {
350
+ try {
351
+ await l.selectOption(value, { timeout: 4000 });
352
+ return true;
353
+ }
354
+ catch {
355
+ /* try next */
356
+ }
357
+ }
358
+ return false;
359
+ }
360
+ }
361
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
package/dist/cli.js ADDED
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ import { log } from "./config.js";
3
+ /**
4
+ * Single entry point for the `rhai-mcp` binary.
5
+ *
6
+ * rhai-mcp → run the MCP server (what coding agents launch)
7
+ * rhai-mcp login <url> → open a window to log into a service once
8
+ * rhai-mcp task "<goal>" → run one task directly (for testing)
9
+ */
10
+ async function main() {
11
+ const sub = process.argv[2];
12
+ if (sub === "login") {
13
+ const { loginMain } = await import("./login.js");
14
+ await loginMain(process.argv[3]);
15
+ process.exit(0);
16
+ }
17
+ if (sub === "task") {
18
+ const { taskMain } = await import("./run.js");
19
+ await taskMain(process.argv[3], process.argv[4]);
20
+ process.exit(0);
21
+ }
22
+ if (sub === "help" || sub === "--help" || sub === "-h") {
23
+ console.error([
24
+ "rhai-mcp — a browser agent coding agents can delegate manual web tasks to.",
25
+ "",
26
+ "Usage:",
27
+ " rhai-mcp Run the MCP server (used by Claude Code / Codex / Cursor)",
28
+ " rhai-mcp login <url> Log into a service once; the session is reused afterward",
29
+ ' rhai-mcp task "<goal>" [url] Run a single task directly (for testing)',
30
+ "",
31
+ "Env: OPENAI_API_KEY (required), RHAI_HEADLESS, RHAI_MODEL, RHAI_MAX_STEPS, DATABASE_URL",
32
+ ].join("\n"));
33
+ process.exit(0);
34
+ }
35
+ // Default: run the MCP server.
36
+ const { serverMain } = await import("./server.js");
37
+ await serverMain();
38
+ }
39
+ main().catch((err) => {
40
+ log("fatal:", err);
41
+ process.exit(1);
42
+ });
43
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAElC;;;;;;GAMG;AACH,KAAK,UAAU,IAAI;IACjB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAE5B,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACpB,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;QACjD,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;QACnB,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;QAC9C,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACvD,OAAO,CAAC,KAAK,CACX;YACE,4EAA4E;YAC5E,EAAE;YACF,QAAQ;YACR,uFAAuF;YACvF,sFAAsF;YACtF,2EAA2E;YAC3E,EAAE;YACF,yFAAyF;SAC1F,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,+BAA+B;IAC/B,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IACnD,MAAM,UAAU,EAAE,CAAC;AACrB,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,78 @@
1
+ import { fileURLToPath } from "node:url";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { rmSync, mkdirSync } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ import dotenv from "dotenv";
6
+ dotenv.config();
7
+ const here = dirname(fileURLToPath(import.meta.url));
8
+ // Package root is one level up from src/ (or dist/). Used to locate the Prisma schema.
9
+ export const packageRoot = resolve(here, "..");
10
+ // Persistent data (logged-in profile + memory DB) lives in the user's home so it
11
+ // survives `npx` runs and works for any installed user. Override with RHAI_HOME.
12
+ export const dataDir = process.env.RHAI_HOME ? resolve(process.env.RHAI_HOME) : join(homedir(), ".rhai");
13
+ try {
14
+ mkdirSync(dataDir, { recursive: true });
15
+ }
16
+ catch {
17
+ /* ignore */
18
+ }
19
+ // Point Prisma at a single SQLite file in the data dir unless overridden.
20
+ if (!process.env.DATABASE_URL) {
21
+ process.env.DATABASE_URL = `file:${join(dataDir, "rhai.db")}`;
22
+ }
23
+ export const databaseUrl = process.env.DATABASE_URL;
24
+ export const config = {
25
+ openaiApiKey: process.env.OPENAI_API_KEY ?? "",
26
+ profileDir: process.env.RHAI_PROFILE_DIR ? resolve(process.env.RHAI_PROFILE_DIR) : join(dataDir, "profile"),
27
+ // Headless by default. Set RHAI_HEADLESS=false to watch the agent work.
28
+ // (The one-time `npm run login` flow always runs headed, regardless of this.)
29
+ headless: process.env.RHAI_HEADLESS !== "false",
30
+ // Use your real installed Chrome instead of Playwright's bundled Chromium.
31
+ // e.g. RHAI_BROWSER_CHANNEL=chrome (or "msedge"). Empty = bundled Chromium.
32
+ channel: process.env.RHAI_BROWSER_CHANNEL || "",
33
+ // Attach to a Chrome YOU launched with --remote-debugging-port (so it has your
34
+ // saved passwords / logins), instead of launching an isolated profile.
35
+ // e.g. RHAI_CDP_URL=http://localhost:9222
36
+ cdpUrl: process.env.RHAI_CDP_URL || "",
37
+ model: process.env.RHAI_MODEL ?? "gpt-5",
38
+ // Reasoning effort & step budget. Empty/0 = AUTO (Rhai picks per task). Set the
39
+ // env vars to force a value. ("minimal" = fastest but no web-search.)
40
+ effort: process.env.RHAI_EFFORT || "", // "" = auto
41
+ maxStepsEnv: process.env.RHAI_MAX_STEPS ? Number.parseInt(process.env.RHAI_MAX_STEPS, 10) : 0, // 0 = auto
42
+ };
43
+ // When a TUI is active (the `task` runner on a TTY), progress lines are routed
44
+ // here instead of stderr. MCP mode never sets a sink, so its logging is untouched.
45
+ let logSink = null;
46
+ export function setLogSink(fn) {
47
+ logSink = fn;
48
+ }
49
+ /** User-facing progress — feeds the TUI (or stderr when no UI). */
50
+ export function log(...args) {
51
+ if (logSink) {
52
+ logSink(args.map((a) => (typeof a === "string" ? a : String(a))).join(" "));
53
+ }
54
+ else {
55
+ console.error("[rhai]", ...args);
56
+ }
57
+ }
58
+ /** Internal diagnostics — hidden by default; only shown with RHAI_DEBUG=1. Never feeds the TUI. */
59
+ export function debug(...args) {
60
+ if (process.env.RHAI_DEBUG)
61
+ console.error("[rhai:debug]", ...args);
62
+ }
63
+ /** Remove a stale single-instance lock left behind by a browser that didn't shut down cleanly. */
64
+ export function clearProfileLock() {
65
+ for (const f of ["SingletonLock", "SingletonSocket", "SingletonCookie"]) {
66
+ try {
67
+ rmSync(join(config.profileDir, f), { force: true });
68
+ }
69
+ catch {
70
+ /* ignore */
71
+ }
72
+ }
73
+ }
74
+ /** True when a launch error is the "profile already in use" / stale-lock case. */
75
+ export function isProfileLockError(err) {
76
+ return /already in use|Opening in existing browser session|SingletonLock/i.test(String(err));
77
+ }
78
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/login.js ADDED
@@ -0,0 +1,65 @@
1
+ import readline from "node:readline";
2
+ import { chromium } from "playwright";
3
+ import { config, log, clearProfileLock, isProfileLockError } from "./config.js";
4
+ /**
5
+ * One-time login helper. Opens a visible Chrome using the SAME persistent profile
6
+ * the agent uses, navigates to the URL you pass, and waits for you to log in. Once
7
+ * you press Enter, the session (cookies) is saved into the profile and the agent
8
+ * will reuse it forever after — no need to log in again.
9
+ *
10
+ * Usage: rhai-mcp login https://console.cloud.google.com
11
+ */
12
+ export async function loginMain(url) {
13
+ if (!url) {
14
+ console.error("Usage: rhai-mcp login <url>");
15
+ console.error("Example: rhai-mcp login https://console.cloud.google.com");
16
+ process.exit(1);
17
+ }
18
+ const waitForEnter = () => new Promise((resolve) => {
19
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
20
+ rl.question("", () => {
21
+ rl.close();
22
+ resolve();
23
+ });
24
+ });
25
+ // CDP mode: you're using your OWN Chrome (with your saved passwords). Just open
26
+ // the URL there; the session lives in your real profile, which the agent also attaches to.
27
+ if (config.cdpUrl) {
28
+ log(`attaching to your Chrome over CDP at ${config.cdpUrl}`);
29
+ const browser = await chromium.connectOverCDP(config.cdpUrl);
30
+ const ctx = browser.contexts()[0] ?? (await browser.newContext());
31
+ const page = ctx.pages()[0] ?? (await ctx.newPage());
32
+ await page.goto(url, { waitUntil: "domcontentloaded" }).catch(() => { });
33
+ console.error("\n→ Log in (your saved passwords/autofill are available — this is your real browser).");
34
+ console.error("→ When you're logged in, come back here and press Enter.\n");
35
+ await waitForEnter();
36
+ await browser.close().catch(() => { }); // disconnect, leaves your Chrome running
37
+ log("done. The agent will reuse this session via CDP.");
38
+ return;
39
+ }
40
+ log(`opening ${url} with profile ${config.profileDir}${config.channel ? ` (channel: ${config.channel})` : ""}`);
41
+ const launchOpts = {
42
+ headless: false,
43
+ channel: config.channel || undefined,
44
+ viewport: { width: 1280, height: 900 },
45
+ };
46
+ let context;
47
+ try {
48
+ context = await chromium.launchPersistentContext(config.profileDir, launchOpts);
49
+ }
50
+ catch (err) {
51
+ if (!isProfileLockError(err))
52
+ throw err;
53
+ log("stale profile lock detected; clearing and retrying");
54
+ clearProfileLock();
55
+ context = await chromium.launchPersistentContext(config.profileDir, launchOpts);
56
+ }
57
+ const page = context.pages()[0] ?? (await context.newPage());
58
+ await page.goto(url, { waitUntil: "domcontentloaded" }).catch(() => { });
59
+ console.error("\n→ Log in to the service in the browser window that just opened.");
60
+ console.error("→ When you're fully logged in, come back here and press Enter to save the session.\n");
61
+ await waitForEnter();
62
+ await context.close();
63
+ log("session saved. The agent can now use this account.");
64
+ }
65
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"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"}