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.
- package/bin/cli.js +113 -6
- package/bin/server.js +1474 -255
- package/browser-agent-configs/all-agents-mcp.json +49 -0
- package/browser-agent-configs/twitter-harness-mcp.json +18 -0
- package/mcp-servers/browser-harness/server.py +600 -0
- package/package.json +3 -2
- package/schema-postgres.sql +16 -0
- package/scripts/dm_conversation.py +1 -1
- package/scripts/dm_send_log.py +7 -0
- package/scripts/dm_short_links.py +56 -9
- package/scripts/engage_reddit.py +21 -8
- package/scripts/engagement_styles.py +344 -146
- package/scripts/fetch_prospect_profile.py +1 -1
- package/scripts/ig_post_type_picker.py +123 -24
- package/scripts/insert_post029.py +4 -1
- package/scripts/insert_post_051.py +85 -0
- package/scripts/log_post.py +43 -3
- package/scripts/post_reddit.py +83 -17
- package/scripts/precompute_dashboard_stats.py +10 -5
- package/scripts/project_stats_json.py +298 -11
- package/scripts/reddit_browser.py +88 -3
- package/scripts/reddit_tools.py +30 -14
- package/scripts/regenerate_ig_plists.py +98 -41
- package/scripts/reply_db.py +6 -0
- package/scripts/run_moltbook_cycle.py +4 -2
- package/scripts/scan_dm_candidates.py +19 -1
- package/scripts/score_twitter_candidates.py +8 -0
- package/scripts/top_performers.py +56 -2
- package/scripts/twitter_account.py +76 -0
- package/scripts/twitter_browser.py +57 -65
- package/scripts/twitter_cycle_helper.py +24 -8
- package/scripts/twitter_gen_links.py +23 -3
- package/scripts/twitter_post_plan.py +14 -2
- package/scripts/update_stats.py +162 -80
- package/scripts/version.py +72 -0
- package/scripts/watchdog_hung_runs.py +6 -1
- package/skill/dm-outreach-twitter.sh +10 -16
- package/skill/engage-dm-replies.sh +29 -9
- package/skill/engage-twitter.sh +64 -157
- package/skill/engage.sh +1 -1
- package/skill/lib/twitter-backend.sh +96 -83
- package/skill/octolens.sh +2 -2
- package/skill/prewarm-funnel.sh +104 -0
- package/skill/run-instagram-daily.sh +11 -3
- package/skill/run-twitter-cycle.sh +81 -130
- package/skill/run-twitter-threads.sh +7 -6
- package/skill/scan-twitter-followups.sh +2 -2
- package/skill/styles.sh +74 -3
- package/browser-agent-configs/twitter-agent-mcp.json +0 -16
- 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 = ['
|
|
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
|
|
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).
|