social-agent-cli 2.0.0 → 2.1.1

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
@@ -3,6 +3,7 @@ import { loadMap } from "./mapper.js";
3
3
  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
+ import { injectWatcher, hasUnexpectedChange } from "../core/page-watcher.js";
6
7
  import type { Post, PostResult } from "../core/types.js";
7
8
  import { join } from "node:path";
8
9
  import { PATHS } from "../core/config.js";
@@ -82,9 +83,10 @@ export async function runCommand(
82
83
  await page.waitForTimeout(3000);
83
84
  }
84
85
 
85
- let steps = [...map.steps];
86
+ // Periferik görüş - MutationObserver enjekte et
87
+ await injectWatcher(page);
86
88
 
87
- // IMAGE parametresi var ama map'te upload yok → planner learn_new ile öğretecek
89
+ let steps = [...map.steps];
88
90
  let stepIdx = 0;
89
91
  let healAttempt = 0;
90
92
 
@@ -114,7 +116,25 @@ export async function runCommand(
114
116
  }
115
117
 
116
118
  stepIdx++;
117
- healAttempt = 0; // başarılı, heal sayacını sıfırla
119
+ healAttempt = 0;
120
+
121
+ // 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
+ const change = await hasUnexpectedChange(page);
124
+ if (change.changed) {
125
+ 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];
137
+ }
118
138
  } catch (err: any) {
119
139
  healAttempt++;
120
140
  const efforts = ["low", "medium", "medium"];
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Page Watcher - İnsan beyni modeliyle sayfa izleme
3
+ *
4
+ * Beyin modeli:
5
+ * 1. Periferik görüş → MutationObserver ile DOM değişikliklerini sürekli izle
6
+ * 2. Değişiklik algılama → Yeni dialog/modal/overlay çıktığında algıla
7
+ * 3. Dikkat yönlendirme → Beklenmeyen element → otomatik odaklan
8
+ * 4. Aksiyon-algı döngüsü → Her step sonrası durumu doğrula
9
+ * 5. Tahmin → Beklenen sonuçla gerçeği karşılaştır
10
+ */
11
+
12
+ import type { Page } from "playwright";
13
+
14
+ export interface PageState {
15
+ hasNewDialog: boolean;
16
+ hasNewOverlay: boolean;
17
+ hasError: boolean;
18
+ newElements: string[]; // yeni eklenen önemli elementlerin açıklamaları
19
+ ariaSnapshot: string;
20
+ }
21
+
22
+ /**
23
+ * Sayfaya MutationObserver enjekte et - periferik görüş
24
+ * DOM'a eklenen dialog, modal, overlay, alert gibi elementleri yakalar
25
+ */
26
+ export async function injectWatcher(page: Page): Promise<void> {
27
+ await page.evaluate(`(() => {
28
+ if (window.__socialAgentWatcher) return;
29
+ window.__socialAgentWatcher = {
30
+ changes: [],
31
+ newDialogs: [],
32
+ newOverlays: [],
33
+ errors: [],
34
+ };
35
+
36
+ const watcher = window.__socialAgentWatcher;
37
+
38
+ const observer = new MutationObserver((mutations) => {
39
+ for (const mutation of mutations) {
40
+ for (const node of mutation.addedNodes) {
41
+ if (!(node instanceof HTMLElement)) continue;
42
+
43
+ const role = node.getAttribute('role');
44
+ const ariaModal = node.getAttribute('aria-modal');
45
+ const tag = node.tagName.toLowerCase();
46
+
47
+ // Dialog/Modal algılama - standart + heuristik
48
+ const isDialog = role === 'dialog' || role === 'alertdialog' || ariaModal === 'true' || tag === 'dialog';
49
+ const style = window.getComputedStyle(node);
50
+ const isFloating = (style.position === 'fixed' || style.position === 'absolute') && style.zIndex && parseInt(style.zIndex) > 50;
51
+ const cls = (node.className?.toString?.() || '').toLowerCase();
52
+ const looksLikeModal = cls.match(/modal|dialog|popup|overlay|drawer|sheet|toast|snackbar|dropdown|popover/);
53
+
54
+ if (isDialog || (isFloating && looksLikeModal)) {
55
+ watcher.newDialogs.push({
56
+ role: role || tag,
57
+ ariaLabel: node.getAttribute('aria-label') || '',
58
+ text: node.textContent?.substring(0, 200) || '',
59
+ className: cls.substring(0, 100),
60
+ time: Date.now()
61
+ });
62
+ }
63
+
64
+ // Overlay/backdrop algılama
65
+ if (isFloating && !isDialog && !looksLikeModal) {
66
+ const area = node.offsetWidth * node.offsetHeight;
67
+ const screenArea = window.innerWidth * window.innerHeight;
68
+ // Ekranın %30'undan büyük fixed element = muhtemelen overlay
69
+ if (area > screenArea * 0.3) {
70
+ watcher.newOverlays.push({
71
+ tag: tag,
72
+ class: cls.substring(0, 100),
73
+ time: Date.now()
74
+ });
75
+ }
76
+ }
77
+
78
+ // Error mesajı algılama - standart + heuristik
79
+ const isError = role === 'alert' || node.getAttribute('aria-live') === 'assertive';
80
+ const looksLikeError = cls.match(/error|warning|alert|danger|notification|banner/);
81
+ if (isError || looksLikeError) {
82
+ watcher.errors.push({
83
+ text: node.textContent?.substring(0, 300) || '',
84
+ time: Date.now()
85
+ });
86
+ }
87
+ }
88
+ }
89
+ });
90
+
91
+ observer.observe(document.body, {
92
+ childList: true,
93
+ subtree: true,
94
+ });
95
+ })()`);
96
+ }
97
+
98
+ /**
99
+ * Sayfanın mevcut durumunu oku - değişiklik algılama
100
+ */
101
+ export async function getPageState(page: Page): Promise<PageState> {
102
+ const watcherData = await page.evaluate(`(() => {
103
+ const w = window.__socialAgentWatcher;
104
+ if (!w) return { newDialogs: [], newOverlays: [], errors: [] };
105
+ const data = {
106
+ newDialogs: [...w.newDialogs],
107
+ newOverlays: [...w.newOverlays],
108
+ errors: [...w.errors],
109
+ };
110
+ // Reset - bir kere okuduktan sonra temizle
111
+ w.newDialogs = [];
112
+ w.newOverlays = [];
113
+ w.errors = [];
114
+ return data;
115
+ })()`).catch(() => ({ newDialogs: [], newOverlays: [], errors: [] })) as any;
116
+
117
+ const newElements: string[] = [];
118
+
119
+ for (const d of watcherData.newDialogs) {
120
+ newElements.push(`[dialog] ${d.ariaLabel || d.text.substring(0, 50)}`);
121
+ }
122
+ for (const o of watcherData.newOverlays) {
123
+ newElements.push(`[overlay] ${o.tag}.${o.class.substring(0, 30)}`);
124
+ }
125
+ for (const e of watcherData.errors) {
126
+ newElements.push(`[error] ${e.text.substring(0, 50)}`);
127
+ }
128
+
129
+ let ariaSnapshot = "";
130
+ try {
131
+ ariaSnapshot = await page.locator('body').ariaSnapshot();
132
+ } catch {}
133
+
134
+ return {
135
+ hasNewDialog: watcherData.newDialogs.length > 0,
136
+ hasNewOverlay: watcherData.newOverlays.length > 0,
137
+ hasError: watcherData.errors.length > 0,
138
+ newElements,
139
+ ariaSnapshot,
140
+ };
141
+ }
142
+
143
+ /**
144
+ * Step sonrası durumu doğrula - aksiyon-algı döngüsü
145
+ * Beklenmeyen element varsa true döner
146
+ */
147
+ export async function hasUnexpectedChange(page: Page): Promise<{ changed: boolean; description: string }> {
148
+ const state = await getPageState(page);
149
+
150
+ if (state.hasNewDialog) {
151
+ return {
152
+ changed: true,
153
+ description: `Yeni dialog açıldı: ${state.newElements.filter(e => e.startsWith('[dialog]')).join(', ')}`,
154
+ };
155
+ }
156
+
157
+ if (state.hasError) {
158
+ return {
159
+ changed: true,
160
+ description: `Hata mesajı: ${state.newElements.filter(e => e.startsWith('[error]')).join(', ')}`,
161
+ };
162
+ }
163
+
164
+ if (state.hasNewOverlay) {
165
+ return {
166
+ changed: true,
167
+ description: `Yeni overlay: ${state.newElements.filter(e => e.startsWith('[overlay]')).join(', ')}`,
168
+ };
169
+ }
170
+
171
+ return { changed: false, description: "" };
172
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "social-agent-cli",
3
- "version": "2.0.0",
3
+ "version": "2.1.1",
4
4
  "description": "AI-powered social media agent - free APIs + browser automation with self-healing selectors",
5
5
  "type": "module",
6
6
  "bin": {