social-autoposter 1.6.165 → 1.6.166
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/mcp/dist/index.js +32 -21
- package/mcp/dist/version.json +2 -2
- package/mcp/manifest.json +1 -1
- package/mcp/package.json +1 -1
- package/package.json +1 -1
- package/scripts/harness_overlay.py +15 -0
- package/skill/run-overlay-watch.sh +15 -1
package/mcp/dist/index.js
CHANGED
|
@@ -82,17 +82,21 @@ const MEMORY_SNAPSHOT_INTERVAL_SECS = 60;
|
|
|
82
82
|
const STALL_WATCH_LABEL = "com.m13v.social-autopilot-stall-watch";
|
|
83
83
|
const STALL_WATCH_PLIST = path.join(os.homedir(), "Library", "LaunchAgents", `${STALL_WATCH_LABEL}.plist`);
|
|
84
84
|
const STALL_WATCH_INTERVAL_SECS = 120;
|
|
85
|
-
// On-screen overlay watcher
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
85
|
+
// On-screen overlay watcher. The harness status overlay ("S4L running" / idle
|
|
86
|
+
// banner) only renders WHILE `harness_overlay.py watch` is alive. That watcher
|
|
87
|
+
// is fire-and-forget with no supervisor of its own, so when it dies (or the
|
|
88
|
+
// harness Chrome restarts) nothing brings it back and the overlay silently
|
|
89
|
+
// disappears. Promote it to a first-class launchd job, but run the long-lived
|
|
90
|
+
// watcher in the FOREGROUND under KeepAlive (NOT a StartInterval that re-invokes
|
|
91
|
+
// a spawn-and-exit supervisor). The supervisor pattern races launchd on macOS:
|
|
92
|
+
// the instant the kicker shell exits, launchd SIGKILLs the whole job process
|
|
93
|
+
// group and reaps the just-spawned watcher before it can detach. Running the
|
|
94
|
+
// watcher AS the job's main process makes launchd supervise it directly:
|
|
95
|
+
// RunAtLoad starts it at boot, KeepAlive restarts it if it ever exits, and on
|
|
96
|
+
// unload its SIGTERM handler clears the overlay cleanly. Disable with
|
|
97
|
+
// SAPS_OVERLAY_WATCH=0.
|
|
93
98
|
const OVERLAY_WATCH_LABEL = "com.m13v.social-overlay-watch";
|
|
94
99
|
const OVERLAY_WATCH_PLIST = path.join(os.homedir(), "Library", "LaunchAgents", `${OVERLAY_WATCH_LABEL}.plist`);
|
|
95
|
-
const OVERLAY_WATCH_INTERVAL_SECS = 60;
|
|
96
100
|
// Daily self-updater. Enabled alongside autopilot so a hands-free (headless)
|
|
97
101
|
// install keeps itself current — the interactive `runtime` tool (action:'update')
|
|
98
102
|
// only helps when
|
|
@@ -134,6 +138,9 @@ function launchdPath() {
|
|
|
134
138
|
}
|
|
135
139
|
function plistXml(opts) {
|
|
136
140
|
const args = opts.programArgs.map((a) => `\t\t<string>${a}</string>`).join("\n");
|
|
141
|
+
const schedule = opts.keepAlive
|
|
142
|
+
? `\t<key>KeepAlive</key>\n\t<true/>`
|
|
143
|
+
: `\t<key>StartInterval</key>\n\t<integer>${opts.intervalSecs}</integer>`;
|
|
137
144
|
// Background (cron/autopilot) runs get the same Chrome the interactive cycle
|
|
138
145
|
// uses, so a no-sudo ~/Applications install (which the shell's own resolver
|
|
139
146
|
// doesn't scan) is still found off-screen. Omitted when Chrome resolves via
|
|
@@ -159,8 +166,7 @@ function plistXml(opts) {
|
|
|
159
166
|
\t<array>
|
|
160
167
|
${args}
|
|
161
168
|
\t</array>
|
|
162
|
-
|
|
163
|
-
\t<integer>${opts.intervalSecs}</integer>
|
|
169
|
+
${schedule}
|
|
164
170
|
\t<key>StandardOutPath</key>
|
|
165
171
|
\t<string>${opts.stdoutLog}</string>
|
|
166
172
|
\t<key>StandardErrorPath</key>
|
|
@@ -2695,15 +2701,19 @@ async function ensureMemorySnapshotInstalled() {
|
|
|
2695
2701
|
return { ok: false, detail: e?.message || String(e) };
|
|
2696
2702
|
}
|
|
2697
2703
|
}
|
|
2698
|
-
// Install/refresh the on-screen overlay watcher launchd
|
|
2704
|
+
// Install/refresh the on-screen overlay watcher launchd job. Promotes the
|
|
2699
2705
|
// harness status overlay from a best-effort, fired-from-other-tools nicety to a
|
|
2700
|
-
// first-class self-healing job
|
|
2701
|
-
//
|
|
2702
|
-
//
|
|
2703
|
-
//
|
|
2704
|
-
//
|
|
2705
|
-
//
|
|
2706
|
-
//
|
|
2706
|
+
// first-class self-healing job. We run `harness_overlay.py watch` directly in
|
|
2707
|
+
// the FOREGROUND under KeepAlive (RunAtLoad starts it at boot; launchd restarts
|
|
2708
|
+
// it if it ever exits) rather than a StartInterval that re-fires a spawn-and-exit
|
|
2709
|
+
// supervisor: on macOS that supervisor races launchd, which SIGKILLs the job's
|
|
2710
|
+
// process group the instant the kicker shell exits and reaps the just-spawned
|
|
2711
|
+
// watcher before it can detach (verified on the box: the watcher caught the
|
|
2712
|
+
// group SIGTERM and cleared the overlay every cycle). harness_overlay.py holds a
|
|
2713
|
+
// singleton flock so the MCP's best-effort run-overlay-watch.sh lane can never
|
|
2714
|
+
// double-paint. SAPS_PYTHON is baked by plistXml; we add SAPS_LOG_DIR (so the
|
|
2715
|
+
// watcher reads the same cycle logs to decide busy/idle) and the harness CDP
|
|
2716
|
+
// URL. Disable with SAPS_OVERLAY_WATCH=0.
|
|
2707
2717
|
async function ensureOverlayWatchInstalled() {
|
|
2708
2718
|
try {
|
|
2709
2719
|
if (process.platform !== "darwin")
|
|
@@ -2719,8 +2729,9 @@ async function ensureOverlayWatchInstalled() {
|
|
|
2719
2729
|
}
|
|
2720
2730
|
const xml = plistXml({
|
|
2721
2731
|
label: OVERLAY_WATCH_LABEL,
|
|
2722
|
-
programArgs: [
|
|
2723
|
-
intervalSecs:
|
|
2732
|
+
programArgs: [resolvePython(), path.join(repoDir(), "scripts", "harness_overlay.py"), "watch"],
|
|
2733
|
+
intervalSecs: 0,
|
|
2734
|
+
keepAlive: true,
|
|
2724
2735
|
runAtLoad: true,
|
|
2725
2736
|
stdoutLog: path.join(logDir, "launchd-overlay-watch-stdout.log"),
|
|
2726
2737
|
stderrLog: path.join(logDir, "launchd-overlay-watch-stderr.log"),
|
package/mcp/dist/version.json
CHANGED
package/mcp/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"dxt_version": "0.1",
|
|
3
3
|
"name": "social-autoposter",
|
|
4
4
|
"display_name": "S4L",
|
|
5
|
-
"version": "1.6.
|
|
5
|
+
"version": "1.6.166",
|
|
6
6
|
"description": "Draft, review, approve, and autopilot X/Twitter posts.",
|
|
7
7
|
"long_description": "The disclaimer above is generic Claude boilerplate. S4L is an open source product developed by Mediar.ai Incorporated, a VC-backed San Francisco-based startup.\n\nTo get started:\n\n1\\. Copy this prompt: **Set me up on S4L plugin end to end**\n\n2\\. Quit fully with CMD+Q, restart Claude, and paste the prompt into a new chat.",
|
|
8
8
|
"author": {
|
package/mcp/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m13v/social-autoposter-mcp",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.166",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Desktop MCP client for social-autoposter (X/Twitter rail): manual draft/review/approve loop, autopilot control, and stats. Thin wrapper over the existing pipeline scripts.",
|
|
6
6
|
"license": "MIT",
|
package/package.json
CHANGED
|
@@ -36,6 +36,7 @@ down it sleeps and retries; it never crashes the pipeline.
|
|
|
36
36
|
|
|
37
37
|
from __future__ import annotations
|
|
38
38
|
|
|
39
|
+
import fcntl
|
|
39
40
|
import glob
|
|
40
41
|
import json
|
|
41
42
|
import os
|
|
@@ -454,6 +455,20 @@ def cmd_watch(interval: float = 2.0) -> int:
|
|
|
454
455
|
holds ONE CDP connection open across ticks (light, and friendly to the
|
|
455
456
|
poster's concurrent CDP session) and only reconnects when the harness Chrome
|
|
456
457
|
comes/goes. Never raises into the pipeline."""
|
|
458
|
+
# Singleton guard: there must be exactly ONE watcher painting at a time, or
|
|
459
|
+
# two loops fight over the same overlay (double heartbeat, flicker). Two start
|
|
460
|
+
# lanes can race to spawn this: the MCP's foreground KeepAlive launchd job and
|
|
461
|
+
# the best-effort run-overlay-watch.sh supervisor. Hold an exclusive,
|
|
462
|
+
# non-blocking flock for the life of the process; if another watcher already
|
|
463
|
+
# holds it, exit 0 quietly and let that one own the overlay. The lock fd is
|
|
464
|
+
# intentionally leaked (kept open) until the process dies so the OS releases
|
|
465
|
+
# it automatically on exit/kill.
|
|
466
|
+
try:
|
|
467
|
+
_lock_fd = os.open("/tmp/saps_overlay_watch.lock", os.O_CREAT | os.O_RDWR, 0o644)
|
|
468
|
+
fcntl.flock(_lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
469
|
+
except OSError:
|
|
470
|
+
print("another overlay watcher already running; exiting", file=sys.stderr)
|
|
471
|
+
return 0
|
|
457
472
|
print(f"watching {LOG_DIR}/twitter-cycle-*.log -> overlay on {CDP_URL} (Ctrl-C to stop)")
|
|
458
473
|
# Treat SIGTERM (launchd unload, `kill`) like Ctrl-C so the overlay is
|
|
459
474
|
# cleared on the way out instead of lingering until the next navigation.
|
|
@@ -73,6 +73,20 @@ WATCH_LOG="${SAPS_LOG_DIR}/overlay-watch.log"
|
|
|
73
73
|
# --- spawn detached ----------------------------------------------------------
|
|
74
74
|
cd "${REPO_DIR}" || exit 0
|
|
75
75
|
echo "[overlay-watch] $(date '+%Y-%m-%d %H:%M:%S') starting watcher py=${PYBIN} cdp=${TWITTER_CDP_URL} log=${SAPS_LOG_DIR}" >>"${WATCH_LOG}" 2>&1
|
|
76
|
-
|
|
76
|
+
# Spawn the watcher in a NEW SESSION so it outlives this supervisor.
|
|
77
|
+
# When launchd fires this script (StartInterval 60), the script is the job's
|
|
78
|
+
# main process; the moment it exits, launchd reaps the WHOLE job process group.
|
|
79
|
+
# `nohup` only blocks SIGHUP, not that group SIGKILL, so a plain `nohup ... &`
|
|
80
|
+
# child dies the instant we `exit 0` (it survives only when this runs from an
|
|
81
|
+
# interactive shell, which is NOT a launchd job). macOS ships no setsid(1), so
|
|
82
|
+
# use the python we already resolved to os.setsid() off the launchd group, then
|
|
83
|
+
# exec the real watcher. The watcher's own interpreter self-heal (os.execv)
|
|
84
|
+
# keeps the new session.
|
|
85
|
+
nohup "${PYBIN}" -c 'import os, sys
|
|
86
|
+
try:
|
|
87
|
+
os.setsid()
|
|
88
|
+
except OSError:
|
|
89
|
+
pass
|
|
90
|
+
os.execv(sys.argv[1], sys.argv[1:])' "${PYBIN}" "${OVERLAY_PY}" watch >>"${WATCH_LOG}" 2>&1 &
|
|
77
91
|
disown 2>/dev/null || true
|
|
78
92
|
exit 0
|