social-autoposter 1.6.113 → 1.6.114
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 +66 -1
- package/mcp/menubar/s4l_state.py +20 -0
- package/mcp/package.json +1 -1
- package/package.json +1 -1
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.114",
|
|
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": {
|
|
@@ -168,6 +168,16 @@ class S4LMenuBar(rumps.App):
|
|
|
168
168
|
self._posting_batch_done = 0
|
|
169
169
|
self._spin_i = 0
|
|
170
170
|
self._spinner = None # fast rumps.Timer animating the title while busy
|
|
171
|
+
# Durable posting progress, derived from the review-queue PLAN rather than
|
|
172
|
+
# this process's in-memory burst queue. The in-memory counter dies on a
|
|
173
|
+
# menu bar restart and is blind to posts driven by the autopilot/agent, so
|
|
174
|
+
# the title used to fall back to plain "S4L" mid-drain. These track a drain
|
|
175
|
+
# by the plan's posted count climbing, with hysteresis across the multi-
|
|
176
|
+
# second gaps between individual posts so the indicator never blinks off.
|
|
177
|
+
self._posting_label = None
|
|
178
|
+
self._drain_baseline = None # posted count just before this drain started
|
|
179
|
+
self._drain_last_posted = None
|
|
180
|
+
self._drain_last_change = 0.0
|
|
171
181
|
# Reliable self-check of our own Accessibility (TCC) grant — this is the
|
|
172
182
|
# faithful reading (our launchd process identity, not a parent's). Logged
|
|
173
183
|
# so menubar.err.log records whether keystroke posting will work.
|
|
@@ -395,15 +405,70 @@ class S4LMenuBar(rumps.App):
|
|
|
395
405
|
# animates the title with the label and stops itself when activity clears.
|
|
396
406
|
# Both run on the main thread (rumps timers).
|
|
397
407
|
def _poll_activity(self, _):
|
|
408
|
+
# Refresh the durable plan-based posting label (cheap: one small file read)
|
|
409
|
+
# so the title can show steady posting progress even when the server's
|
|
410
|
+
# per-post activity.json is momentarily empty between posts.
|
|
411
|
+
self._posting_label = self._compute_posting_label()
|
|
398
412
|
act = st.read_activity()
|
|
399
|
-
|
|
413
|
+
has_label = bool((act and act.get("label")) or self._posting_label)
|
|
414
|
+
if has_label and self._spinner is None:
|
|
400
415
|
self._spin_i = 0
|
|
401
416
|
self._spinner = rumps.Timer(self._spin, 0.12)
|
|
402
417
|
self._spinner.start()
|
|
403
418
|
|
|
419
|
+
def _compute_posting_label(self):
|
|
420
|
+
"""Posting progress from the durable review-queue plan, with hysteresis.
|
|
421
|
+
|
|
422
|
+
A drain is detected by the plan's posted count INCREASING (or the server's
|
|
423
|
+
activity.json reporting a post in flight) — never by the raw unposted
|
|
424
|
+
backlog, which sits non-zero for drafts merely awaiting review. The label
|
|
425
|
+
is held for a grace window after the last post so the indicator doesn't
|
|
426
|
+
blink back to "S4L" during the multi-second gaps between posts. Survives a
|
|
427
|
+
menu bar restart and reflects posts driven by the autopilot/agent, not just
|
|
428
|
+
this process's own approval queue."""
|
|
429
|
+
now = time.time()
|
|
430
|
+
try:
|
|
431
|
+
posted = st.review_queue_posted_count()
|
|
432
|
+
except Exception:
|
|
433
|
+
posted = None
|
|
434
|
+
act = st.read_activity()
|
|
435
|
+
act_posting = bool(act and "posting" in str(act.get("label") or ""))
|
|
436
|
+
if posted is None:
|
|
437
|
+
posted = self._drain_last_posted # ride through a transient read miss
|
|
438
|
+
if posted is None:
|
|
439
|
+
return None
|
|
440
|
+
if self._drain_last_posted is None:
|
|
441
|
+
self._drain_last_posted = posted
|
|
442
|
+
advanced = posted > self._drain_last_posted
|
|
443
|
+
if advanced or act_posting:
|
|
444
|
+
if self._drain_baseline is None:
|
|
445
|
+
# New drain: baseline is the count just BEFORE its first post.
|
|
446
|
+
self._drain_baseline = self._drain_last_posted
|
|
447
|
+
self._drain_last_change = now
|
|
448
|
+
if advanced:
|
|
449
|
+
self._drain_last_posted = posted
|
|
450
|
+
if self._drain_baseline is None:
|
|
451
|
+
return None
|
|
452
|
+
# Drain is over once the server is idle AND no new post landed for a grace
|
|
453
|
+
# window (covers the long gaps between the last few slow posts).
|
|
454
|
+
if not act_posting and now - self._drain_last_change > 45.0:
|
|
455
|
+
self._drain_baseline = None
|
|
456
|
+
return None
|
|
457
|
+
sent = max(0, posted - self._drain_baseline)
|
|
458
|
+
return f"posting · {sent} sent"
|
|
459
|
+
|
|
404
460
|
def _spin(self, _):
|
|
405
461
|
act = st.read_activity()
|
|
406
462
|
label = act.get("label") if act else None
|
|
463
|
+
act_state = act.get("state") if act else None
|
|
464
|
+
# For POSTING, prefer the menu bar's durable cumulative label over the
|
|
465
|
+
# server's per-call "1/1" so the count climbs smoothly and the indicator
|
|
466
|
+
# holds through the gaps between posts. Non-posting activity (scanning /
|
|
467
|
+
# drafting) keeps its own server label.
|
|
468
|
+
if self._posting_label and (
|
|
469
|
+
not label or act_state == "posting" or "posting" in str(label)
|
|
470
|
+
):
|
|
471
|
+
label = self._posting_label
|
|
407
472
|
if label:
|
|
408
473
|
# The update arrow must stay visible even while a tool runs, so the
|
|
409
474
|
# "update available" signal is never masked by activity. _tick skips the
|
package/mcp/menubar/s4l_state.py
CHANGED
|
@@ -349,6 +349,26 @@ def read_plan(plan_path):
|
|
|
349
349
|
return None
|
|
350
350
|
|
|
351
351
|
|
|
352
|
+
def review_queue_posted_count():
|
|
353
|
+
"""Posts that have LANDED in the review-queue plan — the durable, cross-process
|
|
354
|
+
truth. Independent of the menu bar's in-memory burst queue (which dies on a
|
|
355
|
+
restart) and of WHICH process is posting (the menu bar worker, the autopilot,
|
|
356
|
+
or a host agent draining via post_drafts). Returns the posted count, or None
|
|
357
|
+
when the plan can't be read. Drives the menu-bar posting indicator so progress
|
|
358
|
+
stays visible regardless of how the drain is driven."""
|
|
359
|
+
plan_path = None
|
|
360
|
+
req = read_review_request()
|
|
361
|
+
if req:
|
|
362
|
+
plan_path = req.get("plan_path")
|
|
363
|
+
if not plan_path:
|
|
364
|
+
plan_path = "/tmp/twitter_cycle_plan_review-queue.json"
|
|
365
|
+
plan = read_plan(plan_path)
|
|
366
|
+
cands = (plan or {}).get("candidates")
|
|
367
|
+
if not cands:
|
|
368
|
+
return None
|
|
369
|
+
return sum(1 for c in cands if c.get("posted") is True)
|
|
370
|
+
|
|
371
|
+
|
|
352
372
|
def review_drafts(plan):
|
|
353
373
|
"""Flatten a plan into the card model: only UNDECIDED candidates. A card that's
|
|
354
374
|
posted, terminal (rejected/dead), or already approved is a settled decision and
|
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.114",
|
|
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