@m13v/s4l 1.6.197-rc.10
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/README.md +143 -0
- package/SKILL.md +342 -0
- package/bin/cli.js +980 -0
- package/bin/cookie-helper.js +315 -0
- package/bin/platform.js +59 -0
- package/bin/scheduler/index.js +12 -0
- package/bin/scheduler/launchd.js +518 -0
- package/browser-agent-configs/all-agents-mcp.json +68 -0
- package/browser-agent-configs/linkedin-agent-mcp.json +16 -0
- package/browser-agent-configs/linkedin-agent.json +17 -0
- package/browser-agent-configs/linkedin-harness-mcp.json +21 -0
- package/browser-agent-configs/reddit-agent-mcp.json +16 -0
- package/browser-agent-configs/reddit-agent.json +17 -0
- package/browser-agent-configs/twitter-harness-mcp.json +18 -0
- package/config.example.json +45 -0
- package/mcp/dist/index.js +4212 -0
- package/mcp/dist/onboarding.js +200 -0
- package/mcp/dist/panel.html +176 -0
- package/mcp/dist/product-link.html +102 -0
- package/mcp/dist/repo.js +222 -0
- package/mcp/dist/runtime.js +1079 -0
- package/mcp/dist/screencast.js +323 -0
- package/mcp/dist/setup.js +545 -0
- package/mcp/dist/telemetry.js +306 -0
- package/mcp/dist/twitterAuth.js +138 -0
- package/mcp/dist/version.js +271 -0
- package/mcp/dist/version.json +4 -0
- package/mcp/install-runtime.mjs +70 -0
- package/mcp/install.mjs +169 -0
- package/mcp/manifest.json +80 -0
- package/mcp/menubar/dashboard_server.py +213 -0
- package/mcp/menubar/s4l_card.py +1336 -0
- package/mcp/menubar/s4l_log_relay.py +179 -0
- package/mcp/menubar/s4l_menubar.py +2439 -0
- package/mcp/menubar/s4l_state.py +891 -0
- package/mcp/package.json +34 -0
- package/mcp/shared/doctor.cjs +437 -0
- package/mcp/shared/onboarding-ledger.cjs +324 -0
- package/mcp-servers/browser-harness/server.py +968 -0
- package/package.json +160 -0
- package/requirements.txt +20 -0
- package/scripts/_compute_allowlist.py +58 -0
- package/scripts/_db_update.py +20 -0
- package/scripts/_filt.py +9 -0
- package/scripts/_li_notif_match.py +76 -0
- package/scripts/_li_notif_orchestrate.py +126 -0
- package/scripts/_lock_preempt_test.py +60 -0
- package/scripts/_run_icp_precheck.py +57 -0
- package/scripts/a16z_pearx_calendar_reminders.py +99 -0
- package/scripts/account_resolver.py +141 -0
- package/scripts/active_campaigns.py +114 -0
- package/scripts/active_users.py +190 -0
- package/scripts/amplitude_24h_signups.py +468 -0
- package/scripts/amplitude_signups.py +177 -0
- package/scripts/apply_onboarding_selections.py +131 -0
- package/scripts/audience_pages.py +243 -0
- package/scripts/audit_helper.py +120 -0
- package/scripts/author_history_block.py +353 -0
- package/scripts/autopilot_stall_watch.py +284 -0
- package/scripts/backfill_twitter_attempts_topic.py +81 -0
- package/scripts/backfill_twitter_log_post_no_id.py +322 -0
- package/scripts/bench_dashboard.sh +138 -0
- package/scripts/bh_send.py +39 -0
- package/scripts/build_persona.py +409 -0
- package/scripts/bulk_icp.py +18 -0
- package/scripts/campaign_bump.py +51 -0
- package/scripts/capture_thread_media.py +288 -0
- package/scripts/check_browser_lock_health.sh +81 -0
- package/scripts/check_external_pool_depth.py +253 -0
- package/scripts/check_unread_web_chats.py +28 -0
- package/scripts/claim_web_chat.py +47 -0
- package/scripts/classify_run_error.py +158 -0
- package/scripts/claude_job.py +988 -0
- package/scripts/clean_stale_singleton.sh +56 -0
- package/scripts/cleanup_harness_tabs.py +68 -0
- package/scripts/copy_browser_cookies.py +454 -0
- package/scripts/counterparty_history.py +350 -0
- package/scripts/db.py +57 -0
- package/scripts/discover_claude_profiles.py +120 -0
- package/scripts/discover_linkedin_candidates.py +984 -0
- package/scripts/dm_conversation.py +682 -0
- package/scripts/dm_db_update.py +69 -0
- package/scripts/dm_engage_helper.py +161 -0
- package/scripts/dm_outreach_helper.py +147 -0
- package/scripts/dm_outreach_twitter_helper.py +129 -0
- package/scripts/dm_send_log.py +106 -0
- package/scripts/dm_short_links.py +1084 -0
- package/scripts/dump_web_chat_history.py +47 -0
- package/scripts/engage_github.py +640 -0
- package/scripts/engage_reddit.py +1235 -0
- package/scripts/engage_twitter_helper.py +301 -0
- package/scripts/engagement_styles.py +1787 -0
- package/scripts/enrich_twitter_candidates.py +82 -0
- package/scripts/feedback_digest.py +448 -0
- package/scripts/fetch_prospect_profile.py +312 -0
- package/scripts/fetch_twitter_t1.py +134 -0
- package/scripts/find_threads.py +530 -0
- package/scripts/follow_gate_log.py +59 -0
- package/scripts/funnel_per_day.py +194 -0
- package/scripts/generate_daily_human_style.py +494 -0
- package/scripts/generation_trace.py +173 -0
- package/scripts/get_run_cost.py +107 -0
- package/scripts/github_engage_helper.py +93 -0
- package/scripts/github_tools.py +509 -0
- package/scripts/harness_overlay.py +556 -0
- package/scripts/harvest_twitter_following.py +243 -0
- package/scripts/heartbeat.sh +70 -0
- package/scripts/history_context.py +284 -0
- package/scripts/http_api.py +206 -0
- package/scripts/human_dm_replies_helper.py +169 -0
- package/scripts/identity.py +302 -0
- package/scripts/ig_batch_creator.sh +93 -0
- package/scripts/ig_post_type_picker.py +243 -0
- package/scripts/ig_scrape_transcribe.sh +91 -0
- package/scripts/ingest_human_dm_replies.py +271 -0
- package/scripts/ingest_web_chat_replies.py +229 -0
- package/scripts/install_fleet.py +187 -0
- package/scripts/invent_mcp_server.py +350 -0
- package/scripts/invent_topics.py +1462 -0
- package/scripts/learned_preferences.py +263 -0
- package/scripts/li_discovery.py +161 -0
- package/scripts/link_edit_helper.py +142 -0
- package/scripts/link_tail.py +592 -0
- package/scripts/linkedin_api.py +561 -0
- package/scripts/linkedin_browser.py +730 -0
- package/scripts/linkedin_cooldown.py +128 -0
- package/scripts/linkedin_exclusions.py +234 -0
- package/scripts/linkedin_killswitch.py +1333 -0
- package/scripts/linkedin_search_topic_schema.py +49 -0
- package/scripts/linkedin_unipile.py +658 -0
- package/scripts/linkedin_url.py +228 -0
- package/scripts/log_claude_session.py +636 -0
- package/scripts/log_draft.py +143 -0
- package/scripts/log_linkedin_search_attempts.py +126 -0
- package/scripts/log_post.py +651 -0
- package/scripts/log_run.py +364 -0
- package/scripts/log_thread_media.py +108 -0
- package/scripts/log_twitter_search_attempts.py +150 -0
- package/scripts/log_twitter_skips.py +211 -0
- package/scripts/lookup_post.py +78 -0
- package/scripts/mark_web_chat_processed.py +32 -0
- package/scripts/mcp_lock_proxy.py +370 -0
- package/scripts/memory_snapshot.py +972 -0
- package/scripts/merge_review_queue.py +215 -0
- package/scripts/mint_external_pool.py +182 -0
- package/scripts/mint_kent_pool.py +249 -0
- package/scripts/moltbook_post.py +320 -0
- package/scripts/moltbook_tools.py +159 -0
- package/scripts/pending_threads.py +188 -0
- package/scripts/pick_ig_account.py +177 -0
- package/scripts/pick_project.py +208 -0
- package/scripts/pick_search_topic.py +771 -0
- package/scripts/pick_thread_target.py +279 -0
- package/scripts/pick_twitter_thread_target.py +202 -0
- package/scripts/podlog_fetch_batch.sh +32 -0
- package/scripts/post_github.py +1311 -0
- package/scripts/post_reddit.py +2668 -0
- package/scripts/precompute_dashboard_stats.py +204 -0
- package/scripts/preflight.sh +297 -0
- package/scripts/progress.py +88 -0
- package/scripts/project_excludes.py +353 -0
- package/scripts/project_slugs.py +91 -0
- package/scripts/project_stats.py +241 -0
- package/scripts/project_stats_json.py +1563 -0
- package/scripts/project_topics.py +192 -0
- package/scripts/qualified_query_bank.py +436 -0
- package/scripts/reap_stale_claude_sessions.py +867 -0
- package/scripts/reddit_browser.py +2549 -0
- package/scripts/reddit_browser_fetch.py +141 -0
- package/scripts/reddit_browser_lock.py +593 -0
- package/scripts/reddit_chat_sync.py +710 -0
- package/scripts/reddit_query_bank.py +200 -0
- package/scripts/reddit_threads_helper.py +151 -0
- package/scripts/reddit_tools.py +956 -0
- package/scripts/refresh_instagram_tokens.py +280 -0
- package/scripts/release-mcpb.sh +513 -0
- package/scripts/reply_db.py +334 -0
- package/scripts/reply_insert.py +98 -0
- package/scripts/reply_risk_digest.py +761 -0
- package/scripts/reset-test-machine.sh +602 -0
- package/scripts/restore_twitter_session.py +177 -0
- package/scripts/ripen_reddit_plan.py +478 -0
- package/scripts/run_claude.sh +433 -0
- package/scripts/run_moltbook_cycle.py +555 -0
- package/scripts/s4l_box_update.sh +226 -0
- package/scripts/s4l_channel.py +103 -0
- package/scripts/s4l_ctl.sh +75 -0
- package/scripts/s4l_env.py +47 -0
- package/scripts/saps_activity.py +126 -0
- package/scripts/saps_mode.py +328 -0
- package/scripts/scan_dm_candidates.py +580 -0
- package/scripts/scan_github_replies.py +168 -0
- package/scripts/scan_instagram_comments.py +481 -0
- package/scripts/scan_moltbook_replies.py +252 -0
- package/scripts/scan_pii.py +190 -0
- package/scripts/scan_reddit_replies.py +377 -0
- package/scripts/scan_twitter_mentions_browser.py +327 -0
- package/scripts/scan_twitter_thread_followups.py +299 -0
- package/scripts/scan_x_profile.py +384 -0
- package/scripts/schedule_state.py +202 -0
- package/scripts/scheduled_tasks_snapshot.py +123 -0
- package/scripts/score_linkedin_candidates.py +419 -0
- package/scripts/score_twitter_candidates.py +718 -0
- package/scripts/scrape_linkedin_comment_stats.py +1755 -0
- package/scripts/scrape_linkedin_stats_browser.py +52 -0
- package/scripts/scrape_reddit_views.py +365 -0
- package/scripts/seed_search_queries.py +453 -0
- package/scripts/seed_search_topics.py +127 -0
- package/scripts/send_web_chat_reply.py +130 -0
- package/scripts/sentry_init.py +128 -0
- package/scripts/setup_twitter_auth.py +1320 -0
- package/scripts/snapshot.py +583 -0
- package/scripts/stats.py +2702 -0
- package/scripts/stats_helper.py +52 -0
- package/scripts/strike_alert.py +783 -0
- package/scripts/sweep_post_link_clicks.py +107 -0
- package/scripts/sync_ig_to_posts.py +147 -0
- package/scripts/test_browser_lock.py +189 -0
- package/scripts/test_installation_api.sh +52 -0
- package/scripts/test_percard_posting.py +142 -0
- package/scripts/top_dud_linkedin_queries.py +71 -0
- package/scripts/top_dud_reddit_queries.py +67 -0
- package/scripts/top_dud_twitter_queries.py +71 -0
- package/scripts/top_dud_twitter_topics.py +102 -0
- package/scripts/top_linkedin_queries.py +55 -0
- package/scripts/top_omitted_reddit_topics.py +91 -0
- package/scripts/top_performers.py +588 -0
- package/scripts/top_search_topics.py +180 -0
- package/scripts/top_twitter_queries.py +190 -0
- package/scripts/twitter_access_check.py +382 -0
- package/scripts/twitter_account.py +41 -0
- package/scripts/twitter_batch_phase.py +126 -0
- package/scripts/twitter_browser.py +2804 -0
- package/scripts/twitter_cookie_mirror.py +130 -0
- package/scripts/twitter_cycle_helper.py +310 -0
- package/scripts/twitter_gen_links.py +287 -0
- package/scripts/twitter_post_plan.py +1188 -0
- package/scripts/twitter_scan.py +324 -0
- package/scripts/twitter_supply_signal.py +57 -0
- package/scripts/twitter_threads_helper.py +152 -0
- package/scripts/unclaim_web_chat.py +29 -0
- package/scripts/update_instagram_stats.py +261 -0
- package/scripts/update_linkedin_stats_from_feed.py +328 -0
- package/scripts/version.py +72 -0
- package/scripts/watchdog_hung_runs.py +343 -0
- package/scripts/write_generation_trace.py +73 -0
- package/setup/SKILL.md +277 -0
- package/skill/amplitude-24h-signups.sh +38 -0
- package/skill/archive-old-logs.sh +40 -0
- package/skill/audit-dm-staleness.sh +42 -0
- package/skill/audit-linkedin.sh +14 -0
- package/skill/audit-moltbook.sh +4 -0
- package/skill/audit-reddit-resurrect.sh +67 -0
- package/skill/audit-reddit.sh +4 -0
- package/skill/audit-twitter.sh +4 -0
- package/skill/audit.sh +287 -0
- package/skill/backfill-twitter-attempts-topic.sh +19 -0
- package/skill/backfill-twitter-ghost-posts.sh +24 -0
- package/skill/check-external-pool-depth.sh +7 -0
- package/skill/check-web-chats.sh +203 -0
- package/skill/dm-outreach-linkedin.sh +250 -0
- package/skill/dm-outreach-reddit.sh +274 -0
- package/skill/dm-outreach-twitter.sh +265 -0
- package/skill/engage-dm-replies-linkedin.sh +4 -0
- package/skill/engage-dm-replies-reddit.sh +4 -0
- package/skill/engage-dm-replies-twitter.sh +4 -0
- package/skill/engage-dm-replies.sh +1597 -0
- package/skill/engage-linkedin.sh +581 -0
- package/skill/engage-moltbook.sh +36 -0
- package/skill/engage-reddit.sh +146 -0
- package/skill/engage-twitter.sh +467 -0
- package/skill/github-engage.sh +176 -0
- package/skill/ingest-web-chat-replies.sh +38 -0
- package/skill/invent-supply-test.sh +100 -0
- package/skill/invent-topics.sh +50 -0
- package/skill/lib/linkedin-backend.sh +364 -0
- package/skill/lib/platform.sh +48 -0
- package/skill/lib/reddit-backend.sh +234 -0
- package/skill/lib/twitter-backend.sh +314 -0
- package/skill/link-edit-github.sh +136 -0
- package/skill/link-edit-moltbook.sh +117 -0
- package/skill/link-edit-reddit.sh +201 -0
- package/skill/linkedin-presence.sh +182 -0
- package/skill/linkedin-recovery.sh +282 -0
- package/skill/lock.sh +647 -0
- package/skill/memory-snapshot.sh +39 -0
- package/skill/precompute-stats.sh +35 -0
- package/skill/prewarm-funnel.sh +104 -0
- package/skill/refresh-instagram-tokens.sh +57 -0
- package/skill/refresh-twitter-following.sh +52 -0
- package/skill/reply-risk-digest.sh +31 -0
- package/skill/run-cycle-update-guard.sh +44 -0
- package/skill/run-draft-and-publish.sh +123 -0
- package/skill/run-generate-daily-style.sh +50 -0
- package/skill/run-github-launchd.sh +62 -0
- package/skill/run-github.sh +102 -0
- package/skill/run-instagram-daily.sh +149 -0
- package/skill/run-instagram-render.sh +875 -0
- package/skill/run-linkedin-launchd.sh +81 -0
- package/skill/run-linkedin-unipile.sh +130 -0
- package/skill/run-linkedin.sh +1593 -0
- package/skill/run-moltbook-launchd.sh +61 -0
- package/skill/run-moltbook.sh +38 -0
- package/skill/run-overlay-watch.sh +100 -0
- package/skill/run-reddit-search-launchd.sh +64 -0
- package/skill/run-reddit-search.sh +505 -0
- package/skill/run-reddit-threads-double.sh +32 -0
- package/skill/run-reddit-threads.sh +847 -0
- package/skill/run-scan-moltbook-replies.sh +57 -0
- package/skill/run-twitter-cycle-launchd.sh +63 -0
- package/skill/run-twitter-cycle-singleton.sh +62 -0
- package/skill/run-twitter-cycle.sh +2408 -0
- package/skill/run-twitter-threads.sh +592 -0
- package/skill/scan-instagram-replies.sh +61 -0
- package/skill/scan-twitter-followups.sh +57 -0
- package/skill/social-autoposter-update.sh +66 -0
- package/skill/stats-instagram.sh +72 -0
- package/skill/stats-linkedin.sh +271 -0
- package/skill/stats-moltbook.sh +4 -0
- package/skill/stats-reddit.sh +4 -0
- package/skill/stats-twitter.sh +4 -0
- package/skill/stats.sh +521 -0
- package/skill/strike-alert.sh +18 -0
- package/skill/styles.sh +87 -0
- package/skill/sweep-link-clicks.sh +40 -0
- package/skill/topics.sh +51 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# run-moltbook-launchd.sh — detach wrapper invoked by launchd.
|
|
3
|
+
#
|
|
4
|
+
# Why this exists:
|
|
5
|
+
# launchd's StartInterval silently SUPPRESSES a scheduled fire when the prior
|
|
6
|
+
# invocation of the same Label is still alive. The moltbook cycle has a 600s
|
|
7
|
+
# T0->T1 sleep plus phased posting, so a single run takes 12-25 min and
|
|
8
|
+
# regularly overlaps the next 15-min slot. Without this wrapper we lost
|
|
9
|
+
# 1-2 of every 4 fires.
|
|
10
|
+
#
|
|
11
|
+
# How it works:
|
|
12
|
+
# Python double-fork daemon idiom — first fork gives launchd a parent that
|
|
13
|
+
# exits immediately (so the job is marked complete in milliseconds), setsid
|
|
14
|
+
# detaches the session, second fork prevents reacquiring a controlling
|
|
15
|
+
# terminal, then we exec the real pipeline. macOS lacks `setsid(1)` and
|
|
16
|
+
# `nohup ... & disown` is not enough because launchd reaps the wrapper's
|
|
17
|
+
# pgid, taking the nohup child with it.
|
|
18
|
+
#
|
|
19
|
+
# Cross-cycle safety:
|
|
20
|
+
# run_moltbook_cycle.py uses already_posted_thread_ids() against the posts
|
|
21
|
+
# table at pick time, so two overlapping cycles will not double-post the
|
|
22
|
+
# same MoltBook thread. The 600s T0->T1 sleep further narrows the window.
|
|
23
|
+
|
|
24
|
+
REPO_DIR="$HOME/social-autoposter"
|
|
25
|
+
LOG_DIR="$REPO_DIR/skill/logs"
|
|
26
|
+
mkdir -p "$LOG_DIR"
|
|
27
|
+
|
|
28
|
+
SCRIPT="$REPO_DIR/skill/run-moltbook.sh"
|
|
29
|
+
OUT="$LOG_DIR/launchd-moltbook-stdout.log"
|
|
30
|
+
ERR="$LOG_DIR/launchd-moltbook-stderr.log"
|
|
31
|
+
|
|
32
|
+
# Preflight (added 2026-05-02): skip cleanly if Claude is blocked on a
|
|
33
|
+
# quota cap, or if the system is under memory pressure. See
|
|
34
|
+
# scripts/preflight.sh for full design.
|
|
35
|
+
SA_PREFLIGHT_SCRIPT="run-moltbook"
|
|
36
|
+
source "$REPO_DIR/scripts/preflight.sh"
|
|
37
|
+
preflight_skip_if_claude_blocked
|
|
38
|
+
preflight_skip_if_jetsam_pressure
|
|
39
|
+
|
|
40
|
+
exec /usr/bin/python3 -c "
|
|
41
|
+
import os, sys
|
|
42
|
+
script = '$SCRIPT'
|
|
43
|
+
out_log = '$OUT'
|
|
44
|
+
err_log = '$ERR'
|
|
45
|
+
|
|
46
|
+
if os.fork() != 0:
|
|
47
|
+
os._exit(0)
|
|
48
|
+
os.setsid()
|
|
49
|
+
|
|
50
|
+
if os.fork() != 0:
|
|
51
|
+
os._exit(0)
|
|
52
|
+
|
|
53
|
+
os.chdir('/')
|
|
54
|
+
out_fd = os.open(out_log, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o644)
|
|
55
|
+
err_fd = os.open(err_log, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o644)
|
|
56
|
+
nul_fd = os.open('/dev/null', os.O_RDONLY)
|
|
57
|
+
os.dup2(nul_fd, 0)
|
|
58
|
+
os.dup2(out_fd, 1)
|
|
59
|
+
os.dup2(err_fd, 2)
|
|
60
|
+
os.execv(script, [script])
|
|
61
|
+
"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Social Autoposter - MoltBook phased posting cycle.
|
|
3
|
+
#
|
|
4
|
+
# Delegates to scripts/run_moltbook_cycle.py which implements:
|
|
5
|
+
# - Phase 1: scan hot + new via MoltBook API, snapshot T0
|
|
6
|
+
# - Sleep 600s (T0 -> T1 momentum window)
|
|
7
|
+
# - Phase 2a: re-poll for T1, compute delta_score
|
|
8
|
+
# - Phase 2b: adaptive cap (default 2, bump to 5 when >=3 candidates
|
|
9
|
+
# show real-time momentum), Claude drafts, Python posts
|
|
10
|
+
#
|
|
11
|
+
# Three reduction levers baked in:
|
|
12
|
+
# (1) Historical (project, style) engagement block injected into the drafter prompt.
|
|
13
|
+
# (2) Adaptive cap gated by per-cycle momentum.
|
|
14
|
+
# (3) T0 -> T1 delta filter: dead threads drop out before Claude sees them.
|
|
15
|
+
#
|
|
16
|
+
# Called by launchd every 30 minutes.
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
[ -f "$HOME/social-autoposter/.env" ] && source "$HOME/social-autoposter/.env"
|
|
21
|
+
|
|
22
|
+
REPO_DIR="$HOME/social-autoposter"
|
|
23
|
+
LOG_DIR="$REPO_DIR/skill/logs"
|
|
24
|
+
mkdir -p "$LOG_DIR"
|
|
25
|
+
LOG_FILE="$LOG_DIR/run-moltbook-$(date +%Y-%m-%d_%H%M%S).log"
|
|
26
|
+
|
|
27
|
+
# Per-cycle batch id stamped onto every claude_sessions row spawned by this
|
|
28
|
+
# run (via SA_CYCLE_ID env -> log_claude_session.py). 2026-05-10 cycle_id rollout.
|
|
29
|
+
BATCH_ID="mbcycle-$(date +%Y%m%d-%H%M%S)-$$"
|
|
30
|
+
export SA_CYCLE_ID="$BATCH_ID"
|
|
31
|
+
|
|
32
|
+
echo "=== MoltBook Post Run: $(date) (cycle=$BATCH_ID) ===" | tee "$LOG_FILE"
|
|
33
|
+
|
|
34
|
+
python3 "$REPO_DIR/scripts/run_moltbook_cycle.py" 2>&1 | tee -a "$LOG_FILE"
|
|
35
|
+
|
|
36
|
+
echo "=== Run complete: $(date) ===" | tee -a "$LOG_FILE"
|
|
37
|
+
|
|
38
|
+
find "$LOG_DIR" -name "run-moltbook-*.log" -mtime +7 -delete 2>/dev/null || true
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Idempotent supervisor for the twitter-harness on-screen overlay watcher.
|
|
3
|
+
#
|
|
4
|
+
# WHAT: keeps exactly ONE `harness_overlay.py watch` process alive. That watcher
|
|
5
|
+
# injects the status overlay into the twitter-harness Chrome window so a human
|
|
6
|
+
# watching the harness sees what the pipeline is doing.
|
|
7
|
+
#
|
|
8
|
+
# WHY a supervisor: the overlay only renders WHILE the watch process runs. It was
|
|
9
|
+
# previously a manual, local-only process, so it never appeared on headless /
|
|
10
|
+
# remote installs. This script makes it self-starting from BOTH install lanes:
|
|
11
|
+
# - Lane A (npm/cli): launchd job `com.m13v.social-overlay-watch` (StartInterval
|
|
12
|
+
# 60 + RunAtLoad) re-invokes this script every minute; the pgrep guard makes
|
|
13
|
+
# re-invocation a no-op while the watcher is already up.
|
|
14
|
+
# - Lane B (.mcpb / pure MCP): the MCP calls this script on draft_cycle /
|
|
15
|
+
# autopilot-enable / show_browser_to_user, threading S4L_PYTHON + S4L_LOG_DIR.
|
|
16
|
+
#
|
|
17
|
+
# IDEMPOTENT: safe to call on a 60s timer. If a watcher is already running it
|
|
18
|
+
# exits 0 immediately. Otherwise it spawns one detached (nohup, own session) and
|
|
19
|
+
# returns; the spawned watcher then runs until the machine/MCP tears it down.
|
|
20
|
+
#
|
|
21
|
+
# This script is intentionally NOT locked: the overlay UX is expected to evolve.
|
|
22
|
+
|
|
23
|
+
set -u
|
|
24
|
+
|
|
25
|
+
# SAPS_->S4L_ env mirror (brand rename 2026-07-03): old plists/tasks still
|
|
26
|
+
# export SAPS_*; new code reads S4L_*. Copy names, never values via eval.
|
|
27
|
+
while IFS='=' read -r _k _; do
|
|
28
|
+
case "$_k" in SAPS_*) _n="S4L_${_k#SAPS_}"; eval "[ -n \"\${$_n+x}\" ] || export $_n=\"\${$_k}\"";; esac
|
|
29
|
+
done <<EOF_ENV
|
|
30
|
+
$(env | grep '^SAPS_' | cut -d= -f1 | sed 's/$/=/')
|
|
31
|
+
EOF_ENV
|
|
32
|
+
|
|
33
|
+
# --- resolve the repo this script lives in (works from launchd + MCP cwd) -----
|
|
34
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
35
|
+
REPO_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
36
|
+
OVERLAY_PY="${REPO_DIR}/scripts/harness_overlay.py"
|
|
37
|
+
|
|
38
|
+
if [ ! -f "${OVERLAY_PY}" ]; then
|
|
39
|
+
echo "[overlay-watch] harness_overlay.py not found at ${OVERLAY_PY}; nothing to do" >&2
|
|
40
|
+
exit 0
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# --- idempotency guard: at most one watcher, ever ----------------------------
|
|
44
|
+
# Match the full `harness_overlay.py watch` invocation (NOT a bare `grep -r`, so
|
|
45
|
+
# this never trips the BSD-grep-on-FIFO hang noted in the repo CLAUDE.md).
|
|
46
|
+
if pgrep -f "harness_overlay.py watch" >/dev/null 2>&1; then
|
|
47
|
+
exit 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# --- resolve a python interpreter --------------------------------------------
|
|
51
|
+
# harness_overlay.py self-heals to a playwright-capable interpreter on its own
|
|
52
|
+
# (see _ensure_playwright_interpreter), so any python3 that exists is fine to
|
|
53
|
+
# launch with. Prefer the MCP-threaded S4L_PYTHON (owned uv runtime), then the
|
|
54
|
+
# usual suspects.
|
|
55
|
+
PYBIN=""
|
|
56
|
+
for _cand in \
|
|
57
|
+
"${S4L_PYTHON:-}" \
|
|
58
|
+
"/opt/homebrew/bin/python3.11" \
|
|
59
|
+
"/usr/bin/python3" \
|
|
60
|
+
"/opt/homebrew/bin/python3"
|
|
61
|
+
do
|
|
62
|
+
if [ -n "${_cand}" ] && [ -x "${_cand}" ]; then PYBIN="${_cand}"; break; fi
|
|
63
|
+
done
|
|
64
|
+
if [ -z "${PYBIN}" ]; then
|
|
65
|
+
PYBIN="$(command -v python3 2>/dev/null || true)"
|
|
66
|
+
fi
|
|
67
|
+
if [ -z "${PYBIN}" ]; then
|
|
68
|
+
echo "[overlay-watch] no python3 found; cannot start overlay watcher" >&2
|
|
69
|
+
exit 0
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# --- env the watcher needs ---------------------------------------------------
|
|
73
|
+
# S4L_LOG_DIR: where harness_overlay.py reads cycle logs to decide busy/idle.
|
|
74
|
+
# Default to this repo's skill/logs (MCP overrides it to the materialized repo).
|
|
75
|
+
export S4L_LOG_DIR="${S4L_LOG_DIR:-${REPO_DIR}/skill/logs}"
|
|
76
|
+
# CDP target for the twitter harness Chrome (honor BYO-Chrome installs).
|
|
77
|
+
export TWITTER_CDP_URL="${TWITTER_CDP_URL:-http://127.0.0.1:9555}"
|
|
78
|
+
mkdir -p "${S4L_LOG_DIR}" 2>/dev/null || true
|
|
79
|
+
WATCH_LOG="${S4L_LOG_DIR}/overlay-watch.log"
|
|
80
|
+
|
|
81
|
+
# --- spawn detached ----------------------------------------------------------
|
|
82
|
+
cd "${REPO_DIR}" || exit 0
|
|
83
|
+
echo "[overlay-watch] $(date '+%Y-%m-%d %H:%M:%S') starting watcher py=${PYBIN} cdp=${TWITTER_CDP_URL} log=${S4L_LOG_DIR}" >>"${WATCH_LOG}" 2>&1
|
|
84
|
+
# Spawn the watcher in a NEW SESSION so it outlives this supervisor.
|
|
85
|
+
# When launchd fires this script (StartInterval 60), the script is the job's
|
|
86
|
+
# main process; the moment it exits, launchd reaps the WHOLE job process group.
|
|
87
|
+
# `nohup` only blocks SIGHUP, not that group SIGKILL, so a plain `nohup ... &`
|
|
88
|
+
# child dies the instant we `exit 0` (it survives only when this runs from an
|
|
89
|
+
# interactive shell, which is NOT a launchd job). macOS ships no setsid(1), so
|
|
90
|
+
# use the python we already resolved to os.setsid() off the launchd group, then
|
|
91
|
+
# exec the real watcher. The watcher's own interpreter self-heal (os.execv)
|
|
92
|
+
# keeps the new session.
|
|
93
|
+
nohup "${PYBIN}" -c 'import os, sys
|
|
94
|
+
try:
|
|
95
|
+
os.setsid()
|
|
96
|
+
except OSError:
|
|
97
|
+
pass
|
|
98
|
+
os.execv(sys.argv[1], sys.argv[1:])' "${PYBIN}" "${OVERLAY_PY}" watch >>"${WATCH_LOG}" 2>&1 &
|
|
99
|
+
disown 2>/dev/null || true
|
|
100
|
+
exit 0
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# run-reddit-search-launchd.sh — detach wrapper invoked by launchd.
|
|
3
|
+
#
|
|
4
|
+
# Why this exists:
|
|
5
|
+
# launchd's StartInterval silently SUPPRESSES a scheduled fire when the prior
|
|
6
|
+
# invocation of the same Label is still alive. Reddit-search cycles vary
|
|
7
|
+
# (5 iterations of plan+post) and routinely run 15-25 min, so 1-3 of every
|
|
8
|
+
# 4 fires were getting dropped. Throughput collapsed to ~2 cycles/hr instead
|
|
9
|
+
# of 4.
|
|
10
|
+
#
|
|
11
|
+
# How it works:
|
|
12
|
+
# This wrapper is what launchd actually invokes. It uses Python's classic
|
|
13
|
+
# double-fork daemon idiom to spawn the real pipeline in a fresh session
|
|
14
|
+
# (os.setsid), then exits. macOS lacks the `setsid` binary and a plain
|
|
15
|
+
# `nohup ... &; disown` is NOT sufficient — launchd cleans up the wrapper's
|
|
16
|
+
# process group after the wrapper exits and SIGTERMs the nohup'd child too.
|
|
17
|
+
# The double-fork moves the cycle into its own session/pgid that launchd
|
|
18
|
+
# no longer tracks, so it survives wrapper exit.
|
|
19
|
+
#
|
|
20
|
+
# Net effect: launchd marks the job complete in milliseconds and fires the
|
|
21
|
+
# next 15-min slot on schedule. Multiple run-reddit-search.sh processes can
|
|
22
|
+
# now live in parallel; the reddit-browser lock (skill/lock.sh) serializes
|
|
23
|
+
# the brief post-phase windows, and post_reddit.py's already_posted filter
|
|
24
|
+
# prevents double-posting across overlapping cycles.
|
|
25
|
+
|
|
26
|
+
REPO_DIR="$HOME/social-autoposter"
|
|
27
|
+
LOG_DIR="$REPO_DIR/skill/logs"
|
|
28
|
+
mkdir -p "$LOG_DIR"
|
|
29
|
+
|
|
30
|
+
SCRIPT="$REPO_DIR/skill/run-reddit-search.sh"
|
|
31
|
+
OUT="$LOG_DIR/launchd-reddit-search-stdout.log"
|
|
32
|
+
ERR="$LOG_DIR/launchd-reddit-search-stderr.log"
|
|
33
|
+
|
|
34
|
+
# Preflight (added 2026-05-02): skip cleanly if Claude is blocked on a
|
|
35
|
+
# quota cap, or if the system is under memory pressure. Both paths exit 0
|
|
36
|
+
# with a stderr [skipped:] line so launchd treats the slot as consumed.
|
|
37
|
+
# See scripts/preflight.sh for full design.
|
|
38
|
+
SA_PREFLIGHT_SCRIPT="run-reddit-search"
|
|
39
|
+
source "$REPO_DIR/scripts/preflight.sh"
|
|
40
|
+
preflight_skip_if_claude_blocked
|
|
41
|
+
preflight_skip_if_jetsam_pressure
|
|
42
|
+
|
|
43
|
+
exec /usr/bin/python3 -c "
|
|
44
|
+
import os, sys
|
|
45
|
+
script = '$SCRIPT'
|
|
46
|
+
out_log = '$OUT'
|
|
47
|
+
err_log = '$ERR'
|
|
48
|
+
|
|
49
|
+
if os.fork() != 0:
|
|
50
|
+
os._exit(0)
|
|
51
|
+
os.setsid()
|
|
52
|
+
|
|
53
|
+
if os.fork() != 0:
|
|
54
|
+
os._exit(0)
|
|
55
|
+
|
|
56
|
+
os.chdir('/')
|
|
57
|
+
out_fd = os.open(out_log, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o644)
|
|
58
|
+
err_fd = os.open(err_log, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o644)
|
|
59
|
+
nul_fd = os.open('/dev/null', os.O_RDONLY)
|
|
60
|
+
os.dup2(nul_fd, 0)
|
|
61
|
+
os.dup2(out_fd, 1)
|
|
62
|
+
os.dup2(err_fd, 2)
|
|
63
|
+
os.execv(script, [script])
|
|
64
|
+
"
|