social-autoposter 1.6.151 → 1.6.153
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/version.json +2 -2
- package/mcp/manifest.json +1 -1
- package/mcp/menubar/s4l_menubar.py +49 -0
- package/mcp/package.json +1 -1
- package/package.json +1 -1
- package/skill/run-twitter-cycle.sh +9 -7
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.153",
|
|
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 end to end**\n\n2\\. Quit fully with CMD+Q, restart Claude, and paste the prompt into a new chat.",
|
|
8
8
|
"author": {
|
|
@@ -143,6 +143,31 @@ AUTOPILOT_RUNNING_STALL_SECONDS = 900
|
|
|
143
143
|
# truth, scripts/schedule_state.py (FIRING_WINDOW there). _schedule_state delegates
|
|
144
144
|
# to it, so it is intentionally NOT redefined here.
|
|
145
145
|
|
|
146
|
+
# How long the producer can sit narrating "drafting replies (Nm)" before we treat
|
|
147
|
+
# the draft as STUCK rather than healthy. The producer writes that label the whole
|
|
148
|
+
# time it blocks waiting for a worker to return a result (up to its 30-min queue
|
|
149
|
+
# timeout). A healthy drain clears in ~1-2 min; if the label has been "drafting"
|
|
150
|
+
# this long, the worker keeps dying mid-run (host inactivity-kill) or never claims,
|
|
151
|
+
# and nothing is draining — so we flip the menu bar from a reassuring spinner to
|
|
152
|
+
# ⚠ instead of letting the stale "drafting (8m)" lie persist. Well above any
|
|
153
|
+
# healthy single drain (the worker itself dies at ~2 min today).
|
|
154
|
+
DRAFT_STUCK_SECONDS = 300
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _label_elapsed_secs(label):
|
|
158
|
+
"""Parse the trailing duration the producer encodes in a drafting activity
|
|
159
|
+
label — 'drafting replies (8m)', '... (queued 18m)', '... (45s)' — into
|
|
160
|
+
seconds. Returns 0 when there's no parseable duration. _fmt_dur (claude_job.py)
|
|
161
|
+
only ever emits '<n>s' (<60s) or '<n>m', so this mirror stays trivial."""
|
|
162
|
+
if not label:
|
|
163
|
+
return 0
|
|
164
|
+
import re
|
|
165
|
+
matches = re.findall(r"(\d+)\s*([sm])\b", str(label))
|
|
166
|
+
if not matches:
|
|
167
|
+
return 0
|
|
168
|
+
n, unit = matches[-1]
|
|
169
|
+
return int(n) * (60 if unit == "m" else 1)
|
|
170
|
+
|
|
146
171
|
|
|
147
172
|
def _glyph(status):
|
|
148
173
|
return GLYPH.get(status, "·")
|
|
@@ -1013,6 +1038,22 @@ class S4LMenuBar(rumps.App):
|
|
|
1013
1038
|
attention = True
|
|
1014
1039
|
else:
|
|
1015
1040
|
self._stall_reason_info = ("", "")
|
|
1041
|
+
# Draft worker stuck/killed: the producer narrates "drafting replies (Nm)"
|
|
1042
|
+
# the whole time it blocks waiting for a worker to return a result, with NO
|
|
1043
|
+
# idea the worker died. A healthy drain clears in ~1-2 min; once that label
|
|
1044
|
+
# has been "drafting" past DRAFT_STUCK_SECONDS the worker keeps getting
|
|
1045
|
+
# killed mid-run (or never claims) and nothing is draining — flip to ⚠
|
|
1046
|
+
# instead of leaving the reassuring "drafting (8m)" spinner up. Skip when a
|
|
1047
|
+
# more specific cause (rate limit) already owns the reason.
|
|
1048
|
+
if setup_complete and self._stall_reason_info[0] != "rate_limited":
|
|
1049
|
+
_act = st.read_activity()
|
|
1050
|
+
if (
|
|
1051
|
+
_act
|
|
1052
|
+
and _act.get("state") == "drafting"
|
|
1053
|
+
and _label_elapsed_secs(_act.get("label")) >= DRAFT_STUCK_SECONDS
|
|
1054
|
+
):
|
|
1055
|
+
attention = True
|
|
1056
|
+
self._stall_reason_info = ("draft_stuck", _act.get("label") or "")
|
|
1016
1057
|
# Drop the stale "drafting" spinner while we need attention so the ⚠ shows.
|
|
1017
1058
|
self._stalled = attention
|
|
1018
1059
|
|
|
@@ -1350,6 +1391,14 @@ class S4LMenuBar(rumps.App):
|
|
|
1350
1391
|
items.append(self._label(
|
|
1351
1392
|
" " + (self._stall_reason_info[1] or "wait for reset or switch account")
|
|
1352
1393
|
))
|
|
1394
|
+
elif self._stall_reason_info[0] == "draft_stuck":
|
|
1395
|
+
# Routines fire and the producer keeps narrating "drafting" but the
|
|
1396
|
+
# worker keeps getting killed mid-run / never returns a result. Don't
|
|
1397
|
+
# offer Re-arm (routines are fine); state the real problem.
|
|
1398
|
+
items.append(self._label("⚠ Draft not completing — worker keeps getting killed"))
|
|
1399
|
+
items.append(self._label(
|
|
1400
|
+
" " + (self._stall_reason_info[1] or "drafting") + " — no result yet"
|
|
1401
|
+
))
|
|
1353
1402
|
elif schedule_state == "disabled":
|
|
1354
1403
|
items.append(self._label("⚠ Draft tasks are scheduled but disabled"))
|
|
1355
1404
|
items.append(rumps.MenuItem("Set up draft schedule for this account", callback=self._rearm))
|
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.153",
|
|
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
|
@@ -1772,21 +1772,23 @@ log "Engagement style assigned: mode=$PICKED_MODE style=${PICKED_STYLE:-(invent)
|
|
|
1772
1772
|
# The arm is stamped onto every post this cycle via SAPS_DRAFT_PROMPT_VARIANT
|
|
1773
1773
|
# (read by twitter_post_plan.py -> log_post.py -> posts.draft_prompt_variant),
|
|
1774
1774
|
# mirroring the tail_link_variant plumbing. Split tunable via
|
|
1775
|
-
# TWITTER_DRAFT_PROMPT_AB_RATE
|
|
1776
|
-
#
|
|
1777
|
-
#
|
|
1778
|
-
#
|
|
1775
|
+
# TWITTER_DRAFT_PROMPT_AB_RATE = fraction of cycles assigned to 'treatment' (the
|
|
1776
|
+
# decoupled directive). CODE DEFAULT 1 = 100% treatment: a fresh plugin install /
|
|
1777
|
+
# new user with no override drafts in the DECOUPLED style by default (the old
|
|
1778
|
+
# concede->pivot 'control' is opt-in, not the default for customers). OUR own
|
|
1779
|
+
# install pins this to 0.5 in .env for a 50/50 holdback so we can measure
|
|
1780
|
+
# decoupled vs the old behavior on our account. The dashboard reads the SAME var
|
|
1779
1781
|
# with the SAME default (bin/server.js), so display and routing never diverge.
|
|
1780
|
-
DRAFT_PROMPT_AB_RATE="${TWITTER_DRAFT_PROMPT_AB_RATE:-
|
|
1782
|
+
DRAFT_PROMPT_AB_RATE="${TWITTER_DRAFT_PROMPT_AB_RATE:-1}"
|
|
1781
1783
|
SAPS_DRAFT_PROMPT_VARIANT=$(python3 -c "
|
|
1782
1784
|
import random
|
|
1783
1785
|
try:
|
|
1784
1786
|
rate = float('$DRAFT_PROMPT_AB_RATE')
|
|
1785
1787
|
except Exception:
|
|
1786
|
-
rate =
|
|
1788
|
+
rate = 1.0
|
|
1787
1789
|
rate = min(1.0, max(0.0, rate))
|
|
1788
1790
|
print('treatment' if random.random() < rate else 'control')
|
|
1789
|
-
" 2>/dev/null || echo
|
|
1791
|
+
" 2>/dev/null || echo treatment)
|
|
1790
1792
|
export SAPS_DRAFT_PROMPT_VARIANT
|
|
1791
1793
|
log "Draft-prompt A/B arm: $SAPS_DRAFT_PROMPT_VARIANT (rate=$DRAFT_PROMPT_AB_RATE)"
|
|
1792
1794
|
if [ "$SAPS_DRAFT_PROMPT_VARIANT" = "treatment" ]; then
|