social-autoposter 1.4.0 → 1.5.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.
Files changed (50) hide show
  1. package/bin/cli.js +113 -6
  2. package/bin/server.js +1474 -255
  3. package/browser-agent-configs/all-agents-mcp.json +49 -0
  4. package/browser-agent-configs/twitter-harness-mcp.json +18 -0
  5. package/mcp-servers/browser-harness/server.py +600 -0
  6. package/package.json +3 -2
  7. package/schema-postgres.sql +16 -0
  8. package/scripts/dm_conversation.py +1 -1
  9. package/scripts/dm_send_log.py +7 -0
  10. package/scripts/dm_short_links.py +56 -9
  11. package/scripts/engage_reddit.py +21 -8
  12. package/scripts/engagement_styles.py +344 -146
  13. package/scripts/fetch_prospect_profile.py +1 -1
  14. package/scripts/ig_post_type_picker.py +123 -24
  15. package/scripts/insert_post029.py +4 -1
  16. package/scripts/insert_post_051.py +85 -0
  17. package/scripts/log_post.py +43 -3
  18. package/scripts/post_reddit.py +83 -17
  19. package/scripts/precompute_dashboard_stats.py +10 -5
  20. package/scripts/project_stats_json.py +298 -11
  21. package/scripts/reddit_browser.py +88 -3
  22. package/scripts/reddit_tools.py +30 -14
  23. package/scripts/regenerate_ig_plists.py +98 -41
  24. package/scripts/reply_db.py +6 -0
  25. package/scripts/run_moltbook_cycle.py +4 -2
  26. package/scripts/scan_dm_candidates.py +19 -1
  27. package/scripts/score_twitter_candidates.py +8 -0
  28. package/scripts/top_performers.py +56 -2
  29. package/scripts/twitter_account.py +76 -0
  30. package/scripts/twitter_browser.py +57 -65
  31. package/scripts/twitter_cycle_helper.py +24 -8
  32. package/scripts/twitter_gen_links.py +23 -3
  33. package/scripts/twitter_post_plan.py +14 -2
  34. package/scripts/update_stats.py +162 -80
  35. package/scripts/version.py +72 -0
  36. package/scripts/watchdog_hung_runs.py +6 -1
  37. package/skill/dm-outreach-twitter.sh +10 -16
  38. package/skill/engage-dm-replies.sh +29 -9
  39. package/skill/engage-twitter.sh +64 -157
  40. package/skill/engage.sh +1 -1
  41. package/skill/lib/twitter-backend.sh +96 -83
  42. package/skill/octolens.sh +2 -2
  43. package/skill/prewarm-funnel.sh +104 -0
  44. package/skill/run-instagram-daily.sh +11 -3
  45. package/skill/run-twitter-cycle.sh +81 -130
  46. package/skill/run-twitter-threads.sh +7 -6
  47. package/skill/scan-twitter-followups.sh +2 -2
  48. package/skill/styles.sh +74 -3
  49. package/browser-agent-configs/twitter-agent-mcp.json +0 -16
  50. package/browser-agent-configs/twitter-agent.json +0 -17
package/bin/cli.js CHANGED
@@ -23,6 +23,7 @@ const COPY_TARGETS = [
23
23
  'skill',
24
24
  'setup',
25
25
  'browser-agent-configs',
26
+ 'mcp-servers',
26
27
  ];
27
28
 
28
29
  const ENV_TEMPLATE = `# social-autoposter environment variables
@@ -42,16 +43,20 @@ DATABASE_URL=
42
43
  const USER_FILES = new Set(['config.json', '.env', 'SKILL.md']);
43
44
 
44
45
  // Browser agent config templates -> install path under ~/.claude/browser-agent-configs/
46
+ // twitter-harness replaces the retired twitter-agent (2026-05-19). The harness
47
+ // runs a CDP-driven real Chrome on port 9555 backed by an MCP stdio server at
48
+ // ~/.claude/mcp-servers/browser-harness/server.py. installBrowserHarness()
49
+ // below provisions the supporting bits (uv, browser-harness CLI, mcp pkg).
45
50
  const BROWSER_AGENT_CONFIGS = [
46
- 'twitter-agent-mcp.json',
47
- 'twitter-agent.json',
48
51
  'reddit-agent-mcp.json',
49
52
  'reddit-agent.json',
50
53
  'linkedin-agent-mcp.json',
51
54
  'linkedin-agent.json',
55
+ 'twitter-harness-mcp.json',
56
+ 'all-agents-mcp.json',
52
57
  ];
53
58
 
54
- const BROWSER_PROFILES = ['twitter', 'reddit', 'linkedin'];
59
+ const BROWSER_PROFILES = ['reddit', 'linkedin', 'browser-harness'];
55
60
 
56
61
  function copyDir(src, dest) {
57
62
  fs.mkdirSync(dest, { recursive: true });
@@ -71,8 +76,27 @@ function linkOrRelink(target, linkPath) {
71
76
  fs.symlinkSync(target, linkPath);
72
77
  }
73
78
 
79
+ // Locate uv (Astral's Python launcher). The browser-harness MCP server is
80
+ // shebanged through uv so it can pull `mcp` on first run without polluting
81
+ // the system Python. Returns the absolute path if found, or empty string.
82
+ function findUvBin() {
83
+ const candidates = [
84
+ path.join(HOME, '.local', 'bin', 'uv'),
85
+ '/opt/homebrew/bin/uv',
86
+ '/usr/local/bin/uv',
87
+ '/usr/bin/uv',
88
+ ];
89
+ for (const c of candidates) {
90
+ if (fs.existsSync(c)) return c;
91
+ }
92
+ const which = spawnSync('command', ['-v', 'uv'], { shell: true, encoding: 'utf8' });
93
+ const found = (which.stdout || '').trim().split('\n')[0];
94
+ return found && fs.existsSync(found) ? found : '';
95
+ }
96
+
74
97
  function installBrowserAgentConfigs() {
75
98
  const nodeBin = path.dirname(process.execPath);
99
+ const uvBin = findUvBin() || path.join(HOME, '.local', 'bin', 'uv');
76
100
  const srcDir = path.join(PKG_ROOT, 'browser-agent-configs');
77
101
  const destDir = path.join(HOME, '.claude', 'browser-agent-configs');
78
102
  fs.mkdirSync(destDir, { recursive: true });
@@ -90,7 +114,8 @@ function installBrowserAgentConfigs() {
90
114
  const tpl = fs.readFileSync(src, 'utf8');
91
115
  const out = tpl
92
116
  .replace(/__HOME__/g, HOME)
93
- .replace(/__NODE_BIN__/g, nodeBin);
117
+ .replace(/__NODE_BIN__/g, nodeBin)
118
+ .replace(/__UV_BIN__/g, uvBin);
94
119
  fs.writeFileSync(dest, out);
95
120
  installed++;
96
121
  }
@@ -105,16 +130,92 @@ function installBrowserAgentConfigs() {
105
130
  console.log(` browser profile dirs ready -> ${profilesDir}/{${BROWSER_PROFILES.join(',')}}`);
106
131
  }
107
132
 
133
+ // Provision the browser-harness toolchain that backs the twitter-harness MCP:
134
+ // 1. install uv (Astral) if missing
135
+ // 2. git-clone browser-use/browser-harness
136
+ // 3. uv tool install -e . (provides the `browser-harness` CLI)
137
+ // 4. ensure `mcp` Python package is importable for server.py
138
+ // 5. copy our shipped server.py into ~/.claude/mcp-servers/browser-harness/
139
+ // All steps are idempotent.
140
+ function installBrowserHarness() {
141
+ console.log(' setting up browser-harness (twitter-harness MCP backend)...');
142
+
143
+ // Step 1: uv. Try the official installer first; fall back to pip.
144
+ let uvBin = findUvBin();
145
+ if (!uvBin) {
146
+ console.log(' uv not found -> installing via Astral installer');
147
+ const sh = spawnSync('bash', ['-lc', 'curl -LsSf https://astral.sh/uv/install.sh | sh'], { stdio: 'inherit' });
148
+ if (sh.status !== 0) {
149
+ console.log(' Astral installer failed; falling back to pip3 install uv');
150
+ let pip = spawnSync('pip3', ['install', '-q', 'uv'], { stdio: 'inherit' });
151
+ if (pip.status !== 0) {
152
+ pip = spawnSync('pip3', ['install', '-q', 'uv', '--break-system-packages'], { stdio: 'inherit' });
153
+ }
154
+ }
155
+ uvBin = findUvBin();
156
+ }
157
+ if (!uvBin) {
158
+ console.warn(' WARNING: uv install failed; twitter-harness MCP server.py will not start.');
159
+ console.warn(' Install manually: curl -LsSf https://astral.sh/uv/install.sh | sh');
160
+ } else {
161
+ console.log(` uv -> ${uvBin}`);
162
+ }
163
+
164
+ // Step 2 + 3: clone + `uv tool install -e .` browser-harness.
165
+ const harnessDir = path.join(HOME, 'Developer', 'browser-harness');
166
+ if (!fs.existsSync(harnessDir)) {
167
+ fs.mkdirSync(path.dirname(harnessDir), { recursive: true });
168
+ console.log(' cloning browser-harness from GitHub...');
169
+ const clone = spawnSync('git', ['clone', '--depth', '1', 'https://github.com/browser-use/browser-harness', harnessDir], { stdio: 'inherit' });
170
+ if (clone.status !== 0) {
171
+ console.warn(' WARNING: git clone failed; twitter-harness will not work until you clone manually.');
172
+ }
173
+ } else {
174
+ console.log(` browser-harness clone exists -> ${harnessDir}`);
175
+ }
176
+
177
+ if (uvBin && fs.existsSync(harnessDir)) {
178
+ console.log(' installing browser-harness CLI via uv tool...');
179
+ const install = spawnSync(uvBin, ['tool', 'install', '-e', harnessDir], { stdio: 'inherit' });
180
+ if (install.status !== 0) {
181
+ console.warn(' WARNING: `uv tool install -e .` failed; check the output above.');
182
+ }
183
+ }
184
+
185
+ // Step 4: ensure mcp Python package available (server.py uses `from mcp.server.fastmcp ...`).
186
+ // server.py is shebanged through `uv run --with mcp ...` so this is belt-and-suspenders;
187
+ // we install it into the system Python too so a plain `python3 server.py` also works.
188
+ console.log(' ensuring mcp>=1.0.0 Python package is importable...');
189
+ let pip = spawnSync('pip3', ['install', '-q', 'mcp>=1.0.0'], { stdio: 'inherit' });
190
+ if (pip.status !== 0) {
191
+ pip = spawnSync('pip3', ['install', '-q', 'mcp>=1.0.0', '--break-system-packages'], { stdio: 'inherit' });
192
+ }
193
+
194
+ // Step 5: copy our shipped server.py into the canonical install location.
195
+ const srcServer = path.join(PKG_ROOT, 'mcp-servers', 'browser-harness', 'server.py');
196
+ const destServer = path.join(HOME, '.claude', 'mcp-servers', 'browser-harness', 'server.py');
197
+ if (fs.existsSync(srcServer)) {
198
+ fs.mkdirSync(path.dirname(destServer), { recursive: true });
199
+ fs.copyFileSync(srcServer, destServer);
200
+ try { fs.chmodSync(destServer, 0o755); } catch {}
201
+ console.log(` server.py -> ${destServer}`);
202
+ } else {
203
+ console.warn(` WARNING: package missing mcp-servers/browser-harness/server.py (${srcServer})`);
204
+ }
205
+ }
206
+
108
207
  // Register the three browser-agent MCP servers with Claude so they show up
109
208
  // under user scope (writes to ~/.claude.json). Idempotent: parses the output
110
209
  // of `claude mcp list` and only calls `add-json` for missing entries.
111
210
  // If the `claude` CLI is not on PATH, prints manual instructions and returns.
112
211
  function registerBrowserAgentMcpServers() {
113
212
  const configDir = path.join(HOME, '.claude', 'browser-agent-configs');
213
+ // twitter-agent retired 2026-05-19, replaced by twitter-harness (CDP-driven
214
+ // real Chrome on port 9555 via the browser-harness MCP server).
114
215
  const servers = [
115
- { name: 'twitter-agent', file: path.join(configDir, 'twitter-agent-mcp.json') },
116
216
  { name: 'reddit-agent', file: path.join(configDir, 'reddit-agent-mcp.json') },
117
217
  { name: 'linkedin-agent', file: path.join(configDir, 'linkedin-agent-mcp.json') },
218
+ { name: 'twitter-harness', file: path.join(configDir, 'twitter-harness-mcp.json') },
118
219
  ];
119
220
 
120
221
  const claudeBin = spawnSync('claude', ['--version'], { stdio: 'pipe' });
@@ -138,7 +239,7 @@ function registerBrowserAgentMcpServers() {
138
239
  continue;
139
240
  }
140
241
  // `claude mcp list` prints one server per line starting with the name.
141
- // Use a word-boundary check so twitter-agent does not false-match reddit-agent.
242
+ // Use a word-boundary check so e.g. reddit-agent does not false-match linkedin-agent.
142
243
  const re = new RegExp(`(^|\\s)${s.name.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}(:|\\s|$)`, 'm');
143
244
  if (re.test(existing)) {
144
245
  skipped++;
@@ -288,6 +389,9 @@ function init() {
288
389
  installSystemdUnits();
289
390
  }
290
391
 
392
+ // Provision the browser-harness toolchain BEFORE writing harness configs so
393
+ // findUvBin() picks up a freshly-installed uv on first run.
394
+ installBrowserHarness();
291
395
  // Install browser agent MCP configs + profile dirs (skips existing files)
292
396
  installBrowserAgentConfigs();
293
397
  // Register those MCP servers with Claude so they show up in `claude mcp list`.
@@ -369,6 +473,9 @@ function update() {
369
473
  installSystemdUnits();
370
474
  }
371
475
 
476
+ // Provision browser-harness (uv + clone + uv tool install + mcp pkg + server.py).
477
+ // Idempotent: skips steps that are already done.
478
+ installBrowserHarness();
372
479
  // Top up browser agent configs (won't overwrite user customizations)
373
480
  installBrowserAgentConfigs();
374
481
  // Register any newly added MCP servers with Claude (idempotent).