@m13v/s4l 1.7.1-rc.7 → 1.7.1-rc.8
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 +56 -14
- 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.7.1-rc.
|
|
5
|
+
"version": "1.7.1-rc.8",
|
|
6
6
|
"description": "Draft, review, approve, and autopilot X/Twitter posts.",
|
|
7
7
|
"long_description": "## **⚠️ The disclaimer above is generic Claude boilerplate.** Anthropic shows the same warning on every plugin regardless of what it does; any plugin has the same level of access as any app you download from the internet.\n\nS4L 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 with CMD+Q, reopen Claude, paste into a new chat.\n\nWhat happens next:\n\n* About every 5 minutes S4L scans X for posts that match your topics and drafts replies in your voice.\n* Drafts show up as review cards, usually the first within a few minutes. Nothing is posted automatically; you approve each one.\n* Posting autopilot stays off until you explicitly turn it on.",
|
|
8
8
|
"author": {
|
|
@@ -132,20 +132,24 @@ except Exception as _import_err:
|
|
|
132
132
|
import s4l_state as st # noqa: E402
|
|
133
133
|
|
|
134
134
|
# AppKit is available in the owned venv (PyObjC is a rumps dependency). We use it
|
|
135
|
-
#
|
|
136
|
-
#
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
# menu bar — the alert still shows, just possibly not front-most.
|
|
135
|
+
# to pull the accessory (LSUIElement) app to the front AND to force the alert
|
|
136
|
+
# window itself above whatever else is on screen. Guarded so a missing AppKit
|
|
137
|
+
# never breaks the menu bar — the alert still shows, just possibly not
|
|
138
|
+
# front-most.
|
|
140
139
|
try:
|
|
141
|
-
from AppKit import NSApplication # noqa: E402
|
|
140
|
+
from AppKit import NSApplication, NSAlert, NSFloatingWindowLevel # noqa: E402
|
|
142
141
|
except Exception:
|
|
143
142
|
NSApplication = None
|
|
143
|
+
NSAlert = None
|
|
144
|
+
NSFloatingWindowLevel = None
|
|
144
145
|
|
|
145
146
|
|
|
146
147
|
def _activate_front():
|
|
147
|
-
"""Bring this accessory app to the front so the next
|
|
148
|
-
|
|
148
|
+
"""Bring this accessory app to the front so the next alert opens on top of
|
|
149
|
+
whatever was frontmost, instead of behind it. Best-effort; kept as a
|
|
150
|
+
belt-and-suspenders call alongside _show_alert's own window-level fix
|
|
151
|
+
below, since activation also affects which app is "key" for keyboard
|
|
152
|
+
input on the alert's buttons."""
|
|
149
153
|
try:
|
|
150
154
|
if NSApplication is not None:
|
|
151
155
|
NSApplication.sharedApplication().activateIgnoringOtherApps_(True)
|
|
@@ -153,6 +157,44 @@ def _activate_front():
|
|
|
153
157
|
pass
|
|
154
158
|
|
|
155
159
|
|
|
160
|
+
def _show_alert(title=None, message='', ok=None, cancel=None, other=None):
|
|
161
|
+
"""Like rumps.alert(), but actually shows up on top.
|
|
162
|
+
|
|
163
|
+
rumps.alert() builds an NSAlert and immediately calls runModal() with no
|
|
164
|
+
window-level or ordering call at all — it just assumes the app is already
|
|
165
|
+
active. activateIgnoringOtherApps_ (_activate_front, above) is async, so
|
|
166
|
+
it can lose the race against runModal()'s own (assumed-active) window
|
|
167
|
+
presentation: the alert window is created at the default level and never
|
|
168
|
+
explicitly raised, so when this accessory app ISN'T active (e.g. Claude
|
|
169
|
+
Desktop is frontmost), it renders BEHIND Claude's window instead of on
|
|
170
|
+
top of it. Confirmed recurring 2026-07-07 (uninstall modal hidden behind
|
|
171
|
+
Claude Desktop) despite the activation call landing 2026-06-30.
|
|
172
|
+
|
|
173
|
+
Fix: build the NSAlert ourselves (same call rumps.alert uses) and force
|
|
174
|
+
its window to NSFloatingWindowLevel + orderFrontRegardless() BEFORE
|
|
175
|
+
runModal(), mirroring the pattern s4l_card.py's review panel already uses
|
|
176
|
+
successfully (see _ReviewPanel._build: setLevel_(NSFloatingWindowLevel) +
|
|
177
|
+
orderFrontRegardless()). Floating level sits above normal app windows in
|
|
178
|
+
the window server's global z-order regardless of app-activation state,
|
|
179
|
+
so it doesn't depend on the activation race resolving in time."""
|
|
180
|
+
_activate_front()
|
|
181
|
+
if NSAlert is None:
|
|
182
|
+
# AppKit unavailable in this venv: fall back to rumps' plain alert.
|
|
183
|
+
return rumps.alert(title=title, message=message, ok=ok, cancel=cancel, other=other)
|
|
184
|
+
msg = str(message).replace('%', '%%')
|
|
185
|
+
if not isinstance(cancel, str):
|
|
186
|
+
cancel = 'Cancel' if cancel else None
|
|
187
|
+
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
|
|
188
|
+
title, ok, cancel, other, msg)
|
|
189
|
+
alert.setAlertStyle_(0) # same style rumps.alert() uses
|
|
190
|
+
try:
|
|
191
|
+
alert.window().setLevel_(NSFloatingWindowLevel)
|
|
192
|
+
alert.window().orderFrontRegardless()
|
|
193
|
+
except Exception:
|
|
194
|
+
pass
|
|
195
|
+
return alert.runModal()
|
|
196
|
+
|
|
197
|
+
|
|
156
198
|
CLAUDE_APP = "Claude"
|
|
157
199
|
|
|
158
200
|
|
|
@@ -595,7 +637,7 @@ class S4LMenuBar(rumps.App):
|
|
|
595
637
|
msg = "Click OK, then open Claude and ask it to " + action_desc + "."
|
|
596
638
|
try:
|
|
597
639
|
_activate_front()
|
|
598
|
-
|
|
640
|
+
_show_alert(title=title, message=msg, ok="OK")
|
|
599
641
|
except Exception:
|
|
600
642
|
self._notify("S4L · prompt copied" if copied else "S4L",
|
|
601
643
|
"Paste the prompt into Claude (Cmd+V) and press Enter.")
|
|
@@ -666,7 +708,7 @@ class S4LMenuBar(rumps.App):
|
|
|
666
708
|
also run the registry self-heal, which additionally repairs the
|
|
667
709
|
account-switch orphan case, so this one click covers both known causes."""
|
|
668
710
|
_activate_front()
|
|
669
|
-
choice =
|
|
711
|
+
choice = _show_alert(
|
|
670
712
|
title="Restart Claude Desktop?",
|
|
671
713
|
message=(
|
|
672
714
|
"Claude’s scheduler stopped running the draft tasks (a known "
|
|
@@ -994,7 +1036,7 @@ class S4LMenuBar(rumps.App):
|
|
|
994
1036
|
# ok=1 (plugin reset, keeps X login + browser layer), other=-1 (deep wipe),
|
|
995
1037
|
# cancel=0. See rumps.alert: default=1, alternate=0, other=-1.
|
|
996
1038
|
_activate_front()
|
|
997
|
-
choice =
|
|
1039
|
+
choice = _show_alert(
|
|
998
1040
|
title="Uninstall S4L?",
|
|
999
1041
|
message=(
|
|
1000
1042
|
"This quits Claude Desktop, removes the S4L extension + its "
|
|
@@ -1037,7 +1079,7 @@ class S4LMenuBar(rumps.App):
|
|
|
1037
1079
|
def _alert(self, title, message):
|
|
1038
1080
|
try:
|
|
1039
1081
|
_activate_front()
|
|
1040
|
-
|
|
1082
|
+
_show_alert(title=title, message=message, ok="OK")
|
|
1041
1083
|
except Exception:
|
|
1042
1084
|
pass
|
|
1043
1085
|
|
|
@@ -1072,7 +1114,7 @@ class S4LMenuBar(rumps.App):
|
|
|
1072
1114
|
modal FIRST that Claude Desktop will restart, since the app window will
|
|
1073
1115
|
close and reopen under them."""
|
|
1074
1116
|
_activate_front()
|
|
1075
|
-
choice =
|
|
1117
|
+
choice = _show_alert(
|
|
1076
1118
|
title="Quit the S4L autoposter?",
|
|
1077
1119
|
message=(
|
|
1078
1120
|
"Quitting stops the autoposter completely: the draft + query "
|
|
@@ -1758,7 +1800,7 @@ class S4LMenuBar(rumps.App):
|
|
|
1758
1800
|
self._cwd_healed = True
|
|
1759
1801
|
return
|
|
1760
1802
|
_activate_front()
|
|
1761
|
-
choice =
|
|
1803
|
+
choice = _show_alert(
|
|
1762
1804
|
title="Tidy the S4L background tasks?",
|
|
1763
1805
|
message=(
|
|
1764
1806
|
"S4L can tidy its background tasks: merge the old draft + query "
|
package/mcp/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m13v/s4l-mcp",
|
|
3
|
-
"version": "1.7.1-rc.
|
|
3
|
+
"version": "1.7.1-rc.8",
|
|
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