social-autoposter 1.6.134 → 1.6.136
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 +47 -50
- package/mcp/dist/version.json +2 -2
- package/mcp/manifest.json +1 -1
- package/mcp/menubar/s4l_menubar.py +100 -115
- package/mcp/package.json +1 -1
- package/package.json +1 -1
package/mcp/dist/index.js
CHANGED
|
@@ -2607,66 +2607,63 @@ async function ensureMemorySnapshotInstalled() {
|
|
|
2607
2607
|
return { ok: false, detail: e?.message || String(e) };
|
|
2608
2608
|
}
|
|
2609
2609
|
}
|
|
2610
|
-
//
|
|
2611
|
-
//
|
|
2612
|
-
//
|
|
2613
|
-
//
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
if (k.startsWith("dxt:allowlistLastUpdated:") && typeof v === "string" && v > bestTs) {
|
|
2622
|
-
bestTs = v;
|
|
2623
|
-
bestId = k.slice("dxt:allowlistLastUpdated:".length);
|
|
2624
|
-
}
|
|
2625
|
-
}
|
|
2626
|
-
return bestId;
|
|
2627
|
-
}
|
|
2628
|
-
catch {
|
|
2629
|
-
return null;
|
|
2630
|
-
}
|
|
2631
|
-
}
|
|
2632
|
-
// The ACTUAL schedule state for the current account (read, not inferred from
|
|
2633
|
-
// firing): 'missing' | 'disabled' | 'ok' | 'unknown'. Drives the dashboard's
|
|
2634
|
-
// "Set up draft schedule" button. Mirrors s4l_menubar.py::_schedule_state.
|
|
2610
|
+
// Is the draft schedule registered AND running for the LIVE account?
|
|
2611
|
+
// 'ok' — worker tasks present+enabled and FIRING (lastRunAt within
|
|
2612
|
+
// SCHEDULE_FIRING_MS) — the host is actively running them.
|
|
2613
|
+
// 'disabled' — present but a worker task is disabled.
|
|
2614
|
+
// 'missing' — not firing anywhere (orphaned / not registered for the live
|
|
2615
|
+
// account) -> dashboard offers "Set up draft schedule".
|
|
2616
|
+
// The live account is identified by the registry the host is actually FIRING
|
|
2617
|
+
// (freshest lastRunAt — only the active account's scheduler advances it), NOT a
|
|
2618
|
+
// session id (which churns on every Claude restart and mis-read "missing" while
|
|
2619
|
+
// the tasks were firing). Mirrors s4l_menubar.py::_schedule_state.
|
|
2620
|
+
const SCHEDULE_FIRING_MS = 420_000; // 7 min; tolerates throttle + restart gaps
|
|
2635
2621
|
function scheduleState() {
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
let
|
|
2622
|
+
let newestMs = null;
|
|
2623
|
+
let newestEnabled = false;
|
|
2624
|
+
let anyPresent = false;
|
|
2625
|
+
let anyEnabled = false;
|
|
2640
2626
|
try {
|
|
2641
2627
|
const base = path.join(os.homedir(), "Library", "Application Support", "Claude", "claude-code-sessions");
|
|
2642
2628
|
for (const ws of fs.readdirSync(base)) {
|
|
2643
|
-
const
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2629
|
+
for (const inner of fs.readdirSync(path.join(base, ws))) {
|
|
2630
|
+
const reg = path.join(base, ws, inner, "scheduled-tasks.json");
|
|
2631
|
+
let d;
|
|
2632
|
+
try {
|
|
2633
|
+
d = JSON.parse(fs.readFileSync(reg, "utf-8"));
|
|
2634
|
+
}
|
|
2635
|
+
catch {
|
|
2636
|
+
continue;
|
|
2637
|
+
}
|
|
2638
|
+
const byId = {};
|
|
2639
|
+
for (const t of d.scheduledTasks || [])
|
|
2640
|
+
byId[t.id] = t;
|
|
2641
|
+
const recs = QUEUE_WORKERS.map((s) => byId[s.taskId]);
|
|
2642
|
+
if (recs.some((r) => !r))
|
|
2643
|
+
continue;
|
|
2644
|
+
anyPresent = true;
|
|
2645
|
+
const enabled = recs.every((r) => r.enabled);
|
|
2646
|
+
anyEnabled = anyEnabled || enabled;
|
|
2647
|
+
const epochs = recs
|
|
2648
|
+
.map((r) => Date.parse(r.lastRunAt || ""))
|
|
2649
|
+
.filter((n) => !Number.isNaN(n));
|
|
2650
|
+
const e = epochs.length ? Math.max(...epochs) : null;
|
|
2651
|
+
if (e !== null && (newestMs === null || e > newestMs)) {
|
|
2652
|
+
newestMs = e;
|
|
2653
|
+
newestEnabled = enabled;
|
|
2654
|
+
}
|
|
2647
2655
|
}
|
|
2648
2656
|
}
|
|
2649
2657
|
}
|
|
2650
2658
|
catch {
|
|
2651
|
-
/* base missing */
|
|
2652
|
-
}
|
|
2653
|
-
if (!regPath)
|
|
2654
|
-
return "missing";
|
|
2655
|
-
try {
|
|
2656
|
-
const d = JSON.parse(fs.readFileSync(regPath, "utf-8"));
|
|
2657
|
-
const byId = {};
|
|
2658
|
-
for (const t of d.scheduledTasks || [])
|
|
2659
|
-
byId[t.id] = t;
|
|
2660
|
-
const recs = QUEUE_WORKERS.map((s) => byId[s.taskId]);
|
|
2661
|
-
if (recs.some((r) => !r))
|
|
2662
|
-
return "missing";
|
|
2663
|
-
if (recs.some((r) => !r.enabled))
|
|
2664
|
-
return "disabled";
|
|
2665
|
-
return "ok";
|
|
2659
|
+
/* base missing -> treat as missing */
|
|
2666
2660
|
}
|
|
2667
|
-
|
|
2668
|
-
return "
|
|
2661
|
+
if (newestMs !== null && Date.now() - newestMs <= SCHEDULE_FIRING_MS) {
|
|
2662
|
+
return newestEnabled ? "ok" : "disabled";
|
|
2669
2663
|
}
|
|
2664
|
+
if (anyPresent && !anyEnabled)
|
|
2665
|
+
return "disabled";
|
|
2666
|
+
return "missing";
|
|
2670
2667
|
}
|
|
2671
2668
|
// Assemble everything the panel needs in one shot (projects + X + autopilot +
|
|
2672
2669
|
// version). Resilient: any probe that throws degrades to a safe default rather
|
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.136",
|
|
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": {
|
|
@@ -133,6 +133,12 @@ REARM_PROMPT = (
|
|
|
133
133
|
# scheduler's per-minute cadence + a slow claim.
|
|
134
134
|
AUTOPILOT_STALL_SECONDS = 180
|
|
135
135
|
|
|
136
|
+
# A worker task whose lastRunAt is within this many seconds is "firing" — the host
|
|
137
|
+
# scheduler runs them every minute, so a fresh stamp means the live account's
|
|
138
|
+
# schedule is active. 7 min tolerates host throttling + a restart gap without
|
|
139
|
+
# false "not scheduled". Used by _schedule_state.
|
|
140
|
+
FIRING_WINDOW = 420
|
|
141
|
+
|
|
136
142
|
|
|
137
143
|
def _glyph(status):
|
|
138
144
|
return GLYPH.get(status, "·")
|
|
@@ -236,9 +242,14 @@ class S4LMenuBar(rumps.App):
|
|
|
236
242
|
# indicator + the "Please update now" menu item.
|
|
237
243
|
self._update_available = False
|
|
238
244
|
self._latest_version = None
|
|
239
|
-
|
|
245
|
+
# Poll every 15 min, NOT every 60s. _check_update hits GitHub's
|
|
246
|
+
# releases/latest UNAUTHENTICATED (60 req/hr per IP); a 60s poll = 60/hr,
|
|
247
|
+
# right at the cap, so it got 403-rate-limited and silently stopped
|
|
248
|
+
# detecting updates (the "update available" badge never showed). 15 min =
|
|
249
|
+
# 4/hr leaves ample headroom; updates aren't urgent enough to poll faster.
|
|
250
|
+
self._upd_timer = rumps.Timer(self._check_update, 900)
|
|
240
251
|
self._upd_timer.start()
|
|
241
|
-
self._check_update(None)
|
|
252
|
+
self._check_update(None) # one check at launch (badge appears ~immediately)
|
|
242
253
|
# One-shot self-heal: if the autopilot scheduled tasks are running in the
|
|
243
254
|
# wrong folder (or the deprecated single autopilot task still exists),
|
|
244
255
|
# relocate them to ~/.s4l-worker so their once-a-minute runs stop polluting
|
|
@@ -351,76 +362,80 @@ class S4LMenuBar(rumps.App):
|
|
|
351
362
|
do NOT auto-type (focus/timing flaky) and do NOT write the registry directly
|
|
352
363
|
(can't reliably target a just-switched-into account)."""
|
|
353
364
|
copied = self._copy_to_clipboard(REARM_PROMPT)
|
|
365
|
+
# Show the modal BEFORE opening Claude. If we raise Claude first, the NSAlert
|
|
366
|
+
# renders BEHIND it (invisible) while still blocking the main thread, which
|
|
367
|
+
# is what made the menu look frozen/greyed with no visible dialog. Showing
|
|
368
|
+
# it first keeps it frontmost; we open Claude after the user dismisses it.
|
|
369
|
+
# (_notify's osascript notification silently no-ops from this bundle-less
|
|
370
|
+
# launchd process, so a modal is the only reliable feedback.)
|
|
371
|
+
msg = (
|
|
372
|
+
"The setup prompt is copied to your clipboard.\n\nClick OK, then click into "
|
|
373
|
+
"the Claude chat, paste it (Cmd+V), and press Enter — that schedules the "
|
|
374
|
+
"draft tasks for this account."
|
|
375
|
+
if copied
|
|
376
|
+
else "Click OK, then open Claude and ask it to set up the draft schedule for this account."
|
|
377
|
+
)
|
|
378
|
+
try:
|
|
379
|
+
rumps.alert(title="Set up the draft schedule", message=msg, ok="OK")
|
|
380
|
+
except Exception:
|
|
381
|
+
self._notify("S4L · setup prompt copied" if copied else "S4L",
|
|
382
|
+
"Paste the prompt into Claude (Cmd+V) and press Enter.")
|
|
354
383
|
self._open_claude()
|
|
355
|
-
if copied:
|
|
356
|
-
self._notify(
|
|
357
|
-
"S4L · setup prompt copied",
|
|
358
|
-
"Paste it into Claude (⌘V) and press Enter to schedule the draft "
|
|
359
|
-
"tasks for this account.",
|
|
360
|
-
)
|
|
361
|
-
else:
|
|
362
|
-
self._notify(
|
|
363
|
-
"S4L",
|
|
364
|
-
"Open Claude and ask it to set up the draft schedule for this account.",
|
|
365
|
-
)
|
|
366
384
|
|
|
367
|
-
# ---- schedule-state detection
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
385
|
+
# ---- schedule-state detection ----------------------------------------
|
|
386
|
+
# Identify the LIVE account's registry by which one the host is actually
|
|
387
|
+
# FIRING (freshest lastRunAt — only the active account's scheduler advances
|
|
388
|
+
# it), then read THAT registry's enabled state. This is robust across the
|
|
389
|
+
# session-id churn that restarts cause; the old "active session = newest
|
|
390
|
+
# config.json allowlist key" heuristic mis-reported "missing" after a restart
|
|
391
|
+
# even while the tasks were firing.
|
|
392
|
+
@staticmethod
|
|
393
|
+
def _iso_to_epoch(s):
|
|
394
|
+
if not s:
|
|
395
|
+
return None
|
|
374
396
|
try:
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
best_id, best_ts = None, ""
|
|
380
|
-
for k, v in d.items():
|
|
381
|
-
if k.startswith("dxt:allowlistLastUpdated:") and isinstance(v, str):
|
|
382
|
-
if v > best_ts: # ISO-8601 sorts lexically by time
|
|
383
|
-
best_ts, best_id = v, k.split(":", 2)[2]
|
|
384
|
-
return best_id
|
|
397
|
+
import calendar
|
|
398
|
+
return calendar.timegm(
|
|
399
|
+
time.strptime(str(s).strip().rstrip("Z").split(".")[0], "%Y-%m-%dT%H:%M:%S")
|
|
400
|
+
)
|
|
385
401
|
except Exception:
|
|
386
402
|
return None
|
|
387
403
|
|
|
388
|
-
def _active_session_registry_path(self):
|
|
389
|
-
"""Path to the current account's scheduled-tasks.json (claude-code-sessions/
|
|
390
|
-
<workspace>/<active-session>/...). None if the active session can't be found
|
|
391
|
-
or its dir doesn't exist yet (brand-new account -> schedule absent)."""
|
|
392
|
-
sid = self._active_session_id()
|
|
393
|
-
if not sid:
|
|
394
|
-
return None
|
|
395
|
-
hits = glob.glob(os.path.join(
|
|
396
|
-
os.path.expanduser("~"), "Library", "Application Support", "Claude",
|
|
397
|
-
"claude-code-sessions", "*", sid, "scheduled-tasks.json"))
|
|
398
|
-
return hits[0] if hits else None
|
|
399
|
-
|
|
400
404
|
def _schedule_state(self):
|
|
401
|
-
"""
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
'disabled' —
|
|
405
|
-
'
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
405
|
+
"""Is the draft schedule registered AND running for the live account?
|
|
406
|
+
'ok' — worker tasks present+enabled and FIRING (lastRunAt within
|
|
407
|
+
FIRING_WINDOW) — the host is actively running them.
|
|
408
|
+
'disabled' — present but a worker task is disabled.
|
|
409
|
+
'missing' — not firing anywhere (orphaned / not registered for the live
|
|
410
|
+
account) -> offer re-arm.
|
|
411
|
+
The active account is identified by the freshest-firing registry, NOT a
|
|
412
|
+
session id (which churns on restart)."""
|
|
413
|
+
newest_epoch, newest_enabled = None, False
|
|
414
|
+
any_present, any_enabled = False, False
|
|
415
|
+
for f in glob.glob(SCHED_REGISTRY_GLOB):
|
|
416
|
+
try:
|
|
417
|
+
with open(f) as fh:
|
|
418
|
+
d = json.load(fh)
|
|
419
|
+
except Exception:
|
|
420
|
+
continue
|
|
421
|
+
by_id = {t.get("id"): t for t in (d.get("scheduledTasks") or [])}
|
|
422
|
+
recs = [by_id.get(tid) for tid in WORKER_TASK_IDS]
|
|
423
|
+
if any(r is None for r in recs):
|
|
424
|
+
continue
|
|
425
|
+
any_present = True
|
|
426
|
+
enabled = all(r.get("enabled") for r in recs)
|
|
427
|
+
any_enabled = any_enabled or enabled
|
|
428
|
+
epochs = [self._iso_to_epoch(r.get("lastRunAt")) for r in recs]
|
|
429
|
+
e = max([x for x in epochs if x is not None], default=None)
|
|
430
|
+
if e is not None and (newest_epoch is None or e > newest_epoch):
|
|
431
|
+
newest_epoch, newest_enabled = e, enabled
|
|
432
|
+
# Firing recently => the live account's schedule is active and healthy.
|
|
433
|
+
if newest_epoch is not None and (time.time() - newest_epoch) <= FIRING_WINDOW:
|
|
434
|
+
return "ok" if newest_enabled else "disabled"
|
|
435
|
+
# Not firing anywhere. Registered-but-disabled => disabled; else missing.
|
|
436
|
+
if any_present and not any_enabled:
|
|
422
437
|
return "disabled"
|
|
423
|
-
return "
|
|
438
|
+
return "missing"
|
|
424
439
|
|
|
425
440
|
# ---- autopilot liveness (the false-green fix) -------------------------
|
|
426
441
|
def _autopilot_stalled(self):
|
|
@@ -998,21 +1013,17 @@ class S4LMenuBar(rumps.App):
|
|
|
998
1013
|
blocker = (ob or {}).get("current_blocker")
|
|
999
1014
|
blocker_code = (blocker or {}).get("code")
|
|
1000
1015
|
# --- Autopilot health (only meaningful once setup is complete) --------
|
|
1001
|
-
#
|
|
1002
|
-
# (
|
|
1003
|
-
# (
|
|
1004
|
-
#
|
|
1016
|
+
# SINGLE signal: is the draft schedule registered AND firing for the live
|
|
1017
|
+
# account (schedule_state)? 'ok' = the host is running the tasks -> healthy,
|
|
1018
|
+
# NO warning (even if no draft has drained yet — that's just an empty queue
|
|
1019
|
+
# between cycles, not a setup problem). 'missing'/'disabled' = not running
|
|
1020
|
+
# for this account -> show re-arm. We deliberately do NOT drive the menu off
|
|
1021
|
+
# the drain-status latch anymore: it stayed stale after recovery and made a
|
|
1022
|
+
# firing, healthy autopilot look "not set up".
|
|
1005
1023
|
schedule_state = self._schedule_state() if setup_complete else "ok"
|
|
1006
|
-
stalled = setup_complete and self._autopilot_stalled()
|
|
1007
1024
|
self._schedule_state_cache = schedule_state
|
|
1008
|
-
|
|
1009
|
-
#
|
|
1010
|
-
self._stall_reason_info = (
|
|
1011
|
-
self._stall_reason() if (stalled and schedule_state in ("ok", "unknown")) else ("", "")
|
|
1012
|
-
)
|
|
1013
|
-
# Any non-healthy state drops the stale "drafting" spinner so the ⚠ in the
|
|
1014
|
-
# title isn't masked (see _poll_activity).
|
|
1015
|
-
attention = setup_complete and (schedule_state in ("missing", "disabled") or stalled)
|
|
1025
|
+
attention = setup_complete and schedule_state in ("missing", "disabled")
|
|
1026
|
+
# Drop the stale "drafting" spinner while we need attention so the ⚠ shows.
|
|
1016
1027
|
self._stalled = attention
|
|
1017
1028
|
|
|
1018
1029
|
# Spinner owns the title while busy; _spin already keeps the ⬆ visible there.
|
|
@@ -1026,32 +1037,19 @@ class S4LMenuBar(rumps.App):
|
|
|
1026
1037
|
blocker.get("message", "Setup is blocked"),
|
|
1027
1038
|
)
|
|
1028
1039
|
self._last_blocker_code = blocker_code
|
|
1029
|
-
# Notify once per episode
|
|
1040
|
+
# Notify once per episode (the draft schedule isn't running for this account).
|
|
1030
1041
|
if attention and not self._stall_notified:
|
|
1031
|
-
|
|
1032
|
-
if schedule_state == "missing":
|
|
1033
|
-
self._notify(
|
|
1034
|
-
"S4L draft autopilot not scheduled",
|
|
1035
|
-
"No draft tasks are scheduled on this Claude account (switching "
|
|
1036
|
-
"accounts clears them). Open the S4L menu → “Set up draft schedule”.",
|
|
1037
|
-
)
|
|
1038
|
-
elif schedule_state == "disabled":
|
|
1042
|
+
if schedule_state == "disabled":
|
|
1039
1043
|
self._notify(
|
|
1040
1044
|
"S4L draft tasks disabled",
|
|
1041
1045
|
"The draft tasks are scheduled but disabled. Open the S4L menu → "
|
|
1042
1046
|
"“Set up draft schedule” to re-enable.",
|
|
1043
1047
|
)
|
|
1044
|
-
elif kind == "rate_limited":
|
|
1045
|
-
self._notify(
|
|
1046
|
-
"S4L autopilot paused",
|
|
1047
|
-
"Claude usage limit reached" + (f" ({detail})" if detail else "")
|
|
1048
|
-
+ ". Drafting resumes at reset, or sign into an account with quota.",
|
|
1049
|
-
)
|
|
1050
1048
|
else:
|
|
1051
1049
|
self._notify(
|
|
1052
|
-
"S4L autopilot
|
|
1053
|
-
"
|
|
1054
|
-
"S4L menu
|
|
1050
|
+
"S4L draft autopilot not scheduled",
|
|
1051
|
+
"No draft tasks are running on this Claude account (switching "
|
|
1052
|
+
"accounts clears them). Open the S4L menu → “Set up draft schedule”.",
|
|
1055
1053
|
)
|
|
1056
1054
|
self._stall_notified = True
|
|
1057
1055
|
elif not attention:
|
|
@@ -1338,29 +1336,16 @@ class S4LMenuBar(rumps.App):
|
|
|
1338
1336
|
items.append(header)
|
|
1339
1337
|
items.append(rumps.separator)
|
|
1340
1338
|
|
|
1341
|
-
#
|
|
1342
|
-
#
|
|
1343
|
-
#
|
|
1344
|
-
#
|
|
1345
|
-
# aren't draining do we fall to the stall-reason wording (rate-limit/other),
|
|
1346
|
-
# where re-creating the schedule wouldn't help.
|
|
1339
|
+
# Attention = the draft schedule isn't running for THIS account (missing or
|
|
1340
|
+
# disabled). "Set up draft schedule" fixes it via host create_scheduled_task.
|
|
1341
|
+
# When the schedule IS firing (ok), attention is False and nothing shows here
|
|
1342
|
+
# — a firing autopilot reads as healthy even if no draft has drained yet.
|
|
1347
1343
|
if attention:
|
|
1348
|
-
if schedule_state == "
|
|
1349
|
-
items.append(self._label("⚠ Draft tasks aren’t scheduled on this account"))
|
|
1350
|
-
items.append(rumps.MenuItem("Set up draft schedule for this account", callback=self._rearm))
|
|
1351
|
-
elif schedule_state == "disabled":
|
|
1344
|
+
if schedule_state == "disabled":
|
|
1352
1345
|
items.append(self._label("⚠ Draft tasks are scheduled but disabled"))
|
|
1353
|
-
items.append(rumps.MenuItem("Set up draft schedule for this account", callback=self._rearm))
|
|
1354
1346
|
else:
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
items.append(self._label("⚠ Autopilot paused — Claude usage limit"))
|
|
1358
|
-
if detail:
|
|
1359
|
-
items.append(self._label(f" {detail}"))
|
|
1360
|
-
items.append(self._label(" Resumes at reset, or switch Claude account"))
|
|
1361
|
-
else:
|
|
1362
|
-
items.append(self._label("⚠ Scheduled, but no drafts are being produced"))
|
|
1363
|
-
items.append(rumps.MenuItem("Set up draft schedule for this account", callback=self._rearm))
|
|
1347
|
+
items.append(self._label("⚠ Draft tasks aren’t scheduled on this account"))
|
|
1348
|
+
items.append(rumps.MenuItem("Set up draft schedule for this account", callback=self._rearm))
|
|
1364
1349
|
items.append(rumps.separator)
|
|
1365
1350
|
|
|
1366
1351
|
if not runtime_ready:
|
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.136",
|
|
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