social-agent-cli 2.1.1 → 2.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
@@ -4,6 +4,7 @@ import { createPlan, type ExecutionPlan, type PlanStep } from "./planner.js";
4
4
  import { healAndContinue } from "./mapper.js";
5
5
  import { saveToHistory } from "../core/history.js";
6
6
  import { injectWatcher, hasUnexpectedChange } from "../core/page-watcher.js";
7
+ import { tryReflex } from "../core/reflexes.js";
7
8
  import type { Post, PostResult } from "../core/types.js";
8
9
  import { join } from "node:path";
9
10
  import { PATHS } from "../core/config.js";
@@ -119,36 +120,52 @@ export async function runCommand(
119
120
  healAttempt = 0;
120
121
 
121
122
  // Aksiyon-algı döngüsü: step sonrası beklenmeyen değişiklik var mı?
122
- await page.waitForTimeout(300); // kısa bekleme - DOM güncellensin
123
+ await page.waitForTimeout(300);
123
124
  const change = await hasUnexpectedChange(page);
124
125
  if (change.changed) {
125
126
  console.log(` ⚡ ${change.description}`);
126
- // Beklenmeyen element çıktı - AI'a sor ne yapmalı
127
- const healScreenshot = join(PATHS.screenshots, `${platform}_unexpected.png`);
128
- await page.screenshot({ path: healScreenshot });
129
- const ariaSnap = await page.locator('body').ariaSnapshot();
130
-
131
- const newSteps = await healAndContinue(
132
- platform, stepIdx, steps, healScreenshot, ariaSnap,
133
- `Beklenmeyen değişiklik: ${change.description}. Bu element'i handle edip kalan adımlara devam et.`,
134
- post.content || step.description, "low"
135
- );
136
- steps = [...steps.slice(0, stepIdx), ...newSteps];
127
+
128
+ // Sistem 1: Refleks - hızlı, otomatik
129
+ const reflex = await tryReflex(page, change.description);
130
+ if (reflex.handled) {
131
+ console.log(` ↩ Refleks: ${reflex.action}`);
132
+ } else {
133
+ // Sistem 2: AI - yavaş, bilinçli (sadece refleks çözemediğinde)
134
+ console.log(` 🧠 Refleks çözemedi, AI devreye giriyor...`);
135
+ const healScreenshot = join(PATHS.screenshots, `${platform}_unexpected.png`);
136
+ await page.screenshot({ path: healScreenshot });
137
+ const ariaSnap = await page.locator('body').ariaSnapshot();
138
+
139
+ const newSteps = await healAndContinue(
140
+ platform, stepIdx, steps, healScreenshot, ariaSnap,
141
+ `Beklenmeyen değişiklik: ${change.description}. Bu element'i handle edip kalan adımlara devam et.`,
142
+ post.content || step.description, "low"
143
+ );
144
+ steps = [...steps.slice(0, stepIdx), ...newSteps];
145
+ }
137
146
  }
138
147
  } catch (err: any) {
148
+ console.log(` ✗ ${err.message}`);
149
+
150
+ // Sistem 1: Refleks - hata sonrası otomatik düzeltme
151
+ const reflex = await tryReflex(page, err.message);
152
+ if (reflex.handled) {
153
+ console.log(` ↩ Refleks: ${reflex.action}`);
154
+ continue; // aynı step'i tekrar dene
155
+ }
156
+
157
+ // Sistem 2: AI
139
158
  healAttempt++;
140
159
  const efforts = ["low", "medium", "medium"];
141
160
  const effort = efforts[Math.min(healAttempt - 1, efforts.length - 1)];
142
161
 
143
- console.log(` ${err.message}`);
144
- console.log(` → AI düzeltiyor (deneme ${healAttempt}/3)...`);
162
+ console.log(` 🧠 AI düzeltiyor (deneme ${healAttempt}/3)...`);
145
163
 
146
164
  if (healAttempt > 3) {
147
- console.log(` [heal] 3 denemede çözülemedi, atlanıyor.`);
165
+ console.log(` 3 denemede çözülemedi, atlanıyor.`);
148
166
  break;
149
167
  }
150
168
 
151
- // Screenshot + DOM al
152
169
  const healScreenshot = join(PATHS.screenshots, `${platform}_heal_${stepIdx}.png`);
153
170
  await page.screenshot({ path: healScreenshot });
154
171
 
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Reflexes - Sistem 1 (Hızlı, Otomatik)
3
+ *
4
+ * Kahneman'ın "Thinking, Fast and Slow" modeli:
5
+ * - Sistem 1: Hızlı, otomatik, refleks tepkiler (bu dosya)
6
+ * - Sistem 2: Yavaş, bilinçli, AI destekli (heal mekanizması)
7
+ *
8
+ * Bilinen UI pattern'larını AI çağırmadan milisaniyede çözer.
9
+ */
10
+
11
+ import type { Page } from "playwright";
12
+
13
+ interface ReflexResult {
14
+ handled: boolean;
15
+ action: string; // ne yapıldı
16
+ }
17
+
18
+ /**
19
+ * Sayfadaki beklenmeyen durumu refleksle çözmeye çalış.
20
+ * Çözemezse handled: false döner → Sistem 2 (AI) devreye girer.
21
+ */
22
+ export async function tryReflex(page: Page, changeDescription: string): Promise<ReflexResult> {
23
+ // 1. Modal/Dialog kapatma refleksi
24
+ const dismissed = await tryDismissDialog(page);
25
+ if (dismissed.handled) return dismissed;
26
+
27
+ // 2. Cookie/consent banner kapatma
28
+ const cookie = await tryDismissCookieBanner(page);
29
+ if (cookie.handled) return cookie;
30
+
31
+ // 3. File input algılama - dosya bekliyor mu?
32
+ const fileInput = await tryDetectFileInput(page);
33
+ if (fileInput.handled) return fileInput;
34
+
35
+ // 4. Error/alert kapatma
36
+ const error = await tryDismissError(page);
37
+ if (error.handled) return error;
38
+
39
+ // 5. Overlay/backdrop tıklama
40
+ const overlay = await tryDismissOverlay(page);
41
+ if (overlay.handled) return overlay;
42
+
43
+ return { handled: false, action: "reflex bulunamadı" };
44
+ }
45
+
46
+ /**
47
+ * Dialog/Modal kapatma - "close", "cancel", "dismiss", "x" butonlarını ara
48
+ */
49
+ async function tryDismissDialog(page: Page): Promise<ReflexResult> {
50
+ // Önce dialog'un post oluşturma dialog'u olup olmadığını kontrol et - onu kapatma!
51
+ const isComposeDialog = await page.evaluate(`(() => {
52
+ const dialogs = document.querySelectorAll('[role="dialog"], [aria-modal="true"], dialog');
53
+ for (const d of dialogs) {
54
+ const text = (d.textContent || '').toLowerCase();
55
+ if (text.includes('post') || text.includes('gönderi') || text.includes('tweet') ||
56
+ text.includes('paylaş') || text.includes('share') || text.includes('compose') ||
57
+ text.includes('yaz') || text.includes('düzenle') || text.includes('edit')) {
58
+ return true;
59
+ }
60
+ }
61
+ return false;
62
+ })()`).catch(() => false);
63
+
64
+ // Compose dialog'u kapatma!
65
+ if (isComposeDialog) return { handled: false, action: "compose dialog - kapatılmadı" };
66
+
67
+ // Gereksiz dialog'ları kapat
68
+ const closeSelectors = [
69
+ 'button[aria-label="Close"]',
70
+ 'button[aria-label="Kapat"]',
71
+ 'button[aria-label="Dismiss"]',
72
+ 'button[aria-label="İptal"]',
73
+ 'button[aria-label="Cancel"]',
74
+ '[role="dialog"] button[aria-label*="close" i]',
75
+ '[role="dialog"] button[aria-label*="kapat" i]',
76
+ '[aria-modal="true"] button[aria-label*="close" i]',
77
+ '[aria-modal="true"] button[aria-label*="kapat" i]',
78
+ 'dialog button[aria-label*="close" i]',
79
+ ];
80
+
81
+ for (const sel of closeSelectors) {
82
+ try {
83
+ const btn = page.locator(sel).first();
84
+ if (await btn.isVisible({ timeout: 500 })) {
85
+ await btn.click();
86
+ await page.waitForTimeout(300);
87
+ return { handled: true, action: `dialog kapatıldı: ${sel}` };
88
+ }
89
+ } catch {}
90
+ }
91
+
92
+ return { handled: false, action: "" };
93
+ }
94
+
95
+ /**
96
+ * Cookie/consent banner kapatma
97
+ */
98
+ async function tryDismissCookieBanner(page: Page): Promise<ReflexResult> {
99
+ const cookieSelectors = [
100
+ 'button[aria-label*="cookie" i]',
101
+ 'button[aria-label*="çerez" i]',
102
+ 'button[aria-label*="Accept" i]',
103
+ 'button[aria-label*="Kabul" i]',
104
+ 'button[aria-label*="consent" i]',
105
+ '[id*="cookie"] button',
106
+ '[class*="cookie"] button',
107
+ '[class*="consent"] button',
108
+ ];
109
+
110
+ for (const sel of cookieSelectors) {
111
+ try {
112
+ const btn = page.locator(sel).first();
113
+ if (await btn.isVisible({ timeout: 300 })) {
114
+ await btn.click();
115
+ await page.waitForTimeout(300);
116
+ return { handled: true, action: `cookie banner kapatıldı: ${sel}` };
117
+ }
118
+ } catch {}
119
+ }
120
+
121
+ return { handled: false, action: "" };
122
+ }
123
+
124
+ /**
125
+ * Yeni file input algılama
126
+ */
127
+ async function tryDetectFileInput(page: Page): Promise<ReflexResult> {
128
+ try {
129
+ const hasNewFileInput = await page.evaluate(`(() => {
130
+ const inputs = document.querySelectorAll('input[type="file"]');
131
+ return inputs.length > 0;
132
+ })()`);
133
+
134
+ if (hasNewFileInput) {
135
+ return { handled: true, action: "file input algılandı - upload step'i çalışabilir" };
136
+ }
137
+ } catch {}
138
+
139
+ return { handled: false, action: "" };
140
+ }
141
+
142
+ /**
143
+ * Error/alert kapatma
144
+ */
145
+ async function tryDismissError(page: Page): Promise<ReflexResult> {
146
+ const errorSelectors = [
147
+ '[role="alert"] button',
148
+ '[role="alert"] [aria-label*="close" i]',
149
+ '[role="alert"] [aria-label*="kapat" i]',
150
+ '[aria-live="assertive"] button',
151
+ ];
152
+
153
+ for (const sel of errorSelectors) {
154
+ try {
155
+ const btn = page.locator(sel).first();
156
+ if (await btn.isVisible({ timeout: 300 })) {
157
+ await btn.click();
158
+ await page.waitForTimeout(200);
159
+ return { handled: true, action: `error kapatıldı: ${sel}` };
160
+ }
161
+ } catch {}
162
+ }
163
+
164
+ return { handled: false, action: "" };
165
+ }
166
+
167
+ /**
168
+ * Overlay/backdrop tıklama ile kapatma
169
+ */
170
+ async function tryDismissOverlay(page: Page): Promise<ReflexResult> {
171
+ try {
172
+ // ESC tuşu ile kapatmayı dene
173
+ await page.keyboard.press("Escape");
174
+ await page.waitForTimeout(300);
175
+
176
+ // Dialog hâlâ var mı kontrol et
177
+ const stillHasDialog = await page.evaluate(`(() => {
178
+ return document.querySelectorAll('[role="dialog"], [aria-modal="true"]').length > 0;
179
+ })()`);
180
+
181
+ if (!stillHasDialog) {
182
+ return { handled: true, action: "overlay ESC ile kapatıldı" };
183
+ }
184
+ } catch {}
185
+
186
+ return { handled: false, action: "" };
187
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "social-agent-cli",
3
- "version": "2.1.1",
3
+ "version": "2.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": {