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 +1 -1
- package/cli.ts +1 -1
- package/core/upload-helper.ts +18 -14
- package/package.json +1 -1
- package/platforms/browser/driver.ts +183 -132
package/ai/runner.ts
CHANGED
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[] {
|
package/core/upload-helper.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
//
|
|
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,58 +1,128 @@
|
|
|
1
|
-
import { execSync
|
|
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.
|
|
25
|
+
throw new Error(err.stderr?.toString()?.trim() || err.message);
|
|
34
26
|
}
|
|
35
27
|
}
|
|
36
28
|
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
`--
|
|
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ı
|
|
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
|
-
|
|
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
|
|
120
|
-
const { writeFileSync
|
|
121
|
-
|
|
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
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
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
|
|
172
|
-
|
|
173
|
-
// 1.
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
}
|
|
188
|
-
|
|
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.
|
|
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
|
|
208
|
-
// Snapshot'tan medya
|
|
209
|
-
let btnSel = "button[aria-label
|
|
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.
|
|
212
|
-
const
|
|
213
|
-
|
|
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
|
-
|
|
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 (
|
|
224
|
-
|
|
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
|
-
|
|
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
|
}
|