grok-builder-cli 1.0.0 → 1.0.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grok-builder-cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Auto-register Grok (x.ai) accounts with temp mail (CloakMail/glx.web.id) and auto-integrate into 9router — multi-threaded CLI",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -10,7 +10,7 @@ import cliProgress from 'cli-progress';
10
10
  import * as readline from 'node:readline/promises';
11
11
  import { stdin as input, stdout as output } from 'node:process';
12
12
 
13
- import { runOneSignup, getTurnstilePath } from './signup.js';
13
+ import { runOneSignup, applyTurnstileBypass } from './signup.js';
14
14
  import { login as loginRouter9 } from './router9.js';
15
15
 
16
16
  // ── ANSI helpers ──────────────────────────────────────────────
@@ -32,16 +32,26 @@ function row(num, email, status, elapsed, extra = '') {
32
32
  console.log(` ${dim(String(num).padStart(3))} ${cyan('│')} ${e} ${s} ${t} ${extra}`);
33
33
  }
34
34
 
35
+ // ── Parse CLI args ───────────────────────────────────────────
36
+ const args = process.argv.slice(2);
37
+ const argCount = args.includes('--count') ? parseInt(args[args.indexOf('--count') + 1]) : null;
38
+ const argThreads = args.includes('--threads') ? parseInt(args[args.indexOf('--threads') + 1]) : null;
39
+ const argNoRouter = args.includes('--no-router');
40
+
41
+ // Interactive mode: only prompt if no args provided
42
+ const interactive = !argCount && !argThreads;
43
+
35
44
  // ── Interactive prompt ───────────────────────────────────────
36
- const rl = readline.createInterface({ input, output });
45
+ const rl = interactive ? readline.createInterface({ input, output }) : null;
37
46
 
38
47
  async function ask(q, defaultVal) {
39
- const answer = await rl.question(`${cyan('?')} ${bold(q)} ${defaultVal ? dim(`(${defaultVal}) `) : ''}`);
48
+ if (!interactive) return defaultVal;
49
+ const answer = await rl.question(`${cyan('?')} ${bold(q)} ${defaultVal !== undefined ? dim(`(${defaultVal}) `) : ''}`);
40
50
  return answer.trim() || defaultVal;
41
51
  }
42
52
 
43
53
  function closePrompt() {
44
- rl.close();
54
+ if (rl) rl.close();
45
55
  }
46
56
 
47
57
  // ── Main ──────────────────────────────────────────────────────
@@ -72,13 +82,11 @@ async function main() {
72
82
 
73
83
  // ── Interactive questions ───────────────────────────────────
74
84
  console.log();
75
- const countStr = await ask('How many accounts to create?', '5');
76
- const count = parseInt(countStr) || 5;
85
+ const count = argCount || parseInt(await ask('How many accounts to create?', '5')) || 5;
77
86
 
78
- const threadsStr = await ask('How many parallel threads?', '2');
79
- const threads = parseInt(threadsStr) || 2;
87
+ const threads = argThreads || parseInt(await ask('How many parallel threads?', '2')) || 2;
80
88
 
81
- const addRouter = (await ask('Auto-add to 9router?', 'y')).toLowerCase() === 'y';
89
+ const addRouter = argNoRouter ? false : (await ask('Auto-add to 9router?', 'y')).toLowerCase() === 'y';
82
90
 
83
91
  let routerOk = false;
84
92
  if (addRouter) {
@@ -115,19 +123,15 @@ async function main() {
115
123
 
116
124
  const progressBar = multibar.create(count, 0);
117
125
 
118
- // ── Launch persistent browser with turnstile patch ──────────
119
- const extPath = getTurnstilePath();
120
- console.log(` ${dim('Turnstile patch:')} ${extPath}\n`);
126
+ // ── Launch browser ─────────────────────────────────────────
127
+ console.log(` ${dim('Turnstile patch: addInitScript')}\n`);
121
128
 
122
- // We'll use one shared browser for all contexts
123
129
  const browser = await chromium.launch({
124
130
  headless: false,
125
131
  args: [
126
132
  '--no-sandbox',
127
133
  '--disable-dev-shm-usage',
128
134
  '--disable-blink-features=AutomationControlled',
129
- `--disable-extensions-except=${extPath}`,
130
- `--load-extension=${extPath}`,
131
135
  ],
132
136
  });
133
137
 
@@ -137,6 +141,8 @@ async function main() {
137
141
  viewport: { width: 1280, height: 1024 },
138
142
  ignoreHTTPSErrors: true,
139
143
  });
144
+ // Inject turnstile bypass into every page
145
+ applyTurnstileBypass(context);
140
146
  try {
141
147
  const account = await runOneSignup({
142
148
  context,
package/src/mail.js CHANGED
@@ -35,22 +35,37 @@ export async function fetchEmails(addr) {
35
35
  if (!res.ok) throw new Error(`Inbox fetch failed: ${res.status}`);
36
36
  const json = await res.json();
37
37
 
38
- // Parse SvelteKit __data.json format
38
+ // Parse SvelteKit __data.json compact format
39
+ // raw = [descriptor, addr_str, [email_indices...], email_obj_with_refs...]
40
+ // descriptor = {address: refIndex, emails: refIndex, pagination: refIndex}
41
+ // email_obj = {id: refIndex, to: refIndex, from: refIndex, subject: refIndex, ...}
42
+ function resolveCompact(v, raw, depth = 0) {
43
+ if (depth > 50) return v; // prevent circular refs
44
+ if (typeof v === 'number') return resolveCompact(raw[v], raw, depth + 1);
45
+ if (Array.isArray(v)) return v.map(i => resolveCompact(i, raw, depth + 1));
46
+ if (v && typeof v === 'object' && !Array.isArray(v)) {
47
+ const obj = {};
48
+ for (const [k, val] of Object.entries(v)) {
49
+ obj[k] = resolveCompact(val, raw, depth + 1);
50
+ }
51
+ return obj;
52
+ }
53
+ return v;
54
+ }
55
+
39
56
  for (const node of json.nodes || []) {
40
57
  if (node?.type === 'data') {
41
- const data = node.data;
42
- // data = [address_string, emails_array, pagination_object]
43
- if (Array.isArray(data)) {
44
- if (data.length >= 2 && Array.isArray(data[1])) return data[1];
45
- if (data.length >= 3 && Array.isArray(data[2])) return data[2];
58
+ const raw = node.data;
59
+ if (Array.isArray(raw)) {
60
+ // Descriptor is first item after resolving
61
+ const descriptor = resolveCompact(raw[0], raw);
62
+ if (descriptor && Array.isArray(descriptor.emails)) return descriptor.emails;
63
+ } else if (raw?.emails && Array.isArray(raw.emails)) {
64
+ return raw.emails;
46
65
  }
47
- // alternative: data = {address, emails, pagination}
48
- if (data?.emails && Array.isArray(data.emails)) return data.emails;
49
66
  }
50
67
  }
51
68
 
52
- // Fallback: try parsing from page data at top level
53
- if (json?.data?.emails) return json.data.emails;
54
69
  return [];
55
70
  }
56
71
 
package/src/signup.js CHANGED
@@ -1,14 +1,30 @@
1
1
  // x.ai signup flow — Playwright automation
2
2
  import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
+ import fs from 'fs';
4
5
  import { createTempEmail, waitForOTP } from './mail.js';
5
6
  import * as r9 from './router9.js';
6
7
 
7
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
- const TS_EXT = path.resolve(__dirname, '..', 'turnstilePatch');
9
9
  const SIGNUP_URL = 'https://accounts.x.ai/sign-up?redirect=grok-com';
10
10
 
11
- const PASSWORD = process.env.PASSWORD || 'GrokBuilder2024!Secure';
11
+ const PASSWORD = process.env.PASSWORD || 'MySecurePass123!@';
12
+
13
+ /**
14
+ * Get turnstile bypass script as string
15
+ */
16
+ export function getTurnstileScript() {
17
+ const scriptPath = path.resolve(__dirname, '..', 'turnstilePatch', 'script.js');
18
+ return fs.readFileSync(scriptPath, 'utf8');
19
+ }
20
+
21
+ /**
22
+ * Apply turnstile bypass to a browser context via addInitScript
23
+ */
24
+ export function applyTurnstileBypass(context) {
25
+ const script = getTurnstileScript();
26
+ context.addInitScript(script);
27
+ }
12
28
 
13
29
  /**
14
30
  * Run one signup flow + optional 9router addition
@@ -79,12 +95,47 @@ export async function runOneSignup({ context, addToRouter = false }) {
79
95
  // Step 7: Solve turnstile & submit
80
96
  let tok = '';
81
97
  for (let i = 0; i < 40; i++) {
82
- tok = await page.evaluate(
83
- () => document.querySelector('input[name=cf-turnstile-response]')?.value || ''
84
- );
98
+ tok = await page.evaluate(() => {
99
+ // Try direct input first
100
+ const el = document.querySelector('input[name=cf-turnstile-response]');
101
+ if (el?.value) return el.value;
102
+ // Try inside shadow DOM / iframes
103
+ const els = document.querySelectorAll('iframe');
104
+ for (const f of els) {
105
+ try {
106
+ const doc = f.contentDocument || f.contentWindow?.document;
107
+ if (doc) {
108
+ const inp = doc.querySelector('input[name=cf-turnstile-response]');
109
+ if (inp?.value) return inp.value;
110
+ }
111
+ } catch {}
112
+ }
113
+ return '';
114
+ });
85
115
  if (tok) break;
116
+ // Try clicking the Turnstile widget itself
117
+ try {
118
+ const widget = await page.locator('.cf-turnstile, iframe[src*="turnstile"], iframe[src*="challenges"]').first();
119
+ if (await widget.isVisible()) {
120
+ await widget.click({ timeout: 2000 });
121
+ }
122
+ } catch {}
86
123
  await sleep(1000);
87
124
  }
125
+ // Fallback: try to inject token directly
126
+ if (!tok) {
127
+ try {
128
+ await page.evaluate(() => {
129
+ const el = document.querySelector('input[name=cf-turnstile-response]');
130
+ if (el) {
131
+ el.value = 'DUMMY_TOKEN_FOR_TEST';
132
+ el.dispatchEvent(new Event('input', { bubbles: true }));
133
+ el.dispatchEvent(new Event('change', { bubbles: true }));
134
+ }
135
+ });
136
+ tok = 'injected';
137
+ } catch {}
138
+ }
88
139
  if (!tok) throw new Error('Turnstile timeout 40s');
89
140
 
90
141
  await page.getByRole('button', { name: /Complete sign up/i }).click();