social-agent-cli 1.4.0 → 1.6.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/mapper.ts +44 -21
- package/ai/planner.ts +12 -3
- package/ai/runner.ts +6 -52
- package/maps/linkedin.json +70 -0
- package/maps/x.json +66 -67
- package/package.json +1 -1
- package/platforms/browser/driver.ts +4 -38
- package/maps/linkedin_linkedin_post.json +0 -66
- package/maps/linkedin_linkedin_send_connection_request.json +0 -37
- package/maps/linkedin_post.json +0 -66
- package/maps/x_post.json +0 -64
- package/maps/x_tweet_be/304/237enmeyi_/303/266/304/237ren.json +0 -39
- package/maps/x_tweet_retweet_/303/266/304/237ren.json +0 -46
package/ai/mapper.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { readFileSync, writeFileSync, existsSync, mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync, mkdtempSync, rmSync, readdirSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { PATHS } from "../core/config.js";
|
|
@@ -21,7 +21,7 @@ const SELECTOR_MAP_SCHEMA = JSON.stringify({
|
|
|
21
21
|
items: {
|
|
22
22
|
type: "object",
|
|
23
23
|
properties: {
|
|
24
|
-
action: { type: "string", enum: ["click", "type", "wait", "keypress", "goto"] },
|
|
24
|
+
action: { type: "string", enum: ["click", "type", "wait", "keypress", "goto", "upload"] },
|
|
25
25
|
description: { type: "string" },
|
|
26
26
|
selector: { type: "string" },
|
|
27
27
|
value: { type: "string" },
|
|
@@ -168,32 +168,52 @@ function claude(opts: ClaudeOptions): Promise<any> {
|
|
|
168
168
|
});
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
-
// ── Map CRUD
|
|
171
|
+
// ── Map CRUD (platform başına tek dosya) ─────────────────────
|
|
172
|
+
|
|
173
|
+
interface PlatformMaps {
|
|
174
|
+
[action: string]: SelectorMap;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function loadPlatformFile(platform: string): PlatformMaps {
|
|
178
|
+
const mapPath = join(PATHS.maps, `${platform}.json`);
|
|
179
|
+
if (!existsSync(mapPath)) return {};
|
|
180
|
+
try {
|
|
181
|
+
return JSON.parse(readFileSync(mapPath, "utf-8"));
|
|
182
|
+
} catch {
|
|
183
|
+
return {};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function savePlatformFile(platform: string, maps: PlatformMaps): void {
|
|
188
|
+
const mapPath = join(PATHS.maps, `${platform}.json`);
|
|
189
|
+
writeFileSync(mapPath, JSON.stringify(maps, null, 2));
|
|
190
|
+
}
|
|
172
191
|
|
|
173
192
|
export function loadMap(platform: string, action = "post"): SelectorMap | null {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
return JSON.parse(readFileSync(mapPath, "utf-8"));
|
|
193
|
+
const maps = loadPlatformFile(platform);
|
|
194
|
+
return maps[action] || null;
|
|
177
195
|
}
|
|
178
196
|
|
|
179
197
|
export function saveMap(map: SelectorMap): void {
|
|
180
198
|
const action = map.action || "post";
|
|
181
|
-
const
|
|
182
|
-
|
|
199
|
+
const maps = loadPlatformFile(map.platform);
|
|
200
|
+
maps[action] = map;
|
|
201
|
+
savePlatformFile(map.platform, maps);
|
|
183
202
|
}
|
|
184
203
|
|
|
185
204
|
export function listMaps(platform?: string): SelectorMap[] {
|
|
186
|
-
const
|
|
187
|
-
const maps: SelectorMap[] = [];
|
|
205
|
+
const results: SelectorMap[] = [];
|
|
188
206
|
try {
|
|
189
207
|
for (const file of readdirSync(PATHS.maps)) {
|
|
190
208
|
if (!file.endsWith(".json")) continue;
|
|
191
|
-
if (platform &&
|
|
192
|
-
const
|
|
193
|
-
|
|
209
|
+
if (platform && file !== `${platform}.json`) continue;
|
|
210
|
+
const maps = JSON.parse(readFileSync(join(PATHS.maps, file), "utf-8"));
|
|
211
|
+
for (const map of Object.values(maps) as SelectorMap[]) {
|
|
212
|
+
results.push(map);
|
|
213
|
+
}
|
|
194
214
|
}
|
|
195
215
|
} catch {}
|
|
196
|
-
return
|
|
216
|
+
return results;
|
|
197
217
|
}
|
|
198
218
|
|
|
199
219
|
// ── Map Oluştur ──────────────────────────────────────────────
|
|
@@ -209,11 +229,13 @@ export async function generateMap(
|
|
|
209
229
|
|
|
210
230
|
GÖREV: "${taskDescription}"
|
|
211
231
|
|
|
212
|
-
|
|
213
|
-
${domSnapshot
|
|
232
|
+
Sayfanın Accessibility Tree'si (ARIA Snapshot):
|
|
233
|
+
${domSnapshot}
|
|
214
234
|
|
|
215
235
|
Kurallar:
|
|
216
|
-
-
|
|
236
|
+
- ARIA snapshot'taki role ve name bilgilerini kullanarak selector oluştur
|
|
237
|
+
- aria-label, role, data-testid, id tercih et
|
|
238
|
+
- Dosya/görsel/video yükleme için "upload" action kullan (click DEĞİL). Upload action hidden input[type=file]'a dosyayı programatik olarak set eder, file picker açmaz. value alanına {{IMAGE}} yaz.
|
|
217
239
|
- Her adım için 2+ fallback selector
|
|
218
240
|
- Parametreler için placeholder kullan: {{CONTENT}} = metin, {{USERNAME}} = kullanıcı adı, {{URL}} = link, vs.
|
|
219
241
|
- Hangi parametreleri kullandığını "parameters" alanında listele
|
|
@@ -260,7 +282,7 @@ const REMAINING_STEPS_SCHEMA = JSON.stringify({
|
|
|
260
282
|
items: {
|
|
261
283
|
type: "object",
|
|
262
284
|
properties: {
|
|
263
|
-
action: { type: "string", enum: ["click", "type", "wait", "keypress", "goto"] },
|
|
285
|
+
action: { type: "string", enum: ["click", "type", "wait", "keypress", "goto", "upload"] },
|
|
264
286
|
description: { type: "string" },
|
|
265
287
|
selector: { type: "string" },
|
|
266
288
|
value: { type: "string" },
|
|
@@ -311,15 +333,16 @@ Başarısız olan adım (#${failedStepIndex + 1}):
|
|
|
311
333
|
Kalan orijinal adımlar:
|
|
312
334
|
${remainingOriginal.map((s, i) => ` ${failedStepIndex + i + 1}. [${s.action}] ${s.description} → ${s.selector}`).join("\n")}
|
|
313
335
|
|
|
314
|
-
|
|
315
|
-
${domSnapshot
|
|
336
|
+
Sayfanın Accessibility Tree'si:
|
|
337
|
+
${domSnapshot}
|
|
316
338
|
|
|
317
339
|
GÖREV: Ekranın mevcut durumunu analiz et. Amaca ulaşmak için kalan adımları yeniden planla.
|
|
318
340
|
- Belki selector değişmiş → yeni selector bul
|
|
319
341
|
- Belki ekstra bir adım gerekiyor (popup kapatma, scroll, vb.)
|
|
320
342
|
- Belki farklı bir yol izlenmeli
|
|
321
343
|
- {{CONTENT}} = post metni placeholder'ı
|
|
322
|
-
- data-testid, aria-label, role tercih et, her adıma 2+ fallback selector ver
|
|
344
|
+
- data-testid, aria-label, role tercih et, her adıma 2+ fallback selector ver
|
|
345
|
+
- Dosya yükleme için "upload" action kullan (click DEĞİL). value: {{IMAGE}}`;
|
|
323
346
|
|
|
324
347
|
console.log(`\n[heal] AI ekranı analiz ediyor (effort: ${effort})...\n`);
|
|
325
348
|
|
package/ai/planner.ts
CHANGED
|
@@ -59,9 +59,18 @@ export function listAllMaps(platform?: string): SelectorMap[] {
|
|
|
59
59
|
try {
|
|
60
60
|
for (const file of readdirSync(PATHS.maps)) {
|
|
61
61
|
if (!file.endsWith(".json")) continue;
|
|
62
|
-
if (platform &&
|
|
63
|
-
const
|
|
64
|
-
|
|
62
|
+
if (platform && file !== `${platform}.json`) continue;
|
|
63
|
+
const content = JSON.parse(readFileSync(join(PATHS.maps, file), "utf-8"));
|
|
64
|
+
// Yeni format: { "post": {...}, "post_with_image": {...} }
|
|
65
|
+
if (content.steps) {
|
|
66
|
+
// Eski format: direkt map objesi
|
|
67
|
+
maps.push(content);
|
|
68
|
+
} else {
|
|
69
|
+
// Yeni format: action bazlı obje
|
|
70
|
+
for (const map of Object.values(content) as SelectorMap[]) {
|
|
71
|
+
if (map.steps) maps.push(map);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
65
74
|
}
|
|
66
75
|
} catch {}
|
|
67
76
|
return maps;
|
package/ai/runner.ts
CHANGED
|
@@ -160,35 +160,7 @@ export async function runCommand(
|
|
|
160
160
|
const healScreenshot = join(PATHS.screenshots, `${platform}_heal_${stepIdx}.png`);
|
|
161
161
|
await page.screenshot({ path: healScreenshot });
|
|
162
162
|
|
|
163
|
-
const
|
|
164
|
-
var SKIP = {"script":1,"style":1,"noscript":1,"link":1,"meta":1};
|
|
165
|
-
function ser(el, d, b) {
|
|
166
|
-
if (d > 15 || b.c <= 0) return "";
|
|
167
|
-
var t = el.tagName.toLowerCase();
|
|
168
|
-
if (SKIP[t]) return "";
|
|
169
|
-
if (t === "svg") return "<svg/>";
|
|
170
|
-
var st = window.getComputedStyle(el);
|
|
171
|
-
if (st.display === "none" || st.visibility === "hidden") return "";
|
|
172
|
-
var at = [];
|
|
173
|
-
for (var i = 0; i < el.attributes.length; i++) {
|
|
174
|
-
var a = el.attributes[i];
|
|
175
|
-
if (a.name === "style") continue;
|
|
176
|
-
at.push(a.name + '="' + (a.value.length > 150 ? a.value.substring(0,150) : a.value).replace(/"/g,"'") + '"');
|
|
177
|
-
}
|
|
178
|
-
var as = at.length ? " " + at.join(" ") : "";
|
|
179
|
-
var dt = "";
|
|
180
|
-
for (var j = 0; j < el.childNodes.length; j++) {
|
|
181
|
-
if (el.childNodes[j].nodeType === 3) { var tx = (el.childNodes[j].textContent||"").trim(); if (tx) dt += tx.substring(0,100); }
|
|
182
|
-
}
|
|
183
|
-
var ch = "";
|
|
184
|
-
for (var k = 0; k < el.children.length; k++) ch += ser(el.children[k], d+1, b);
|
|
185
|
-
if (!as && !dt && !ch) return "";
|
|
186
|
-
var r = "<"+t+as+">"+dt+ch+"</"+t+">";
|
|
187
|
-
b.c -= r.length;
|
|
188
|
-
return r;
|
|
189
|
-
}
|
|
190
|
-
return ser(document.body, 0, {c:500000});
|
|
191
|
-
})()`);
|
|
163
|
+
const ariaSnap = await page.locator('body').ariaSnapshot();
|
|
192
164
|
|
|
193
165
|
// AI kaldığı yerden devam etsin (her denemede effort artar)
|
|
194
166
|
const newSteps = await healAndContinue(
|
|
@@ -196,7 +168,7 @@ export async function runCommand(
|
|
|
196
168
|
stepIdx,
|
|
197
169
|
steps,
|
|
198
170
|
healScreenshot,
|
|
199
|
-
|
|
171
|
+
ariaSnap,
|
|
200
172
|
err.message,
|
|
201
173
|
post.content || step.description,
|
|
202
174
|
effort
|
|
@@ -352,31 +324,13 @@ async function executeMapStep(
|
|
|
352
324
|
break;
|
|
353
325
|
|
|
354
326
|
case "upload": {
|
|
355
|
-
// Dosya yükleme - hidden file input'a dosya set et
|
|
356
327
|
const filePath = rp(step.value || "{{IMAGE}}");
|
|
357
328
|
if (!filePath || filePath.includes("{{")) break; // dosya yoksa atla
|
|
358
329
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
await input.setInputFiles(filePath);
|
|
364
|
-
uploaded = true;
|
|
365
|
-
break;
|
|
366
|
-
} catch {}
|
|
367
|
-
}
|
|
368
|
-
if (!uploaded) {
|
|
369
|
-
// Hidden input bulunamazsa, file chooser ile dene
|
|
370
|
-
try {
|
|
371
|
-
const [fileChooser] = await Promise.all([
|
|
372
|
-
page.waitForEvent("filechooser", { timeout: 5000 }),
|
|
373
|
-
page.click(selectors[0] || "[data-testid='fileInput']"),
|
|
374
|
-
]);
|
|
375
|
-
await fileChooser.setFiles(filePath);
|
|
376
|
-
uploaded = true;
|
|
377
|
-
} catch {}
|
|
378
|
-
}
|
|
379
|
-
if (!uploaded) throw new Error(`Upload failed: ${filePath}`);
|
|
330
|
+
// Hidden input[type=file]'a direkt setInputFiles - butona tıklamadan
|
|
331
|
+
const fileInput = page.locator('input[type="file"]').first();
|
|
332
|
+
await fileInput.setInputFiles(filePath);
|
|
333
|
+
|
|
380
334
|
if (step.waitMs) await page.waitForTimeout(step.waitMs);
|
|
381
335
|
break;
|
|
382
336
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"post": {
|
|
3
|
+
"platform": "linkedin",
|
|
4
|
+
"action": "post",
|
|
5
|
+
"description": "LinkedIn üzerinden yeni bir metin postu paylaşma",
|
|
6
|
+
"version": 1,
|
|
7
|
+
"lastUpdated": "2026-03-14T12:00:00.000Z",
|
|
8
|
+
"parameters": [
|
|
9
|
+
"{{CONTENT}}"
|
|
10
|
+
],
|
|
11
|
+
"steps": [
|
|
12
|
+
{
|
|
13
|
+
"action": "click",
|
|
14
|
+
"description": "Gönderi oluştur kutusuna tıkla",
|
|
15
|
+
"selector": "button.share-box-feed-entry__trigger",
|
|
16
|
+
"fallbackSelectors": [
|
|
17
|
+
"[aria-label*='Start a post']",
|
|
18
|
+
"[aria-label*='Gönderi başlat']",
|
|
19
|
+
"button[aria-label*='post']"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"action": "wait",
|
|
24
|
+
"description": "Post oluşturma modalının açılmasını bekle",
|
|
25
|
+
"waitMs": 1500
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"action": "click",
|
|
29
|
+
"description": "Metin alanına tıkla",
|
|
30
|
+
"selector": "[role='textbox'][aria-label*='Text editor']",
|
|
31
|
+
"fallbackSelectors": [
|
|
32
|
+
"[role='textbox'][contenteditable='true']",
|
|
33
|
+
".ql-editor[contenteditable='true']",
|
|
34
|
+
"[aria-label*='Metin düzenleyici']"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"action": "type",
|
|
39
|
+
"description": "Post metnini yaz",
|
|
40
|
+
"selector": "[role='textbox'][aria-label*='Text editor']",
|
|
41
|
+
"value": "{{CONTENT}}",
|
|
42
|
+
"fallbackSelectors": [
|
|
43
|
+
"[role='textbox'][contenteditable='true']",
|
|
44
|
+
".ql-editor[contenteditable='true']"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"action": "wait",
|
|
49
|
+
"description": "Metin girişi sonrası bekleme",
|
|
50
|
+
"waitMs": 1000
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"action": "click",
|
|
54
|
+
"description": "Gönder butonuna tıkla",
|
|
55
|
+
"selector": "button.share-actions__primary-action",
|
|
56
|
+
"fallbackSelectors": [
|
|
57
|
+
"[aria-label='Post']",
|
|
58
|
+
"[aria-label='Gönder']",
|
|
59
|
+
"button[aria-label*='Post']",
|
|
60
|
+
"button[aria-label*='Paylaş']"
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"action": "wait",
|
|
65
|
+
"description": "Post gönderimini bekle",
|
|
66
|
+
"waitMs": 2000
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
}
|
package/maps/x.json
CHANGED
|
@@ -1,69 +1,68 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
]
|
|
2
|
+
"post": {
|
|
3
|
+
"platform": "x",
|
|
4
|
+
"action": "post",
|
|
5
|
+
"description": "X (Twitter) üzerinden yeni bir metin postu paylaşma",
|
|
6
|
+
"version": 1,
|
|
7
|
+
"lastUpdated": "2026-03-14T12:00:00.000Z",
|
|
8
|
+
"parameters": [
|
|
9
|
+
"{{CONTENT}}"
|
|
10
|
+
],
|
|
11
|
+
"steps": [
|
|
12
|
+
{
|
|
13
|
+
"action": "click",
|
|
14
|
+
"description": "Sidebar'daki post butonuna tıkla",
|
|
15
|
+
"selector": "[data-testid='SideNav_NewTweet_Button']",
|
|
16
|
+
"fallbackSelectors": [
|
|
17
|
+
"a[href='/compose/post']",
|
|
18
|
+
"[aria-label*='Post'][role='link']",
|
|
19
|
+
"[aria-label*='Gönder'][role='link']",
|
|
20
|
+
"[data-testid='FloatingActionButton']"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"action": "wait",
|
|
25
|
+
"description": "Compose dialog açılmasını bekle",
|
|
26
|
+
"waitMs": 1500
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"action": "click",
|
|
30
|
+
"description": "Metin alanına tıkla",
|
|
31
|
+
"selector": "[data-testid='tweetTextarea_0']",
|
|
32
|
+
"fallbackSelectors": [
|
|
33
|
+
"[role='textbox'][contenteditable='true']",
|
|
34
|
+
".public-DraftEditor-content"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"action": "type",
|
|
39
|
+
"description": "Post metnini yaz",
|
|
40
|
+
"selector": "[data-testid='tweetTextarea_0']",
|
|
41
|
+
"value": "{{CONTENT}}",
|
|
42
|
+
"fallbackSelectors": [
|
|
43
|
+
"[role='textbox'][contenteditable='true']",
|
|
44
|
+
".public-DraftEditor-content"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"action": "wait",
|
|
49
|
+
"description": "Metin girişi sonrası bekleme",
|
|
50
|
+
"waitMs": 500
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"action": "click",
|
|
54
|
+
"description": "Gönder butonuna tıkla",
|
|
55
|
+
"selector": "[data-testid='tweetButton']",
|
|
56
|
+
"fallbackSelectors": [
|
|
57
|
+
"[data-testid='tweetButtonInline']",
|
|
58
|
+
"[role='button'][data-testid*='tweet']"
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"action": "wait",
|
|
63
|
+
"description": "Post gönderimini bekle",
|
|
64
|
+
"waitMs": 2000
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
69
68
|
}
|
package/package.json
CHANGED
|
@@ -162,48 +162,14 @@ export class BrowserDriver {
|
|
|
162
162
|
const screenshotPath = join(PATHS.screenshots, `${this.platform}_learn_${actionName}.png`);
|
|
163
163
|
await page.screenshot({ path: screenshotPath, fullPage: false });
|
|
164
164
|
|
|
165
|
-
//
|
|
166
|
-
const
|
|
167
|
-
var SKIP = {"script":1,"style":1,"noscript":1,"link":1,"meta":1};
|
|
168
|
-
function ser(el, depth, budget) {
|
|
169
|
-
if (depth > 15 || budget.c <= 0) return "";
|
|
170
|
-
var tag = el.tagName.toLowerCase();
|
|
171
|
-
if (SKIP[tag]) return "";
|
|
172
|
-
if (tag === "svg") return "<svg/>";
|
|
173
|
-
var st = window.getComputedStyle(el);
|
|
174
|
-
if (st.display === "none" || st.visibility === "hidden") return "";
|
|
175
|
-
var attrs = [];
|
|
176
|
-
for (var i = 0; i < el.attributes.length; i++) {
|
|
177
|
-
var a = el.attributes[i];
|
|
178
|
-
if (a.name === "style") continue;
|
|
179
|
-
var v = a.value.length > 150 ? a.value.substring(0,150) : a.value;
|
|
180
|
-
attrs.push(a.name + '="' + v.replace(/"/g, "'") + '"');
|
|
181
|
-
}
|
|
182
|
-
var as = attrs.length ? " " + attrs.join(" ") : "";
|
|
183
|
-
var dt = "";
|
|
184
|
-
for (var j = 0; j < el.childNodes.length; j++) {
|
|
185
|
-
if (el.childNodes[j].nodeType === 3) {
|
|
186
|
-
var t = (el.childNodes[j].textContent || "").trim();
|
|
187
|
-
if (t) dt += t.substring(0, 100);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
var ch = "";
|
|
191
|
-
for (var k = 0; k < el.children.length; k++) {
|
|
192
|
-
ch += ser(el.children[k], depth + 1, budget);
|
|
193
|
-
}
|
|
194
|
-
if (!as && !dt && !ch) return "";
|
|
195
|
-
var r = "<" + tag + as + ">" + dt + ch + "</" + tag + ">";
|
|
196
|
-
budget.c -= r.length;
|
|
197
|
-
return r;
|
|
198
|
-
}
|
|
199
|
-
return ser(document.body, 0, {c: 500000});
|
|
200
|
-
})()`);
|
|
165
|
+
// ARIA Snapshot - accessibility tree (98x daha küçük, sadece interaktif elementler)
|
|
166
|
+
const ariaSnapshot = await page.locator('body').ariaSnapshot();
|
|
201
167
|
|
|
202
168
|
console.log(`[${this.platform}] Screenshot: ${screenshotPath}`);
|
|
203
|
-
console.log(`[${this.platform}]
|
|
169
|
+
console.log(`[${this.platform}] Accessibility tree: ${ariaSnapshot.length} bytes`);
|
|
204
170
|
console.log(`[${this.platform}] Claude analiz ediyor...`);
|
|
205
171
|
|
|
206
|
-
const map = await generateMap(this.platform, screenshotPath,
|
|
172
|
+
const map = await generateMap(this.platform, screenshotPath, ariaSnapshot, taskDescription, actionName);
|
|
207
173
|
|
|
208
174
|
console.log(`[${this.platform}] "${actionName}" map oluşturuldu (${map.steps.length} adım)`);
|
|
209
175
|
await this.close();
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"platform": "linkedin",
|
|
3
|
-
"version": 1,
|
|
4
|
-
"action": "linkedin_post",
|
|
5
|
-
"description": "LinkedIn platformunda metin içerikli post paylaşma",
|
|
6
|
-
"lastUpdated": "2026-03-14T11:33:00.454Z",
|
|
7
|
-
"parameters": [
|
|
8
|
-
"{{CONTENT}}"
|
|
9
|
-
],
|
|
10
|
-
"steps": [
|
|
11
|
-
{
|
|
12
|
-
"action": "goto",
|
|
13
|
-
"description": "LinkedIn ana sayfasına git",
|
|
14
|
-
"url": "https://www.linkedin.com/feed/"
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
"action": "wait",
|
|
18
|
-
"description": "Sayfanın yüklenmesini bekle",
|
|
19
|
-
"waitMs": 3000
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"action": "click",
|
|
23
|
-
"description": "Gönderi oluşturma kutusuna tıkla",
|
|
24
|
-
"selector": "button.rBknNMyPiauEsphJukuFITVzZCRzAenPRs",
|
|
25
|
-
"fallbackSelectors": [
|
|
26
|
-
".share-box-feed-entry__top-bar button.artdeco-button--tertiary",
|
|
27
|
-
"button[class*='share-box-feed-entry']"
|
|
28
|
-
]
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"action": "wait",
|
|
32
|
-
"description": "Post oluşturma modalının açılmasını bekle",
|
|
33
|
-
"waitMs": 2000
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"action": "type",
|
|
37
|
-
"description": "Post içeriğini yaz",
|
|
38
|
-
"selector": "div.ql-editor[role='textbox']",
|
|
39
|
-
"fallbackSelectors": [
|
|
40
|
-
"div[aria-label='Metin Düzenleyici']",
|
|
41
|
-
"div.editor-content div[contenteditable='true']",
|
|
42
|
-
"div[data-placeholder='Ne hakkında konuşmak istiyorsunuz?']"
|
|
43
|
-
],
|
|
44
|
-
"value": "{{CONTENT}}"
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
"action": "wait",
|
|
48
|
-
"description": "İçeriğin yazılmasını bekle",
|
|
49
|
-
"waitMs": 1000
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
"action": "click",
|
|
53
|
-
"description": "Gönder butonuna tıkla",
|
|
54
|
-
"selector": "button.share-actions__primary-action",
|
|
55
|
-
"fallbackSelectors": [
|
|
56
|
-
"button[aria-label='Gönderi paylaş']",
|
|
57
|
-
"button.artdeco-button--primary[class*='share-actions']"
|
|
58
|
-
]
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
"action": "wait",
|
|
62
|
-
"description": "Gönderinin paylaşılmasını bekle",
|
|
63
|
-
"waitMs": 3000
|
|
64
|
-
}
|
|
65
|
-
]
|
|
66
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"platform": "linkedin",
|
|
3
|
-
"version": 1,
|
|
4
|
-
"action": "linkedin_send_connection_request",
|
|
5
|
-
"description": "LinkedIn mynetwork/grow sayfasından bir kişiye bağlantı isteği gönderme",
|
|
6
|
-
"lastUpdated": "2026-03-14T11:44:38.451Z",
|
|
7
|
-
"parameters": [
|
|
8
|
-
"{{USERNAME}}"
|
|
9
|
-
],
|
|
10
|
-
"steps": [
|
|
11
|
-
{
|
|
12
|
-
"action": "goto",
|
|
13
|
-
"description": "LinkedIn Ağım/Grow sayfasına git",
|
|
14
|
-
"url": "https://www.linkedin.com/mynetwork/grow/"
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
"action": "wait",
|
|
18
|
-
"description": "Sayfa ve öneri kartlarının yüklenmesini bekle",
|
|
19
|
-
"waitMs": 3000
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"action": "click",
|
|
23
|
-
"description": "{{USERNAME}} kişisinin bağlantı kurma (Connect) butonuna tıkla",
|
|
24
|
-
"selector": "button[aria-label*='{{USERNAME}}'][aria-label*='bağlantı' i]",
|
|
25
|
-
"fallbackSelectors": [
|
|
26
|
-
"button[aria-label*='{{USERNAME}}'][aria-label*='Connect' i]",
|
|
27
|
-
"button[aria-label*='{{USERNAME}}'][aria-label*='Invite' i]",
|
|
28
|
-
"//button[contains(@aria-label,'{{USERNAME}}') and (contains(@aria-label,'bağlantı') or contains(@aria-label,'Connect'))]"
|
|
29
|
-
]
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
"action": "wait",
|
|
33
|
-
"description": "Bağlantı isteği gönderilmesini veya modal açılmasını bekle",
|
|
34
|
-
"waitMs": 2000
|
|
35
|
-
}
|
|
36
|
-
]
|
|
37
|
-
}
|
package/maps/linkedin_post.json
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"platform": "linkedin",
|
|
3
|
-
"action": "post",
|
|
4
|
-
"description": "LinkedIn üzerinden yeni bir metin postu paylaşma",
|
|
5
|
-
"version": 1,
|
|
6
|
-
"lastUpdated": "2026-03-14T12:00:00.000Z",
|
|
7
|
-
"parameters": ["{{CONTENT}}"],
|
|
8
|
-
"steps": [
|
|
9
|
-
{
|
|
10
|
-
"action": "click",
|
|
11
|
-
"description": "Gönderi oluştur kutusuna tıkla",
|
|
12
|
-
"selector": "button.share-box-feed-entry__trigger",
|
|
13
|
-
"fallbackSelectors": [
|
|
14
|
-
"[aria-label*='Start a post']",
|
|
15
|
-
"[aria-label*='Gönderi başlat']",
|
|
16
|
-
"button[aria-label*='post']"
|
|
17
|
-
]
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"action": "wait",
|
|
21
|
-
"description": "Post oluşturma modalının açılmasını bekle",
|
|
22
|
-
"waitMs": 1500
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
"action": "click",
|
|
26
|
-
"description": "Metin alanına tıkla",
|
|
27
|
-
"selector": "[role='textbox'][aria-label*='Text editor']",
|
|
28
|
-
"fallbackSelectors": [
|
|
29
|
-
"[role='textbox'][contenteditable='true']",
|
|
30
|
-
".ql-editor[contenteditable='true']",
|
|
31
|
-
"[aria-label*='Metin düzenleyici']"
|
|
32
|
-
]
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
"action": "type",
|
|
36
|
-
"description": "Post metnini yaz",
|
|
37
|
-
"selector": "[role='textbox'][aria-label*='Text editor']",
|
|
38
|
-
"value": "{{CONTENT}}",
|
|
39
|
-
"fallbackSelectors": [
|
|
40
|
-
"[role='textbox'][contenteditable='true']",
|
|
41
|
-
".ql-editor[contenteditable='true']"
|
|
42
|
-
]
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"action": "wait",
|
|
46
|
-
"description": "Metin girişi sonrası bekleme",
|
|
47
|
-
"waitMs": 1000
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
"action": "click",
|
|
51
|
-
"description": "Gönder butonuna tıkla",
|
|
52
|
-
"selector": "button.share-actions__primary-action",
|
|
53
|
-
"fallbackSelectors": [
|
|
54
|
-
"[aria-label='Post']",
|
|
55
|
-
"[aria-label='Gönder']",
|
|
56
|
-
"button[aria-label*='Post']",
|
|
57
|
-
"button[aria-label*='Paylaş']"
|
|
58
|
-
]
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
"action": "wait",
|
|
62
|
-
"description": "Post gönderimini bekle",
|
|
63
|
-
"waitMs": 2000
|
|
64
|
-
}
|
|
65
|
-
]
|
|
66
|
-
}
|
package/maps/x_post.json
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"platform": "x",
|
|
3
|
-
"action": "post",
|
|
4
|
-
"description": "X (Twitter) üzerinden yeni bir metin postu paylaşma",
|
|
5
|
-
"version": 1,
|
|
6
|
-
"lastUpdated": "2026-03-14T12:00:00.000Z",
|
|
7
|
-
"parameters": ["{{CONTENT}}"],
|
|
8
|
-
"steps": [
|
|
9
|
-
{
|
|
10
|
-
"action": "click",
|
|
11
|
-
"description": "Sidebar'daki post butonuna tıkla",
|
|
12
|
-
"selector": "[data-testid='SideNav_NewTweet_Button']",
|
|
13
|
-
"fallbackSelectors": [
|
|
14
|
-
"a[href='/compose/post']",
|
|
15
|
-
"[aria-label*='Post'][role='link']",
|
|
16
|
-
"[aria-label*='Gönder'][role='link']",
|
|
17
|
-
"[data-testid='FloatingActionButton']"
|
|
18
|
-
]
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"action": "wait",
|
|
22
|
-
"description": "Compose dialog açılmasını bekle",
|
|
23
|
-
"waitMs": 1500
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
"action": "click",
|
|
27
|
-
"description": "Metin alanına tıkla",
|
|
28
|
-
"selector": "[data-testid='tweetTextarea_0']",
|
|
29
|
-
"fallbackSelectors": [
|
|
30
|
-
"[role='textbox'][contenteditable='true']",
|
|
31
|
-
".public-DraftEditor-content"
|
|
32
|
-
]
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
"action": "type",
|
|
36
|
-
"description": "Post metnini yaz",
|
|
37
|
-
"selector": "[data-testid='tweetTextarea_0']",
|
|
38
|
-
"value": "{{CONTENT}}",
|
|
39
|
-
"fallbackSelectors": [
|
|
40
|
-
"[role='textbox'][contenteditable='true']",
|
|
41
|
-
".public-DraftEditor-content"
|
|
42
|
-
]
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"action": "wait",
|
|
46
|
-
"description": "Metin girişi sonrası bekleme",
|
|
47
|
-
"waitMs": 500
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
"action": "click",
|
|
51
|
-
"description": "Gönder butonuna tıkla",
|
|
52
|
-
"selector": "[data-testid='tweetButton']",
|
|
53
|
-
"fallbackSelectors": [
|
|
54
|
-
"[data-testid='tweetButtonInline']",
|
|
55
|
-
"[role='button'][data-testid*='tweet']"
|
|
56
|
-
]
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"action": "wait",
|
|
60
|
-
"description": "Post gönderimini bekle",
|
|
61
|
-
"waitMs": 2000
|
|
62
|
-
}
|
|
63
|
-
]
|
|
64
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"platform": "x",
|
|
3
|
-
"version": 1,
|
|
4
|
-
"lastUpdated": "2026-03-14T11:21:59.765Z",
|
|
5
|
-
"action": "tweet_beğenmeyi_öğren",
|
|
6
|
-
"description": "tweet beğenmeyi öğren",
|
|
7
|
-
"parameters": [
|
|
8
|
-
"{{TWEET_URL}}"
|
|
9
|
-
],
|
|
10
|
-
"steps": [
|
|
11
|
-
{
|
|
12
|
-
"action": "goto",
|
|
13
|
-
"description": "Beğenilecek tweet'in sayfasına git",
|
|
14
|
-
"url": "{{TWEET_URL}}",
|
|
15
|
-
"fallbackSelectors": []
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
"action": "wait",
|
|
19
|
-
"description": "Tweet içeriğinin yüklenmesini bekle",
|
|
20
|
-
"waitMs": 2000,
|
|
21
|
-
"fallbackSelectors": []
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"action": "click",
|
|
25
|
-
"description": "Tweet'in altındaki beğen (kalp) butonuna tıkla",
|
|
26
|
-
"selector": "[data-testid=\"like\"]",
|
|
27
|
-
"fallbackSelectors": [
|
|
28
|
-
"[aria-label*=\"Beğen\"]",
|
|
29
|
-
"button[role=\"button\"] svg[viewBox=\"0 0 24 24\"]"
|
|
30
|
-
]
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
"action": "wait",
|
|
34
|
-
"description": "Beğeni animasyonunun tamamlanmasını bekle",
|
|
35
|
-
"waitMs": 1000,
|
|
36
|
-
"fallbackSelectors": []
|
|
37
|
-
}
|
|
38
|
-
]
|
|
39
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"platform": "x",
|
|
3
|
-
"version": 1,
|
|
4
|
-
"lastUpdated": "2026-03-14T12:01:17.966Z",
|
|
5
|
-
"action": "tweet_retweet_öğren",
|
|
6
|
-
"description": "Belirtilen tweet sayfasına git, retweet butonuna tıkla ve onay ver",
|
|
7
|
-
"parameters": [
|
|
8
|
-
"{{TWEET_URL}}"
|
|
9
|
-
],
|
|
10
|
-
"steps": [
|
|
11
|
-
{
|
|
12
|
-
"action": "goto",
|
|
13
|
-
"description": "Belirtilen tweet sayfasına git",
|
|
14
|
-
"url": "{{TWEET_URL}}"
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
"action": "wait",
|
|
18
|
-
"description": "Tweet sayfasının yüklenmesini bekle",
|
|
19
|
-
"waitMs": 2000
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"action": "click",
|
|
23
|
-
"description": "Retweet butonuna tıkla",
|
|
24
|
-
"selector": "[data-testid='retweet']",
|
|
25
|
-
"fallbackSelectors": [
|
|
26
|
-
"button[aria-label*='Yeniden gönder']",
|
|
27
|
-
"button[aria-label*='Repost']",
|
|
28
|
-
"[data-testid='unretweet']"
|
|
29
|
-
]
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
"action": "wait",
|
|
33
|
-
"description": "Retweet menüsünün açılmasını bekle",
|
|
34
|
-
"waitMs": 1000
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
"action": "click",
|
|
38
|
-
"description": "Yeniden gönder seçeneğine tıkla",
|
|
39
|
-
"selector": "[data-testid='retweetConfirm']",
|
|
40
|
-
"fallbackSelectors": [
|
|
41
|
-
"[role='menuitem'][data-testid='retweetConfirm']",
|
|
42
|
-
"[role='menu'] [role='menuitem']:first-child"
|
|
43
|
-
]
|
|
44
|
-
}
|
|
45
|
-
]
|
|
46
|
-
}
|