social-agent-cli 4.2.2 → 4.4.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.
package/ai/runner.ts CHANGED
@@ -5,7 +5,7 @@ import type { Post } from "../core/types.js";
5
5
 
6
6
  const BROWSER_PLATFORMS: Record<string, string> = {
7
7
  x: "https://x.com",
8
- linkedin: "https://www.linkedin.com",
8
+ linkedin: "https://www.linkedin.com/feed/",
9
9
  };
10
10
 
11
11
  /**
package/cli.ts CHANGED
@@ -8,7 +8,7 @@ import type { Post, PostResult, PlatformDriver } from "./core/types.js";
8
8
  // ── Platform registry ────────────────────────────────────────
9
9
  const BROWSER_PLATFORMS: Record<string, string> = {
10
10
  x: "https://x.com",
11
- linkedin: "https://www.linkedin.com",
11
+ linkedin: "https://www.linkedin.com/feed/",
12
12
  };
13
13
 
14
14
  function getAllDrivers(): PlatformDriver[] {
@@ -1,33 +1,37 @@
1
1
  /**
2
- * Upload helper - Playwright fileChooser ile dosya yükler
2
+ * Upload helper - Playwright fileChooser ile dosya yükler + editör modal'ı kapatır
3
3
  * Kullanım: npx tsx core/upload-helper.ts <cdp-port> <click-selector> <file-path>
4
4
  */
5
5
  import { chromium } from "playwright";
6
6
 
7
7
  const [, , port, selector, filePath] = process.argv;
8
-
9
- if (!port || !selector || !filePath) {
10
- process.exit(1);
11
- }
8
+ if (!port || !selector || !filePath) process.exit(1);
12
9
 
13
10
  async function upload() {
14
11
  const browser = await chromium.connectOverCDP(`http://127.0.0.1:${port}`);
15
12
  const pages = browser.contexts()[0].pages();
16
- // LinkedIn sayfasını bul
17
- const page = pages.find(p => p.url().includes("linkedin.com")) || pages[0];
13
+ const page = pages.find(p => p.url().includes("linkedin.com") || p.url().includes("x.com")) || pages[0];
18
14
 
19
- // fileChooser promise'ı ÖNCE başlat
15
+ // 1. fileChooser yakala + butona tıkla
20
16
  const fcPromise = page.waitForEvent("filechooser", { timeout: 10000 });
21
-
22
- // Butona tıkla
23
17
  try { await page.click(selector, { timeout: 5000 }); } catch {}
24
-
25
- // fileChooser'ı yakala ve dosya set et
26
18
  const fc = await fcPromise;
27
19
  await fc.setFiles(filePath);
28
- console.log("OK");
29
20
 
30
- // Hemen çık - beklemeden
21
+ // 2. Editör modal'ı çıkarsa "İleri/Bitti/Done/Next" butonuna tıkla
22
+ await page.waitForTimeout(3000);
23
+ for (const text of ["İleri", "Bitti", "Done", "Next", "Tamam"]) {
24
+ try {
25
+ const btn = page.getByRole("button", { name: text });
26
+ if (await btn.isVisible({ timeout: 1000 })) {
27
+ await btn.click();
28
+ await page.waitForTimeout(1500);
29
+ break;
30
+ }
31
+ } catch {}
32
+ }
33
+
34
+ console.log("OK");
31
35
  process.exit(0);
32
36
  }
33
37
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "social-agent-cli",
3
- "version": "4.2.2",
3
+ "version": "4.4.0",
4
4
  "description": "AI-powered social media agent - free APIs + browser automation with self-healing selectors",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,58 +1,128 @@
1
- import { execSync, spawn } from "node:child_process";
1
+ import { execSync } from "node:child_process";
2
2
  import { join } from "node:path";
3
3
  import { homedir } from "node:os";
4
4
  import { existsSync, mkdirSync, copyFileSync } from "node:fs";
5
5
  import { PATHS } from "../../core/config.js";
6
- import { loadMap } from "../../ai/mapper.js";
6
+ import { loadMap, saveMap } from "../../ai/mapper.js";
7
7
  import { pickChromeProfile, getSavedProfile } from "../../core/profiles.js";
8
- import type { SelectorMap } from "../../core/types.js";
8
+ import type { SelectorMap, SelectorStep } from "../../core/types.js";
9
9
 
10
10
  const CHROME_PATH = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
11
11
  const CDP_PORT = 9222;
12
12
 
13
- /**
14
- * Browser Driver - agent-browser + gerçek Chrome profili (CDP)
15
- */
16
13
  export class BrowserDriver {
17
14
  private chromeStarted = false;
18
- private profileDirName = "";
19
15
 
20
16
  constructor(private platform: string, private homeUrl: string) {}
21
17
 
22
- /**
23
- * agent-browser komutu çalıştır (CDP ile)
24
- */
18
+ // ── agent-browser komutu ──────────────────────────────────
25
19
  private cmd(command: string, timeout = 15000): string {
26
20
  try {
27
21
  return execSync(`agent-browser --cdp ${CDP_PORT} ${command}`, {
28
- encoding: "utf-8",
29
- timeout,
30
- stdio: ["pipe", "pipe", "pipe"],
22
+ encoding: "utf-8", timeout, stdio: ["pipe", "pipe", "pipe"],
31
23
  }).trim();
32
24
  } catch (err: any) {
33
- throw new Error(err.stderr?.toString()?.trim() || err.stdout?.toString()?.trim() || err.message);
25
+ throw new Error(err.stderr?.toString()?.trim() || err.message);
34
26
  }
35
27
  }
36
28
 
37
- /**
38
- * Chrome'u gerçek profil ile CDP modunda başlat
39
- */
29
+ // ── Claude'a snapshot gönder, doğru ref'i bulsun ────────
30
+ private askClaudeForRef(snapshot: string, description: string, action: string): string | null {
31
+ try {
32
+ // Screenshot al - Claude hem görsel hem text ile analiz etsin
33
+ const ssPath = join(PATHS.screenshots, `${this.platform}_ai_ref.png`);
34
+ try { this.cmd(`screenshot "${ssPath}"`); } catch {}
35
+
36
+ const prompt = `Screenshot: ${ssPath}
37
+
38
+ Accessibility tree:
39
+ ${snapshot}
40
+
41
+ Görev: "${description}" (${action})
42
+
43
+ Bu snapshot'ta ve screenshot'ta yukarıdaki görevi yerine getirebilecek elementin ref değerini bul. Sadece ref değerini yaz, başka bir şey yazma. Örnek: e19`;
44
+
45
+ const result = execSync(
46
+ `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --model opus --effort high --max-turns 1 --tools ""`,
47
+ { encoding: "utf-8", timeout: 30000, stdio: ["pipe", "pipe", "pipe"] }
48
+ ).trim();
49
+
50
+ // ref değerini çıkar (e19, e7 gibi)
51
+ const refMatch = result.match(/\b(e\d+)\b/);
52
+ if (refMatch) {
53
+ console.log(` [ai] ${description} → @${refMatch[1]}`);
54
+ return refMatch[1];
55
+ }
56
+ } catch {}
57
+ return null;
58
+ }
59
+
60
+ // ── Snapshot al ve parse et ───────────────────────────────
61
+ private getSnapshot(): { raw: string; refs: Map<string, { ref: string; text: string; role: string }> } {
62
+ const raw = this.cmd("snapshot -i -c", 10000);
63
+ const refs = new Map<string, { ref: string; text: string; role: string }>();
64
+
65
+ // "button "Gönderi başlatın" [ref=e19]" → {ref: "e19", text: "Gönderi başlatın", role: "button"}
66
+ for (const match of raw.matchAll(/(\w+)\s+"([^"]*)"[^[]*\[ref=(\w+)\]/g)) {
67
+ refs.set(match[3], { ref: match[3], text: match[2], role: match[1] });
68
+ }
69
+ return { raw, refs };
70
+ }
71
+
72
+ // ── Snapshot'tan element bul (text match) ─────────────────
73
+ private findRef(snapshot: ReturnType<typeof this.getSnapshot>, description: string, role?: string): string | null {
74
+ // Description'dan tırnak içi text çıkar
75
+ const textMatch = description.match(/["'""\u201C\u201D]([^"'""\u201C\u201D]+)["'""\u201C\u201D]/);
76
+ const searchText = textMatch?.[1] || "";
77
+ if (!searchText) return null;
78
+
79
+ for (const [ref, info] of snapshot.refs) {
80
+ // Tam eşleşme
81
+ if (info.text === searchText) {
82
+ if (!role || info.role === role) return ref;
83
+ }
84
+ }
85
+ // Kısmi eşleşme
86
+ for (const [ref, info] of snapshot.refs) {
87
+ if (info.text.includes(searchText)) {
88
+ if (!role || info.role === role) return ref;
89
+ }
90
+ }
91
+ return null;
92
+ }
93
+
94
+ // ── Element'in CSS selector'ını al ────────────────────────
95
+ private getSelector(ref: string): string {
96
+ try {
97
+ const result = this.cmd(`eval "
98
+ (function() {
99
+ var el = document.querySelector('[data-ref=\\"${ref}\\"]');
100
+ if (!el) {
101
+ // ref ile bulamadıysa snapshot'taki sırayla dene
102
+ return '';
103
+ }
104
+ // Selector üret
105
+ if (el.id) return '#' + el.id;
106
+ if (el.getAttribute('data-testid')) return '[data-testid=\\\"' + el.getAttribute('data-testid') + '\\\"]';
107
+ if (el.getAttribute('aria-label')) return el.tagName.toLowerCase() + '[aria-label=\\\"' + el.getAttribute('aria-label') + '\\\"]';
108
+ return '';
109
+ })()
110
+ "`, 5000);
111
+ return result.replace(/^"|"$/g, "");
112
+ } catch {
113
+ return "";
114
+ }
115
+ }
116
+
117
+ // ── Chrome başlat ─────────────────────────────────────────
40
118
  async launch(): Promise<void> {
41
119
  const profile = getSavedProfile(this.platform);
42
120
  if (!profile) throw new Error(`Profil yok. Önce: social-agent login ${this.platform}`);
43
- this.profileDirName = profile.dirName;
44
121
 
45
- // CDP'ye bağlanmayı dene
46
- try {
47
- execSync(`curl -sf http://127.0.0.1:${CDP_PORT}/json/version`, { stdio: "pipe", timeout: 2000 });
48
- return; // zaten çalışıyor
49
- } catch {}
122
+ try { execSync(`curl -sf http://127.0.0.1:${CDP_PORT}/json/version`, { stdio: "pipe", timeout: 2000 }); return; } catch {}
50
123
 
51
- // Chrome'u CDP ile başlat
52
124
  const workDir = join(PATHS.profiles, "chrome-cdp");
53
125
  mkdirSync(join(workDir, profile.dirName), { recursive: true });
54
-
55
- // Cookie'leri kopyala
56
126
  for (const f of ["Cookies", "Cookies-journal", "Login Data", "Login Data-journal", "Preferences", "Secure Preferences"]) {
57
127
  const src = join(profile.fullPath, f);
58
128
  if (existsSync(src)) { try { copyFileSync(src, join(workDir, profile.dirName, f)); } catch {} }
@@ -60,69 +130,45 @@ export class BrowserDriver {
60
130
  const ls = join(homedir(), "Library", "Application Support", "Google", "Chrome", "Local State");
61
131
  if (existsSync(ls)) { try { copyFileSync(ls, join(workDir, "Local State")); } catch {} }
62
132
 
63
- // agent-browser daemon'u kapat
64
- execSync("agent-browser close", { stdio: "pipe" }).toString();
65
-
133
+ const { spawn } = await import("node:child_process");
134
+ execSync("agent-browser close", { stdio: "pipe" }).toString().catch?.(() => {});
66
135
  const child = spawn(CHROME_PATH, [
67
- `--remote-debugging-port=${CDP_PORT}`,
68
- `--user-data-dir=${workDir}`,
69
- `--profile-directory=${profile.dirName}`,
70
- "--no-first-run", "--no-default-browser-check",
136
+ `--remote-debugging-port=${CDP_PORT}`, `--user-data-dir=${workDir}`,
137
+ `--profile-directory=${profile.dirName}`, "--no-first-run", "--no-default-browser-check",
71
138
  ], { detached: true, stdio: "ignore" });
72
139
  child.unref();
73
- this.chromeStarted = true;
74
140
 
75
- // Bağlantıyı bekle
76
141
  for (let i = 0; i < 30; i++) {
77
142
  await new Promise(r => setTimeout(r, 1000));
78
- try {
79
- execSync(`curl -sf http://127.0.0.1:${CDP_PORT}/json/version`, { stdio: "pipe", timeout: 3000 });
80
- console.log(`[${this.platform}] Chrome bağlandı`);
81
- return;
82
- } catch {}
143
+ try { execSync(`curl -sf http://127.0.0.1:${CDP_PORT}/json/version`, { stdio: "pipe", timeout: 2000 }); console.log(`[${this.platform}] Chrome bağlandı`); return; } catch {}
83
144
  }
84
- throw new Error("Chrome başlatılamadı - Chrome'u kapat ve tekrar dene");
145
+ throw new Error("Chrome başlatılamadı");
85
146
  }
86
147
 
87
148
  async close(): Promise<void> {}
88
149
 
89
- /**
90
- * Login
91
- */
92
150
  async login(): Promise<void> {
93
151
  await pickChromeProfile(this.platform);
94
152
  await this.launch();
95
153
  this.cmd(`open "${this.homeUrl}"`, 30000);
96
-
97
154
  console.log(`\n[${this.platform}] Tarayıcı açıldı: ${this.homeUrl}`);
98
155
  const { createInterface } = await import("node:readline");
99
156
  const rl = createInterface({ input: process.stdin, output: process.stdout });
100
- await new Promise<void>(resolve => {
101
- rl.question("Hesabın açıksa Enter'a bas: ", () => { rl.close(); resolve(); });
102
- });
103
- console.log(`[${this.platform}] Kayıt tamamlandı.`);
157
+ await new Promise<void>(resolve => { rl.question("Enter'a bas: ", () => { rl.close(); resolve(); }); });
104
158
  process.exit(0);
105
159
  }
106
160
 
107
- /**
108
- * Learn - ARIA snapshot al ve dosyaya yaz (Claude Code okuyup map oluşturur)
109
- */
110
- async learn(taskDescription = "yeni bir metin postu at", actionName = "post"): Promise<SelectorMap> {
161
+ async learn(taskDescription = "post at", actionName = "post"): Promise<SelectorMap> {
111
162
  await this.launch();
112
163
  this.cmd(`open "${this.homeUrl}"`, 30000);
113
164
  this.cmd("wait 3000");
114
-
115
- const screenshotPath = join(PATHS.screenshots, `${this.platform}_learn.png`);
116
- this.cmd(`screenshot "${screenshotPath}"`);
117
-
165
+ const ssPath = join(PATHS.screenshots, `${this.platform}_learn.png`);
166
+ this.cmd(`screenshot "${ssPath}"`);
118
167
  const snapshot = this.cmd("snapshot -i -c", 10000);
119
- const snapshotPath = join(PATHS.screenshots, `${this.platform}_snapshot.txt`);
120
- const { writeFileSync: writeFile } = await import("node:fs");
121
- writeFile(snapshotPath, snapshot);
122
-
123
- console.log(`Screenshot: ${screenshotPath}`);
124
- console.log(`Snapshot: ${snapshotPath}`);
125
- console.log(`\nBu dosyaları Claude Code'a ver, map oluştursun.`);
168
+ const snapPath = join(PATHS.screenshots, `${this.platform}_snapshot.txt`);
169
+ const { writeFileSync } = await import("node:fs");
170
+ writeFileSync(snapPath, snapshot);
171
+ console.log(`Screenshot: ${ssPath}\nSnapshot: ${snapPath}`);
126
172
  process.exit(0);
127
173
  return {} as SelectorMap;
128
174
  }
@@ -132,7 +178,8 @@ export class BrowserDriver {
132
178
  this.cmd("wait 2000");
133
179
  }
134
180
 
135
- executeStep(step: any, content: string, imagePath?: string): void {
181
+ // ── Step çalıştır: önce cached selector, bozuksa snapshot+ref ──
182
+ executeStep(step: SelectorStep, content: string, imagePath?: string): void {
136
183
  const val = (step.value || "").replace("{{CONTENT}}", content).replace("{{IMAGE}}", imagePath || "");
137
184
 
138
185
  switch (step.action) {
@@ -145,47 +192,69 @@ export class BrowserDriver {
145
192
  break;
146
193
 
147
194
  case "click": {
148
- const sels = [step.selector, ...(step.fallbackSelectors || [])].filter(Boolean);
149
- // 1. CSS selector ile dene
150
- for (const sel of sels) {
151
- if (sel.includes("{{")) continue;
195
+ // 1. Cached selector dene
196
+ if (step.selector) {
197
+ try { this.cmd(`click "${step.selector}"`); if (step.waitMs) this.cmd(`wait ${step.waitMs}`); return; } catch {}
198
+ }
199
+ for (const sel of step.fallbackSelectors || []) {
152
200
  try { this.cmd(`click "${sel}"`); if (step.waitMs) this.cmd(`wait ${step.waitMs}`); return; } catch {}
153
201
  }
154
- // 2. JS eval fallback - description'dan buton text'i çıkar
155
- const desc = step.description || "";
156
- // Tırnak içindeki text'leri bul: "Gönderi", 'Post', vb.
157
- const textMatches = desc.match(/["'""\u201C\u201D]([^"'""\u201C\u201D]+)["'""\u201C\u201D]/g) || [];
158
- const texts = textMatches.map(m => m.replace(/["'""\u201C\u201D]/g, "").trim()).filter(Boolean);
159
-
160
- for (const text of texts) {
161
- try {
162
- this.cmd(`eval "document.querySelectorAll('button,a,[role=button]').forEach(b => { if(b.textContent.trim()==='${text}' || b.textContent.trim().startsWith('${text}')) b.click() })"`);
163
- if (step.waitMs) this.cmd(`wait ${step.waitMs}`);
164
- return;
165
- } catch {}
202
+
203
+ // 2. Selector bozuk screenshot + snapshot al, ref ile bul
204
+ try { this.cmd(`screenshot "${join(PATHS.screenshots, `${this.platform}_refmatch.png`)}"`); } catch {}
205
+ const snapshot = this.getSnapshot();
206
+ const ref = this.findRef(snapshot, step.description || "", "button");
207
+ if (ref) {
208
+ this.cmd(`click @${ref}`);
209
+ // Yeni selector'ı cache'le
210
+ const newSel = this.getSelector(ref);
211
+ if (newSel && newSel !== step.selector) {
212
+ console.log(` [cache] ${step.selector || "?"} → ${newSel}`);
213
+ step.selector = newSel;
214
+ }
215
+ if (step.waitMs) this.cmd(`wait ${step.waitMs}`);
216
+ return;
166
217
  }
167
- throw new Error(`Click: ${sels[0] || desc}`);
218
+
219
+ // 3. Son çare: Claude'a sor - snapshot'tan doğru elementi bulsun
220
+ const aiRef = this.askClaudeForRef(snapshot.raw, step.description || "", "click");
221
+ if (aiRef) {
222
+ this.cmd(`click @${aiRef}`);
223
+ if (step.waitMs) this.cmd(`wait ${step.waitMs}`);
224
+ return;
225
+ }
226
+ throw new Error(`Click: ${step.description}`);
168
227
  }
169
228
 
170
229
  case "type": {
171
- const sels = [step.selector, ...(step.fallbackSelectors || [])].filter(Boolean);
172
- const escaped = val.replace(/"/g, '\\"').replace(/'/g, "\\'");
173
- // 1. CSS selector ile dene
174
- for (const sel of sels) {
175
- if (sel.includes("{{")) continue;
176
- try {
177
- this.cmd(`type "${sel}" "${escaped}"`, 30000);
178
- if (step.waitMs) this.cmd(`wait ${step.waitMs}`);
179
- return;
180
- } catch {}
230
+ const escaped = val.replace(/"/g, '\\"');
231
+
232
+ // 1. Cached selector dene
233
+ if (step.selector) {
234
+ try { this.cmd(`type "${step.selector}" "${escaped}"`, 30000); if (step.waitMs) this.cmd(`wait ${step.waitMs}`); return; } catch {}
181
235
  }
182
- // 2. Fallback: sayfadaki ilk textbox/contenteditable'a yaz
183
- try {
184
- this.cmd(`eval "var el = document.querySelector('[role=textbox],[contenteditable=true]'); if(el) { el.focus(); document.execCommand('insertText', false, '${escaped}'); }"`);
236
+ for (const sel of step.fallbackSelectors || []) {
237
+ try { this.cmd(`type "${sel}" "${escaped}"`, 30000); if (step.waitMs) this.cmd(`wait ${step.waitMs}`); return; } catch {}
238
+ }
239
+
240
+ // 2. Screenshot + snapshot → ref ile textbox bul
241
+ try { this.cmd(`screenshot "${join(PATHS.screenshots, `${this.platform}_refmatch.png`)}"`); } catch {}
242
+ const snapshot = this.getSnapshot();
243
+ const ref = this.findRef(snapshot, step.description || "", "textbox");
244
+ if (ref) {
245
+ this.cmd(`type @${ref} "${escaped}"`, 30000);
185
246
  if (step.waitMs) this.cmd(`wait ${step.waitMs}`);
186
247
  return;
187
- } catch {}
188
- throw new Error(`Type: ${sels[0]}`);
248
+ }
249
+
250
+ // 3. Claude'a sor
251
+ const aiRef = this.askClaudeForRef(snapshot.raw, step.description || "", "type");
252
+ if (aiRef) {
253
+ this.cmd(`type @${aiRef} "${escaped}"`, 30000);
254
+ if (step.waitMs) this.cmd(`wait ${step.waitMs}`);
255
+ return;
256
+ }
257
+ throw new Error(`Type: ${step.description}`);
189
258
  }
190
259
 
191
260
  case "keypress":
@@ -197,53 +266,35 @@ export class BrowserDriver {
197
266
  const filePath = val || imagePath || "";
198
267
  if (!filePath || filePath.includes("{{")) break;
199
268
 
200
- // 1. agent-browser upload ile dene (X'te çalışır)
201
- try {
202
- this.cmd(`upload "input[type='file']" "${filePath}"`, 5000);
203
- if (step.waitMs) this.cmd(`wait ${step.waitMs}`);
204
- return;
205
- } catch {}
269
+ // 1. input[type=file] direkt (X'te çalışır)
270
+ try { this.cmd(`upload "input[type='file']" "${filePath}"`, 5000); if (step.waitMs) this.cmd(`wait ${step.waitMs}`); return; } catch {}
206
271
 
207
- // 2. Playwright fileChooser ile dene (LinkedIn gibi native dialog kullananlar için)
208
- // Snapshot'tan medya butonunun aria-label'ını bul
209
- let btnSel = "button[aria-label*='Medya']";
272
+ // 2. Playwright helper (LinkedIn gibi native dialog için)
273
+ // Snapshot'tan medya butonunu bul
274
+ let btnSel = "button[aria-label='Medya ekle']";
210
275
  try {
211
- const snap = this.cmd("snapshot -i -c", 5000);
212
- const mediaMatch = snap.match(/button "([^"]*(?:Medya|media|Fotoğraf|photo|Dosya|file|video)[^"]*)"/i);
213
- if (mediaMatch) btnSel = `button[aria-label='${mediaMatch[1]}']`;
276
+ const snap = this.getSnapshot();
277
+ for (const [ref, info] of snap.refs) {
278
+ if (info.text.match(/medya|fotoğraf|photo|media|video/i) && info.role === "button") {
279
+ btnSel = `button[aria-label='${info.text}']`;
280
+ break;
281
+ }
282
+ }
214
283
  } catch {}
284
+
215
285
  try {
216
286
  const thisDir = new URL(".", import.meta.url).pathname;
217
287
  const pkgDir = process.env.SOCIAL_AGENT_PKG || join(thisDir, "../..");
218
- console.log(` [upload] helper: ${btnSel}`);
219
- const helperResult = execSync(
288
+ const result = execSync(
220
289
  `npx tsx "${join(pkgDir, 'core/upload-helper.ts')}" ${CDP_PORT} "${btnSel}" "${filePath}"`,
221
290
  { encoding: "utf-8", timeout: 60000 }
222
291
  ).trim();
223
- if (helperResult === "OK") {
224
- // Editör modal kapatma - "İleri" butonuna tıkla (3 saniye bekle, modal yüklensin)
225
- setTimeout(() => {}, 3000);
226
- try {
227
- execSync(`agent-browser --cdp ${CDP_PORT} wait 3000`, { stdio: "pipe", timeout: 10000 });
228
- execSync(`agent-browser --cdp ${CDP_PORT} eval "document.querySelectorAll('button').forEach(b => { var t=b.textContent.trim().toLowerCase(); if(t==='ileri' || t==='bitti' || t==='done' || t==='next') b.click() })"`, { stdio: "pipe", timeout: 5000 });
229
- execSync(`agent-browser --cdp ${CDP_PORT} wait 2000`, { stdio: "pipe", timeout: 5000 });
230
- } catch {}
231
- return;
232
- }
233
- } catch (e: any) {
234
- console.log(` [upload] helper hata: ${e.message?.substring(0, 80)}`);
235
- }
236
-
292
+ if (result === "OK") return;
293
+ } catch {}
237
294
  throw new Error(`Upload: ${filePath}`);
238
295
  }
239
296
  }
240
297
  }
241
298
 
242
- getSnapshot(): string {
243
- try { return this.cmd("snapshot -i -c", 10000); } catch { return ""; }
244
- }
245
-
246
- takeScreenshot(path: string): void {
247
- try { this.cmd(`screenshot "${path}"`); } catch {}
248
- }
299
+ takeScreenshot(path: string): void { try { this.cmd(`screenshot "${path}"`); } catch {} }
249
300
  }