social-agent-cli 3.0.2 → 3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "social-agent-cli",
3
- "version": "3.0.2",
3
+ "version": "3.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": {
@@ -1,75 +1,103 @@
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(`curl -sf http://127.0.0.1:${CDP_PORT}/json/version`, { 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 < 30; i++) {
77
+ 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 {}
83
+ }
84
+ throw new Error("Chrome başlatılamadı - Chrome'u kapat ve tekrar dene");
58
85
  }
59
86
 
87
+ async close(): Promise<void> {}
88
+
60
89
  /**
61
- * Login - profil seç, tarayıcı aç
90
+ * Login
62
91
  */
63
92
  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: "inherit", timeout: 30000 });
93
+ await pickChromeProfile(this.platform);
94
+ await this.launch();
95
+ this.cmd(`open "${this.homeUrl}"`, 30000);
68
96
 
69
97
  console.log(`\n[${this.platform}] Tarayıcı açıldı: ${this.homeUrl}`);
70
98
  const { createInterface } = await import("node:readline");
71
99
  const rl = createInterface({ input: process.stdin, output: process.stdout });
72
- await new Promise<void>((resolve) => {
100
+ await new Promise<void>(resolve => {
73
101
  rl.question("Hesabın açıksa Enter'a bas: ", () => { rl.close(); resolve(); });
74
102
  });
75
103
  console.log(`[${this.platform}] Kayıt tamamlandı.`);
@@ -77,46 +105,36 @@ export class BrowserDriver {
77
105
  }
78
106
 
79
107
  /**
80
- * Sayfayı öğren - AI ile selector map oluştur
108
+ * Learn - ARIA snapshot ile map oluştur
81
109
  */
82
110
  async learn(taskDescription = "yeni bir metin postu at", actionName = "post"): Promise<SelectorMap> {
83
- await this.setupProfile();
84
-
85
- // Sayfaya git
111
+ await this.launch();
86
112
  this.cmd(`open "${this.homeUrl}"`, 30000);
87
113
  this.cmd("wait 3000");
88
114
 
89
- // Screenshot al
90
115
  const screenshotPath = join(PATHS.screenshots, `${this.platform}_learn_${actionName}.png`);
91
116
  this.cmd(`screenshot "${screenshotPath}"`);
92
117
 
93
- // ARIA snapshot al (agent-browser'ın en güçlü özelliği)
94
- const snapshot = this.cmd("snapshot", 10000);
118
+ // Compact interactive snapshot
119
+ const snapshot = this.cmd("snapshot -i -c", 10000);
95
120
 
96
121
  console.log(`[${this.platform}] Screenshot: ${screenshotPath}`);
97
122
  console.log(`[${this.platform}] Snapshot: ${snapshot.length} bytes`);
98
123
  console.log(`[${this.platform}] Claude analiz ediyor...`);
99
124
 
100
125
  const map = await generateMap(this.platform, screenshotPath, snapshot, taskDescription, actionName);
101
-
102
126
  console.log(`[${this.platform}] "${actionName}" map oluşturuldu (${map.steps.length} adım)`);
103
127
  process.exit(0);
104
128
  return map;
105
129
  }
106
130
 
107
- /**
108
- * Sayfaya git
109
- */
110
131
  navigate(url: string): void {
111
132
  this.cmd(`open "${url}"`, 30000);
112
133
  this.cmd("wait 2000");
113
134
  }
114
135
 
115
- /**
116
- * Tek bir step çalıştır
117
- */
118
136
  executeStep(step: any, content: string, imagePath?: string): void {
119
- const value = (step.value || "").replace("{{CONTENT}}", content).replace("{{IMAGE}}", imagePath || "");
137
+ const val = (step.value || "").replace("{{CONTENT}}", content).replace("{{IMAGE}}", imagePath || "");
120
138
 
121
139
  switch (step.action) {
122
140
  case "goto":
@@ -129,37 +147,25 @@ export class BrowserDriver {
129
147
 
130
148
  case "click": {
131
149
  const sels = [step.selector, ...(step.fallbackSelectors || [])].filter(Boolean);
132
- let clicked = false;
133
150
  for (const sel of sels) {
134
151
  if (sel.includes("{{")) continue;
135
- try { this.cmd(`click "${sel}"`); clicked = true; break; } catch {}
152
+ try { this.cmd(`click "${sel}"`); if (step.waitMs) this.cmd(`wait ${step.waitMs}`); return; } catch {}
136
153
  }
137
- if (!clicked) throw new Error(`Click: ${sels[0]}`);
138
- if (step.waitMs) this.cmd(`wait ${step.waitMs}`);
139
- break;
154
+ throw new Error(`Click: ${sels[0]}`);
140
155
  }
141
156
 
142
157
  case "type": {
143
158
  const sels = [step.selector, ...(step.fallbackSelectors || [])].filter(Boolean);
144
- let typed = false;
159
+ const escaped = val.replace(/"/g, '\\"');
145
160
  for (const sel of sels) {
146
161
  if (sel.includes("{{")) continue;
147
162
  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
- }
163
+ this.cmd(`type "${sel}" "${escaped}"`, 30000);
164
+ if (step.waitMs) this.cmd(`wait ${step.waitMs}`);
165
+ return;
166
+ } catch {}
159
167
  }
160
- if (!typed) throw new Error(`Type: ${sels[0]}`);
161
- if (step.waitMs) this.cmd(`wait ${step.waitMs}`);
162
- break;
168
+ throw new Error(`Type: ${sels[0]}`);
163
169
  }
164
170
 
165
171
  case "keypress":
@@ -168,45 +174,22 @@ export class BrowserDriver {
168
174
  break;
169
175
 
170
176
  case "upload": {
171
- const filePath = value || imagePath || "";
177
+ const filePath = val || imagePath || "";
172
178
  if (!filePath || filePath.includes("{{")) break;
173
-
174
179
  const sels = [step.selector, ...(step.fallbackSelectors || [])].filter(Boolean);
175
- let uploaded = false;
176
180
  for (const sel of sels) {
177
- try {
178
- this.cmd(`upload "${sel}" "${filePath}"`);
179
- uploaded = true;
180
- break;
181
- } catch {}
181
+ try { this.cmd(`upload "${sel}" "${filePath}"`); if (step.waitMs) this.cmd(`wait ${step.waitMs}`); return; } catch {}
182
182
  }
183
- if (!uploaded) throw new Error(`Upload: ${filePath}`);
184
- if (step.waitMs) this.cmd(`wait ${step.waitMs}`);
185
- break;
183
+ throw new Error(`Upload: ${filePath}`);
186
184
  }
187
185
  }
188
186
  }
189
187
 
190
- /**
191
- * Snapshot al (heal için)
192
- */
193
188
  getSnapshot(): string {
194
- try { return this.cmd("snapshot", 10000); } catch { return ""; }
189
+ try { return this.cmd("snapshot -i -c", 10000); } catch { return ""; }
195
190
  }
196
191
 
197
- /**
198
- * Screenshot al
199
- */
200
192
  takeScreenshot(path: string): void {
201
193
  try { this.cmd(`screenshot "${path}"`); } catch {}
202
194
  }
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
195
  }