social-agent-cli 3.0.1 → 3.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "social-agent-cli",
3
- "version": "3.0.1",
3
+ "version": "3.1.0",
4
4
  "description": "AI-powered social media agent - free APIs + browser automation with self-healing selectors",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,75 +1,102 @@
1
- import { execSync } from "node:child_process";
1
+ import { execSync, spawn } from "node:child_process";
2
2
  import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import { existsSync, mkdirSync, copyFileSync } from "node:fs";
3
5
  import { PATHS } from "../../core/config.js";
4
6
  import { loadMap, generateMap } from "../../ai/mapper.js";
5
7
  import { pickChromeProfile, getSavedProfile } from "../../core/profiles.js";
6
8
  import type { SelectorMap } from "../../core/types.js";
7
9
 
10
+ const CHROME_PATH = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
11
+ const CDP_PORT = 9222;
12
+
8
13
  /**
9
- * Browser Driver - agent-browser (Rust) ile browser otomasyonu
10
- * Playwright yerine native CDP, çok daha hızlı ve güvenilir
14
+ * Browser Driver - agent-browser + gerçek Chrome profili (CDP)
11
15
  */
12
16
  export class BrowserDriver {
13
- private profilePath: string = "";
17
+ private chromeStarted = false;
18
+ private profileDirName = "";
14
19
 
15
20
  constructor(private platform: string, private homeUrl: string) {}
16
21
 
22
+ /**
23
+ * agent-browser komutu çalıştır (CDP ile)
24
+ */
17
25
  private cmd(command: string, timeout = 15000): string {
18
- const profileFlag = this.profilePath ? `--profile "${this.profilePath}" --headed` : "--auto-connect";
19
26
  try {
20
- return execSync(`agent-browser ${profileFlag} ${command}`, {
27
+ return execSync(`agent-browser --cdp ${CDP_PORT} ${command}`, {
21
28
  encoding: "utf-8",
22
29
  timeout,
23
30
  stdio: ["pipe", "pipe", "pipe"],
24
31
  }).trim();
25
32
  } catch (err: any) {
26
- const stderr = err.stderr?.toString() || "";
27
- const stdout = err.stdout?.toString() || "";
28
- throw new Error(stderr || stdout || err.message);
33
+ throw new Error(err.stderr?.toString()?.trim() || err.stdout?.toString()?.trim() || err.message);
29
34
  }
30
35
  }
31
36
 
32
37
  /**
33
- * Profil seç ve hazırla
38
+ * Chrome'u gerçek profil ile CDP modunda başlat
34
39
  */
35
- private async setupProfile(askProfile = false): Promise<void> {
36
- const profile = askProfile
37
- ? await pickChromeProfile(this.platform)
38
- : getSavedProfile(this.platform);
39
-
40
- if (!profile) {
41
- throw new Error(`[${this.platform}] profil seçilmemiş. Önce: social-agent login ${this.platform}`);
42
- }
40
+ async launch(): Promise<void> {
41
+ const profile = getSavedProfile(this.platform);
42
+ if (!profile) throw new Error(`Profil yok. Önce: social-agent login ${this.platform}`);
43
+ this.profileDirName = profile.dirName;
43
44
 
44
- // agent-browser profil dizini
45
- this.profilePath = join(PATHS.profiles, `ab-${this.platform}`);
45
+ // CDP'ye bağlanmayı dene
46
+ try {
47
+ execSync(`agent-browser --cdp ${CDP_PORT} url`, { stdio: "pipe", timeout: 2000 });
48
+ return; // zaten çalışıyor
49
+ } catch {}
46
50
 
47
- // İlk kullanımda Chrome cookie'lerini kopyala
48
- const { existsSync, mkdirSync, copyFileSync } = await import("node:fs");
49
- mkdirSync(this.profilePath, { recursive: true });
51
+ // Chrome'u CDP ile başlat
52
+ const workDir = join(PATHS.profiles, "chrome-cdp");
53
+ mkdirSync(join(workDir, profile.dirName), { recursive: true });
50
54
 
51
- const cookieFiles = ["Cookies", "Cookies-journal", "Login Data", "Login Data-journal", "Preferences"];
52
- for (const f of cookieFiles) {
55
+ // Cookie'leri kopyala
56
+ for (const f of ["Cookies", "Cookies-journal", "Login Data", "Login Data-journal", "Preferences", "Secure Preferences"]) {
53
57
  const src = join(profile.fullPath, f);
54
- if (existsSync(src)) {
55
- try { copyFileSync(src, join(this.profilePath, f)); } catch {}
56
- }
58
+ if (existsSync(src)) { try { copyFileSync(src, join(workDir, profile.dirName, f)); } catch {} }
57
59
  }
60
+ const ls = join(homedir(), "Library", "Application Support", "Google", "Chrome", "Local State");
61
+ if (existsSync(ls)) { try { copyFileSync(ls, join(workDir, "Local State")); } catch {} }
62
+
63
+ // agent-browser daemon'u kapat
64
+ execSync("agent-browser close", { stdio: "pipe" }).toString();
65
+
66
+ const child = spawn(CHROME_PATH, [
67
+ `--remote-debugging-port=${CDP_PORT}`,
68
+ `--user-data-dir=${workDir}`,
69
+ `--profile-directory=${profile.dirName}`,
70
+ "--no-first-run", "--no-default-browser-check",
71
+ ], { detached: true, stdio: "ignore" });
72
+ child.unref();
73
+ this.chromeStarted = true;
74
+
75
+ // Bağlantıyı bekle
76
+ for (let i = 0; i < 20; i++) {
77
+ await new Promise(r => setTimeout(r, 500));
78
+ try {
79
+ execSync(`agent-browser --cdp ${CDP_PORT} url`, { stdio: "pipe", timeout: 2000 });
80
+ return;
81
+ } catch {}
82
+ }
83
+ throw new Error("Chrome başlatılamadı");
58
84
  }
59
85
 
86
+ async close(): Promise<void> {}
87
+
60
88
  /**
61
- * Login - profil seç, tarayıcı aç
89
+ * Login
62
90
  */
63
91
  async login(): Promise<void> {
64
- await this.setupProfile(true);
65
- // Login için --profile ile --headed aç
66
- const profileFlag = `--profile "${this.profilePath}" --headed`;
67
- execSync(`agent-browser ${profileFlag} open "${this.homeUrl}"`, { stdio: "pipe", timeout: 30000 });
92
+ await pickChromeProfile(this.platform);
93
+ await this.launch();
94
+ this.cmd(`open "${this.homeUrl}"`, 30000);
68
95
 
69
96
  console.log(`\n[${this.platform}] Tarayıcı açıldı: ${this.homeUrl}`);
70
97
  const { createInterface } = await import("node:readline");
71
98
  const rl = createInterface({ input: process.stdin, output: process.stdout });
72
- await new Promise<void>((resolve) => {
99
+ await new Promise<void>(resolve => {
73
100
  rl.question("Hesabın açıksa Enter'a bas: ", () => { rl.close(); resolve(); });
74
101
  });
75
102
  console.log(`[${this.platform}] Kayıt tamamlandı.`);
@@ -77,46 +104,36 @@ export class BrowserDriver {
77
104
  }
78
105
 
79
106
  /**
80
- * Sayfayı öğren - AI ile selector map oluştur
107
+ * Learn - ARIA snapshot ile map oluştur
81
108
  */
82
109
  async learn(taskDescription = "yeni bir metin postu at", actionName = "post"): Promise<SelectorMap> {
83
- await this.setupProfile();
84
-
85
- // Sayfaya git
110
+ await this.launch();
86
111
  this.cmd(`open "${this.homeUrl}"`, 30000);
87
112
  this.cmd("wait 3000");
88
113
 
89
- // Screenshot al
90
114
  const screenshotPath = join(PATHS.screenshots, `${this.platform}_learn_${actionName}.png`);
91
115
  this.cmd(`screenshot "${screenshotPath}"`);
92
116
 
93
- // ARIA snapshot al (agent-browser'ın en güçlü özelliği)
94
- const snapshot = this.cmd("snapshot", 10000);
117
+ // Compact interactive snapshot
118
+ const snapshot = this.cmd("snapshot -i -c", 10000);
95
119
 
96
120
  console.log(`[${this.platform}] Screenshot: ${screenshotPath}`);
97
121
  console.log(`[${this.platform}] Snapshot: ${snapshot.length} bytes`);
98
122
  console.log(`[${this.platform}] Claude analiz ediyor...`);
99
123
 
100
124
  const map = await generateMap(this.platform, screenshotPath, snapshot, taskDescription, actionName);
101
-
102
125
  console.log(`[${this.platform}] "${actionName}" map oluşturuldu (${map.steps.length} adım)`);
103
126
  process.exit(0);
104
127
  return map;
105
128
  }
106
129
 
107
- /**
108
- * Sayfaya git
109
- */
110
130
  navigate(url: string): void {
111
131
  this.cmd(`open "${url}"`, 30000);
112
132
  this.cmd("wait 2000");
113
133
  }
114
134
 
115
- /**
116
- * Tek bir step çalıştır
117
- */
118
135
  executeStep(step: any, content: string, imagePath?: string): void {
119
- const value = (step.value || "").replace("{{CONTENT}}", content).replace("{{IMAGE}}", imagePath || "");
136
+ const val = (step.value || "").replace("{{CONTENT}}", content).replace("{{IMAGE}}", imagePath || "");
120
137
 
121
138
  switch (step.action) {
122
139
  case "goto":
@@ -129,37 +146,25 @@ export class BrowserDriver {
129
146
 
130
147
  case "click": {
131
148
  const sels = [step.selector, ...(step.fallbackSelectors || [])].filter(Boolean);
132
- let clicked = false;
133
149
  for (const sel of sels) {
134
150
  if (sel.includes("{{")) continue;
135
- try { this.cmd(`click "${sel}"`); clicked = true; break; } catch {}
151
+ try { this.cmd(`click "${sel}"`); if (step.waitMs) this.cmd(`wait ${step.waitMs}`); return; } catch {}
136
152
  }
137
- if (!clicked) throw new Error(`Click: ${sels[0]}`);
138
- if (step.waitMs) this.cmd(`wait ${step.waitMs}`);
139
- break;
153
+ throw new Error(`Click: ${sels[0]}`);
140
154
  }
141
155
 
142
156
  case "type": {
143
157
  const sels = [step.selector, ...(step.fallbackSelectors || [])].filter(Boolean);
144
- let typed = false;
145
158
  for (const sel of sels) {
146
159
  if (sel.includes("{{")) continue;
147
160
  try {
148
- this.cmd(`fill "${sel}" "${value.replace(/"/g, '\\"')}"`);
149
- typed = true;
150
- break;
151
- } catch {
152
- try {
153
- this.cmd(`click "${sel}"`);
154
- this.cmd(`keyboard inserttext "${value.replace(/"/g, '\\"')}"`);
155
- typed = true;
156
- break;
157
- } catch {}
158
- }
161
+ this.cmd(`click "${sel}"`);
162
+ this.cmd(`keyboard inserttext "${val.replace(/"/g, '\\"')}"`);
163
+ if (step.waitMs) this.cmd(`wait ${step.waitMs}`);
164
+ return;
165
+ } catch {}
159
166
  }
160
- if (!typed) throw new Error(`Type: ${sels[0]}`);
161
- if (step.waitMs) this.cmd(`wait ${step.waitMs}`);
162
- break;
167
+ throw new Error(`Type: ${sels[0]}`);
163
168
  }
164
169
 
165
170
  case "keypress":
@@ -168,45 +173,22 @@ export class BrowserDriver {
168
173
  break;
169
174
 
170
175
  case "upload": {
171
- const filePath = value || imagePath || "";
176
+ const filePath = val || imagePath || "";
172
177
  if (!filePath || filePath.includes("{{")) break;
173
-
174
178
  const sels = [step.selector, ...(step.fallbackSelectors || [])].filter(Boolean);
175
- let uploaded = false;
176
179
  for (const sel of sels) {
177
- try {
178
- this.cmd(`upload "${sel}" "${filePath}"`);
179
- uploaded = true;
180
- break;
181
- } catch {}
180
+ try { this.cmd(`upload "${sel}" "${filePath}"`); if (step.waitMs) this.cmd(`wait ${step.waitMs}`); return; } catch {}
182
181
  }
183
- if (!uploaded) throw new Error(`Upload: ${filePath}`);
184
- if (step.waitMs) this.cmd(`wait ${step.waitMs}`);
185
- break;
182
+ throw new Error(`Upload: ${filePath}`);
186
183
  }
187
184
  }
188
185
  }
189
186
 
190
- /**
191
- * Snapshot al (heal için)
192
- */
193
187
  getSnapshot(): string {
194
- try { return this.cmd("snapshot", 10000); } catch { return ""; }
188
+ try { return this.cmd("snapshot -i -c", 10000); } catch { return ""; }
195
189
  }
196
190
 
197
- /**
198
- * Screenshot al
199
- */
200
191
  takeScreenshot(path: string): void {
201
192
  try { this.cmd(`screenshot "${path}"`); } catch {}
202
193
  }
203
-
204
- async close(): Promise<void> {
205
- // agent-browser daemon otomatik yönetir
206
- }
207
-
208
- async launch(): Promise<any> {
209
- await this.setupProfile();
210
- return null; // agent-browser'da page objesi yok, cmd ile çalışıyor
211
- }
212
194
  }