social-agent-cli 4.1.0 → 4.2.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
@@ -53,11 +53,12 @@ export async function runCommand(platform: string, command: string): Promise<voi
53
53
 
54
54
  let steps = [...map.steps];
55
55
 
56
- // Image varsa: gönder butonundan önce upload step ekle
56
+ // Image varsa: metin yazma step'inden ÖNCE upload step ekle (LinkedIn'de önce fotoğraf yüklenmeli)
57
57
  if (image && !steps.some(s => s.action === "upload")) {
58
- const sendIdx = steps.findLastIndex(s => s.action === "click");
59
- if (sendIdx > -1) {
60
- steps.splice(sendIdx, 0, {
58
+ const typeIdx = steps.findIndex(s => s.action === "type");
59
+ const insertIdx = typeIdx > -1 ? typeIdx : steps.findLastIndex(s => s.action === "click");
60
+ if (insertIdx > -1) {
61
+ steps.splice(insertIdx, 0, {
61
62
  action: "upload" as any,
62
63
  description: "Fotoğraf yükle",
63
64
  selector: "input[type='file']",
@@ -69,7 +70,9 @@ export async function runCommand(platform: string, command: string): Promise<voi
69
70
 
70
71
  // Step'leri çalıştır
71
72
  const errors: string[] = [];
73
+ let aborted = false;
72
74
  for (let idx = 0; idx < steps.length; idx++) {
75
+ if (aborted) break;
73
76
  const s = steps[idx];
74
77
  try {
75
78
  console.log(` ${idx + 1}. ${s.description}`);
@@ -77,6 +80,8 @@ export async function runCommand(platform: string, command: string): Promise<voi
77
80
  } catch (err: any) {
78
81
  errors.push(`Step ${idx + 1} (${s.description}): ${err.message}`);
79
82
  console.log(` ✗ ${err.message}`);
83
+ // Upload hatası → geri kalanı çalıştırma (fotoğrafsız post göndermesin)
84
+ if (s.action === "upload") { aborted = true; }
80
85
  }
81
86
  }
82
87
 
@@ -1,8 +1,8 @@
1
1
  /**
2
- * LinkedIn upload helper - Playwright fileChooser ile dosya yükler
3
- * agent-browser CDP ile çalışmıyor çünkü LinkedIn native file dialog kullanıyor
2
+ * Upload helper - CDP fileChooser intercept ile dosya yükler
3
+ * LinkedIn gibi native file dialog kullanan platformlar için
4
4
  *
5
- * Kullanım: npx tsx core/upload-helper.ts <cdp-port> <button-selector> <file-path>
5
+ * Kullanım: npx tsx core/upload-helper.ts <cdp-port> <click-selector> <file-path>
6
6
  */
7
7
  import { chromium } from "playwright";
8
8
 
@@ -15,17 +15,40 @@ if (!port || !selector || !filePath) {
15
15
 
16
16
  async function upload() {
17
17
  const browser = await chromium.connectOverCDP(`http://127.0.0.1:${port}`);
18
- const context = browser.contexts()[0];
19
- const pages = context.pages();
20
- const page = pages[pages.length - 1]; // son aktif sayfa
18
+ const pages = browser.contexts()[0].pages();
19
+ const page = pages[pages.length - 1];
21
20
 
22
- const [fileChooser] = await Promise.all([
23
- page.waitForEvent("filechooser", { timeout: 10000 }),
24
- page.click(selector),
25
- ]);
21
+ // fileChooser event'ini dinlemeye başla
22
+ let resolved = false;
23
+ const fcPromise = page.waitForEvent("filechooser", { timeout: 10000 });
26
24
 
27
- await fileChooser.setFiles(filePath);
28
- console.log("OK");
25
+ // Butona tıkla - fileChooser tetiklenecek
26
+ // Birden fazla selector dene
27
+ const selectors = selector.split("|");
28
+ for (const sel of selectors) {
29
+ try {
30
+ // Playwright text selector
31
+ if (sel.startsWith("text=")) {
32
+ await page.click(sel);
33
+ } else {
34
+ // CSS selector veya role selector
35
+ const el = page.locator(sel).first();
36
+ if (await el.isVisible({ timeout: 2000 })) {
37
+ await el.click();
38
+ }
39
+ }
40
+ } catch {}
41
+ }
42
+
43
+ try {
44
+ const fc = await fcPromise;
45
+ await fc.setFiles(filePath);
46
+ resolved = true;
47
+ console.log("OK");
48
+ } catch (e: any) {
49
+ console.error("FAIL:", e.message.substring(0, 100));
50
+ process.exit(1);
51
+ }
29
52
  }
30
53
 
31
54
  upload().catch((e) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "social-agent-cli",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "AI-powered social media agent - free APIs + browser automation with self-healing selectors",
5
5
  "type": "module",
6
6
  "bin": {
@@ -205,25 +205,34 @@ export class BrowserDriver {
205
205
  } catch {}
206
206
 
207
207
  // 2. Playwright fileChooser ile dene (LinkedIn gibi native dialog kullananlar için)
208
- const sels = [step.selector, ...(step.fallbackSelectors || [])].filter(Boolean);
209
- const btnSel = sels.find(s => !s.includes("input[type")) || "button";
208
+ // Snapshot'tan medya butonunun aria-label'ını bul
209
+ let btnSel = "button[aria-label*='Medya']";
210
210
  try {
211
- const { dirname: dn } = await import("node:path");
212
- const { fileURLToPath: fu } = await import("node:url");
213
- const pkgDir = process.env.SOCIAL_AGENT_PKG || join(dn(fu(import.meta.url)), "../..");
214
- execSync(
215
- `npx tsx "${join(pkgDir, 'core/upload-helper.ts')}" ${CDP_PORT} "${btnSel}" "${filePath}"`,
216
- { stdio: "pipe", timeout: 15000 }
217
- );
218
- this.cmd("wait 3000");
219
- // Editör modal'ı varsa kapat
220
- try {
221
- this.cmd(`eval "document.querySelectorAll('button').forEach(b => { var t=b.textContent.trim().toLowerCase(); if(t==='ileri' || t==='bitti' || t==='done' || t==='next' || t==='tamam') b.click() })"`);
222
- this.cmd("wait 1000");
223
- } catch {}
224
- if (step.waitMs) this.cmd(`wait ${step.waitMs}`);
225
- return;
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]}']`;
226
214
  } catch {}
215
+ try {
216
+ const thisDir = new URL(".", import.meta.url).pathname;
217
+ const pkgDir = process.env.SOCIAL_AGENT_PKG || join(thisDir, "../..");
218
+ console.log(` [upload] helper: ${btnSel}`);
219
+ const helperResult = execSync(
220
+ `npx tsx "${join(pkgDir, 'core/upload-helper.ts')}" ${CDP_PORT} "${btnSel}" "${filePath}"`,
221
+ { encoding: "utf-8", timeout: 60000 }
222
+ ).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
+ }
227
236
 
228
237
  throw new Error(`Upload: ${filePath}`);
229
238
  }