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 +1 -1
- package/src/index.js +21 -15
- package/src/mail.js +25 -10
- package/src/signup.js +56 -5
package/package.json
CHANGED
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,
|
|
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
|
-
|
|
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
|
|
76
|
-
const count = parseInt(countStr) || 5;
|
|
85
|
+
const count = argCount || parseInt(await ask('How many accounts to create?', '5')) || 5;
|
|
77
86
|
|
|
78
|
-
const
|
|
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
|
|
119
|
-
|
|
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
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (
|
|
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 || '
|
|
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
|
-
|
|
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();
|