iframer-cli 1.0.1 → 1.0.3

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 (3) hide show
  1. package/cli.cjs +15 -0
  2. package/index.js +101 -0
  3. package/package.json +1 -1
package/cli.cjs CHANGED
@@ -201,6 +201,20 @@ const [,, command, ...args] = process.argv;
201
201
 
202
202
  async function main() {
203
203
  switch (command) {
204
+ case "install": {
205
+ try {
206
+ execSync("claude mcp add iframer -- npx -y --package=iframer-cli iframer-mcp", { stdio: "inherit" });
207
+ console.log("\n iframer MCP installed in Claude Code!");
208
+ console.log(" Now run: iframer login");
209
+ } catch {
210
+ console.error(" Failed to install. Make sure Claude Code CLI is available.");
211
+ console.error(" You can manually add it with:");
212
+ console.error(" claude mcp add iframer -- npx -y --package=iframer-cli iframer-mcp");
213
+ process.exit(1);
214
+ }
215
+ break;
216
+ }
217
+
204
218
  case "login":
205
219
  await login();
206
220
  break;
@@ -518,6 +532,7 @@ async function main() {
518
532
  iframer - CLI for the Agentic Browser API
519
533
 
520
534
  Commands:
535
+ install Install iframer MCP in Claude Code
521
536
  login Authenticate with the server
522
537
  logout Remove saved credentials
523
538
  status Show current auth status
package/index.js CHANGED
@@ -514,6 +514,107 @@ ALWAYS call this tool when credentials are needed but not stored. NEVER tell the
514
514
  }
515
515
  );
516
516
 
517
+ // ─── Tool 11: recaptcha_solve ───────────────────────────────────────
518
+
519
+ server.tool(
520
+ "recaptcha_solve",
521
+ `Start solving a reCAPTCHA. Clicks the checkbox and if a challenge appears, returns ALL tile images inline in one response along with the prompt text. Much faster than individual recaptcha-click + screenshot + recaptcha-info calls.
522
+
523
+ If the reCAPTCHA is solved immediately (no challenge), returns solved: true.
524
+ If a challenge appears, returns the prompt and all tile images. Then use recaptcha_answer with the tile indices to complete it.`,
525
+ {},
526
+ async () => {
527
+ try {
528
+ const data = await apiPost("/interactive/act", {
529
+ action: { type: "recaptcha-solve" },
530
+ screenshot: false,
531
+ });
532
+ if (!data.ok) return errorResponse(`Error: ${data.error}`);
533
+
534
+ const result = data.result;
535
+ if (result.solved) {
536
+ return { content: [{ type: "text", text: "reCAPTCHA solved automatically (no challenge needed)." }] };
537
+ }
538
+
539
+ // Build content blocks: prompt text + all tile images
540
+ const content = [];
541
+ content.push({
542
+ type: "text",
543
+ text: `reCAPTCHA Challenge: "${result.prompt}"\nGrid: ${result.rows}x${result.cols} (${result.tiles.length} tiles)\nSelect the correct tiles by index (0-${result.tiles.length - 1}) using recaptcha_answer.`,
544
+ });
545
+
546
+ for (const tile of result.tiles) {
547
+ if (tile.image) {
548
+ content.push({
549
+ type: "text",
550
+ text: `Tile ${tile.index}:`,
551
+ });
552
+ content.push({
553
+ type: "image",
554
+ data: tile.image,
555
+ mimeType: "image/jpeg",
556
+ });
557
+ }
558
+ }
559
+
560
+ return { content };
561
+ } catch (err) {
562
+ return errorResponse(`Connection error: ${err.message}. Is the API running at ${BASE_URL}?`);
563
+ }
564
+ }
565
+ );
566
+
567
+ // ─── Tool 12: recaptcha_answer ──────────────────────────────────────
568
+
569
+ server.tool(
570
+ "recaptcha_answer",
571
+ `Submit tile selections and verify the reCAPTCHA in one call. Selects the specified tiles and clicks verify.
572
+
573
+ If solved, returns success. If a new challenge appears (common with reCAPTCHA), returns the new prompt and tile images inline — just call recaptcha_answer again with new selections.`,
574
+ {
575
+ tiles: z.array(z.number()).describe("Tile indices to select (e.g. [0, 3, 6])"),
576
+ },
577
+ async ({ tiles }) => {
578
+ try {
579
+ const data = await apiPost("/interactive/act", {
580
+ action: { type: "recaptcha-answer", tiles },
581
+ screenshot: false,
582
+ });
583
+ if (!data.ok) return errorResponse(`Error: ${data.error}`);
584
+
585
+ const result = data.result;
586
+ if (result.solved) {
587
+ return { content: [{ type: "text", text: "reCAPTCHA solved!" }] };
588
+ }
589
+
590
+ // New challenge — return tiles inline
591
+ const content = [];
592
+ content.push({
593
+ type: "text",
594
+ text: `Not solved yet — new challenge: "${result.prompt || "unknown"}"\nGrid: ${result.rows}x${result.cols} (${result.tiles.length} tiles)\nSelect the correct tiles by index using recaptcha_answer again.`,
595
+ });
596
+
597
+ for (const tile of result.tiles) {
598
+ if (tile.image) {
599
+ content.push({
600
+ type: "text",
601
+ text: `Tile ${tile.index}:`,
602
+ });
603
+ content.push({
604
+ type: "image",
605
+ data: tile.image,
606
+ mimeType: "image/jpeg",
607
+ });
608
+ }
609
+ }
610
+
611
+ return { content };
612
+ } catch (err) {
613
+ return errorResponse(`Connection error: ${err.message}. Is the API running at ${BASE_URL}?`);
614
+ }
615
+ }
616
+ );
617
+
517
618
  // ─── Start ───────────────────────────────────────────────────────────
518
619
 
519
620
  const transport = new StdioServerTransport();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iframer-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "CLI and MCP server for iframer — a headful/headless browser for AI agents",
5
5
  "type": "module",
6
6
  "main": "index.js",