social-autoposter 1.6.149 → 1.6.151
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 +68 -8
- package/mcp/dist/panel.html +23 -12
- package/mcp/dist/version.json +2 -2
- package/mcp/manifest.json +1 -1
- package/mcp/menubar/s4l_menubar.py +27 -66
- package/mcp/package.json +1 -1
- package/package.json +1 -1
package/mcp/dist/index.js
CHANGED
|
@@ -1037,9 +1037,9 @@ tool("engagement_mode", {
|
|
|
1037
1037
|
"toggle.",
|
|
1038
1038
|
inputSchema: {
|
|
1039
1039
|
action: z
|
|
1040
|
-
.enum(["get", "set"])
|
|
1040
|
+
.enum(["get", "set", "toggle"])
|
|
1041
1041
|
.optional()
|
|
1042
|
-
.describe("get = read current mode + persona status. set = record the user's chosen mode."),
|
|
1042
|
+
.describe("get = read current mode + persona status. set = record the user's chosen mode (provisions the persona). toggle = lightweight flip promotion<->personal_brand (mode.json only, no persona work) — the dashboard/menu-bar quick toggle."),
|
|
1043
1043
|
mode: z
|
|
1044
1044
|
.enum(["personal_brand", "promotion"])
|
|
1045
1045
|
.optional()
|
|
@@ -1070,6 +1070,20 @@ tool("engagement_mode", {
|
|
|
1070
1070
|
const persona = findPersonaProject();
|
|
1071
1071
|
return jsonContent({ mode, persona: persona ? persona.name : null });
|
|
1072
1072
|
}
|
|
1073
|
+
// Lightweight flip (the dashboard/menu-bar quick toggle): just rewrite
|
|
1074
|
+
// mode.json via saps_mode.py — NO persona provisioning. Mirrors the menu
|
|
1075
|
+
// bar's pure-local _toggle_mode so flipping from either surface is cheap.
|
|
1076
|
+
if (action === "toggle") {
|
|
1077
|
+
const cur = await runPython("scripts/saps_mode.py", ["get"], { timeoutMs: 15_000 });
|
|
1078
|
+
const now = (cur.stdout || "").trim() || "promotion";
|
|
1079
|
+
const next = now === "personal_brand" ? "promotion" : "personal_brand";
|
|
1080
|
+
const res = await runPython("scripts/saps_mode.py", ["set", next], { timeoutMs: 15_000 });
|
|
1081
|
+
if (res.code !== 0) {
|
|
1082
|
+
const tail = (res.stderr || res.stdout).trim().split("\n").slice(-1)[0] || "unknown error";
|
|
1083
|
+
return textContent(`Could not switch mode: ${tail}`);
|
|
1084
|
+
}
|
|
1085
|
+
return jsonContent({ mode: next });
|
|
1086
|
+
}
|
|
1073
1087
|
const mode = args.mode;
|
|
1074
1088
|
if (mode !== "personal_brand" && mode !== "promotion") {
|
|
1075
1089
|
return textContent("Ask the user which they want, then call again with mode:'personal_brand' (organic brand " +
|
|
@@ -1435,6 +1449,7 @@ tool("project_config", {
|
|
|
1435
1449
|
mcp_version: ver.installed,
|
|
1436
1450
|
latest_version: ver.latest,
|
|
1437
1451
|
update_available: ver.update_available,
|
|
1452
|
+
mode: currentMode(),
|
|
1438
1453
|
update_hint: ver.update_available
|
|
1439
1454
|
? `A newer version (${ver.latest}) is available — you're on ${ver.installed}. ` +
|
|
1440
1455
|
`Tell the user and offer to run the \`runtime\` tool with action:'update' ` +
|
|
@@ -2062,7 +2077,7 @@ async function autopilotLoaded() {
|
|
|
2062
2077
|
// fires every minute, claims ONE job, runs the pipeline's own prompt as its
|
|
2063
2078
|
// Claude turn, writes the result back, and stops.
|
|
2064
2079
|
// ===========================================================================
|
|
2065
|
-
const QUEUE_WORKER_PROMPT_VERSION =
|
|
2080
|
+
const QUEUE_WORKER_PROMPT_VERSION = 6; // v6: the ~90s kill is a host INACTIVITY watchdog (resets on each tool call), not a fixed cap. The worker was drafting all candidates in ONE silent turn (its HARD RULES even forbade log_draft.py), starving the clock -> killed mid-draft. v6 requires incremental draft-then-log per candidate and allows the log_draft.py persist commands, so per-candidate tool calls keep the session alive and the full set drains.
|
|
2066
2081
|
const QUEUE_WORKER_PROMPT_MARKER = "saps_queue_worker_prompt_version";
|
|
2067
2082
|
// One spec per worker task. queueType MUST match scripts/claude_job.py TAG_TO_TYPE.
|
|
2068
2083
|
const QUEUE_WORKERS = [
|
|
@@ -2165,6 +2180,13 @@ function queueWorkerBody(spec) {
|
|
|
2165
2180
|
const job = path.join(repoDir(), "scripts", "claude_job.py");
|
|
2166
2181
|
const sd = sapsStateDir();
|
|
2167
2182
|
const outDir = queueDir();
|
|
2183
|
+
// The Phase-2b "twitter-prep" worker drafts replies for several candidates. The
|
|
2184
|
+
// host kills an unattended scheduled session ~90s after its LAST tool call (an
|
|
2185
|
+
// inactivity watchdog). Drafting everything in one silent turn starves that clock
|
|
2186
|
+
// and the session is killed mid-draft having submitted nothing. So it MUST work
|
|
2187
|
+
// incrementally — draft + run log_draft.py per candidate — which persists drafts
|
|
2188
|
+
// (the pipeline already wants this) AND keeps the activity clock alive.
|
|
2189
|
+
const isDraft = spec.queueType === "twitter-prep";
|
|
2168
2190
|
return [
|
|
2169
2191
|
`You are the S4L "${spec.human}" queue worker. Run ONE iteration, then STOP.`,
|
|
2170
2192
|
``,
|
|
@@ -2174,6 +2196,23 @@ function queueWorkerBody(spec) {
|
|
|
2174
2196
|
`You do this with Bash and Write, and NOTHING else. This run is unattended — ` +
|
|
2175
2197
|
`reaching for any other tool, or trying to "investigate", STALLS it forever.`,
|
|
2176
2198
|
``,
|
|
2199
|
+
...(isDraft
|
|
2200
|
+
? [
|
|
2201
|
+
`PACING — CRITICAL: this unattended session is terminated ~90 seconds after ` +
|
|
2202
|
+
`your LAST tool call (a host inactivity timeout). The prompt below asks ` +
|
|
2203
|
+
`you to draft replies for SEVERAL candidates. Do NOT draft them all ` +
|
|
2204
|
+
`silently in one turn — that emits no tool calls, the clock expires, and ` +
|
|
2205
|
+
`you are KILLED mid-draft having submitted NOTHING. Work ONE candidate at ` +
|
|
2206
|
+
`a time: draft its reply, then IMMEDIATELY run that candidate's ` +
|
|
2207
|
+
`log_draft.py command exactly as the prompt's persist step (3a) specifies ` +
|
|
2208
|
+
`(a quick Bash call), THEN move to the next. Those per-candidate Bash ` +
|
|
2209
|
+
`calls are what keep the session alive. Begin the first candidate ` +
|
|
2210
|
+
`promptly — do NOT spend a long silent stretch analysing before your ` +
|
|
2211
|
+
`first tool call. Only after EVERY candidate is drafted and logged do you ` +
|
|
2212
|
+
`do step 3.`,
|
|
2213
|
+
``,
|
|
2214
|
+
]
|
|
2215
|
+
: []),
|
|
2177
2216
|
`Steps:`,
|
|
2178
2217
|
`1. Claim the next job. Run this EXACT Bash command:`,
|
|
2179
2218
|
` ${py} ${job} next --type ${spec.queueType} --prompt-file --state-dir ${sd}`,
|
|
@@ -2196,11 +2235,18 @@ function queueWorkerBody(spec) {
|
|
|
2196
2235
|
`4. Report in ONE short line what you did, then STOP. Do NOT claim another job, ` +
|
|
2197
2236
|
`do NOT loop, do NOT read other files, do NOT call any other tool.`,
|
|
2198
2237
|
``,
|
|
2199
|
-
|
|
2200
|
-
`
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2238
|
+
isDraft
|
|
2239
|
+
? `HARD RULES: use ONLY the Bash tool (to run claude_job.py AND the per-candidate ` +
|
|
2240
|
+
`log_draft.py persist commands the prompt tells you to run), the Read tool (the ` +
|
|
2241
|
+
`prompt/schema sidecar + the SKILL/config files the prompt names), and the Write ` +
|
|
2242
|
+
`tool (the result file). NEVER post, reply, edit a draft, open a browser, or run ` +
|
|
2243
|
+
`any command the prompt does not explicitly give you. An empty queue is the ` +
|
|
2244
|
+
`NORMAL, expected case most minutes — it is success, not a problem to debug.`
|
|
2245
|
+
: `HARD RULES: ONLY the Bash tool (to run claude_job.py), the Read tool (to read ` +
|
|
2246
|
+
`the prompt/schema sidecar files), and the Write tool (to write the result ` +
|
|
2247
|
+
`file). NEVER run any other shell command. NEVER edit, post, or touch anything ` +
|
|
2248
|
+
`else. An empty queue is the NORMAL, expected case most minutes — it is success, ` +
|
|
2249
|
+
`not a problem to debug.`,
|
|
2204
2250
|
].join("\n");
|
|
2205
2251
|
}
|
|
2206
2252
|
// Full canonical SKILL.md (frontmatter + body + version marker) the MCP writes
|
|
@@ -2753,6 +2799,8 @@ async function buildSnapshot() {
|
|
|
2753
2799
|
runtime_ready: rtReady,
|
|
2754
2800
|
runtime_provisioning: isProvisioning(),
|
|
2755
2801
|
setup_complete: setupComplete,
|
|
2802
|
+
// Engagement mode for display in BOTH surfaces (single source: mode.json).
|
|
2803
|
+
mode: currentMode(),
|
|
2756
2804
|
onboarding: onboardingLive,
|
|
2757
2805
|
};
|
|
2758
2806
|
// Persist this snapshot so the menu bar can answer "set up?" the SAME way when
|
|
@@ -2915,6 +2963,18 @@ function modeChosen() {
|
|
|
2915
2963
|
return false;
|
|
2916
2964
|
}
|
|
2917
2965
|
}
|
|
2966
|
+
// The current engagement mode ('promotion' | 'personal_brand'), surfaced in the
|
|
2967
|
+
// snapshot so the dashboard AND menu bar read it from ONE place (mode.json, the
|
|
2968
|
+
// same file saps_mode.py writes). Defaults to promotion when unset.
|
|
2969
|
+
function currentMode() {
|
|
2970
|
+
try {
|
|
2971
|
+
const m = (JSON.parse(fs.readFileSync(path.join(sapsStateDir(), "mode.json"), "utf-8")).mode || "").trim();
|
|
2972
|
+
return m === "personal_brand" ? "personal_brand" : "promotion";
|
|
2973
|
+
}
|
|
2974
|
+
catch {
|
|
2975
|
+
return "promotion";
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2918
2978
|
// ---- Cross-instance "posting active" flag ----------------------------------
|
|
2919
2979
|
// posting-active.json in the shared state dir is the CROSS-MCP-INSTANCE version
|
|
2920
2980
|
// of the in-process `postingActive` flag. The autopilot scan and the post
|