@m13v/s4l 1.7.5-rc.7 → 1.7.5-rc.9

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 CHANGED
@@ -2814,9 +2814,9 @@ tool("posting_volume", {
2814
2814
  title: "Read or set posting volume (Aggressive / Steady / Chill)",
2815
2815
  description: "Read or set this install's posting-volume mode, the quality bar that decides how many drafts " +
2816
2816
  "per day the twitter cycle produces. Three modes, shown to users as Aggressive (~100+ posts/day), " +
2817
- "Steady (~30/day), and Chill (~5/day, only the very best candidates); 'default' clears the " +
2818
- "override so the cycle's built-in setting applies. Internally the modes are high|medium|low and " +
2819
- "both spellings are accepted. action:'get' returns the current mode plus per-mode estimated " +
2817
+ "Steady (~30/day, the default every install starts on), and Chill (~5/day, only the very best " +
2818
+ "candidates). Internally the modes are high|medium|low and both spellings are accepted. " +
2819
+ "action:'get' returns the current mode plus per-mode estimated " +
2820
2820
  "posts/day computed from this install's own recent candidate pool (show those numbers with the " +
2821
2821
  "Aggressive/Steady/Chill names when the user is choosing). Use when the user asks to post more, " +
2822
2822
  "post less, slow down, be more aggressive, chill out, raise the quality bar, or change posting " +
@@ -2825,9 +2825,9 @@ tool("posting_volume", {
2825
2825
  inputSchema: {
2826
2826
  action: z.enum(["get", "set"]).default("get").describe("get = read mode + rates; set = change it"),
2827
2827
  mode: z
2828
- .enum(["aggressive", "steady", "chill", "high", "medium", "low", "default"])
2828
+ .enum(["aggressive", "steady", "chill", "high", "medium", "low"])
2829
2829
  .optional()
2830
- .describe("Required for action:'set'. aggressive=high, steady=medium, chill=low; 'default' clears the override."),
2830
+ .describe("Required for action:'set'. aggressive=high, steady=medium, chill=low."),
2831
2831
  },
2832
2832
  }, async (args) => {
2833
2833
  const action = args.action || "get";
@@ -2835,7 +2835,7 @@ tool("posting_volume", {
2835
2835
  const ALIAS = { aggressive: "high", steady: "medium", chill: "low" };
2836
2836
  if (action === "set") {
2837
2837
  if (!args.mode) {
2838
- return jsonContent({ error: "mode is required for action:'set' (aggressive|steady|chill|default)" });
2838
+ return jsonContent({ error: "mode is required for action:'set' (aggressive|steady|chill)" });
2839
2839
  }
2840
2840
  const stored = ALIAS[String(args.mode)] || String(args.mode);
2841
2841
  const r = await runPython("scripts/s4l_posting_mode.py", ["set", stored], {
@@ -70,7 +70,7 @@ Boolean requesting whether a visible border and background is provided by the ho
70
70
  - omitted: host decides border`)});m({method:u("ui/request-display-mode"),params:m({mode:st.describe("The display mode being requested.")})});var Eh=m({mode:st.describe("The display mode that was actually set. May differ from requested if not supported.")}).passthrough(),Dh=U([u("model"),u("app")]).describe("Tool visibility scope - who can access the tool.");m({resourceUri:d().optional(),visibility:x(Dh).optional().describe(`Who can access this tool. Default: ["model", "app"]
71
71
  - "model": Tool visible to and callable by the agent
72
72
  - "app": Tool callable by the app from this server only`),csp:Te().optional(),permissions:Te().optional()});m({mimeTypes:x(d()).optional().describe('Array of supported MIME types for UI resources.\nMust include `"text/html;profile=mcp-app"` for MCP Apps support.')});m({method:u("ui/download-file"),params:m({contents:x(U([Ol,Tl])).describe("Resource contents to download — embedded (inline data) or linked (host fetches). Uses standard MCP resource types.")})});m({method:u("ui/message"),params:m({role:u("user").describe('Message role, currently only "user" is supported.'),content:x(jt).describe("Message content blocks (text, image, etc.).")})});m({method:u("ui/notifications/sandbox-resource-ready"),params:m({html:d().describe("HTML content to load into the inner iframe."),sandbox:d().optional().describe("Optional override for the inner iframe's sandbox attribute."),csp:xo.optional().describe("CSP configuration from resource metadata."),permissions:jo.optional().describe("Sandbox permissions from resource metadata.")})});var Rh=m({method:u("ui/notifications/tool-result"),params:Qn.describe("Standard MCP tool execution result.")}),Cl=m({toolInfo:m({id:kt.optional().describe("JSON-RPC id of the tools/call request."),tool:zo.describe("Tool definition including name, inputSchema, etc.")}).optional().describe("Metadata of the tool call that instantiated this App."),theme:yh.optional().describe("Current color theme preference."),styles:Oh.optional().describe("Style configuration for theming the app."),displayMode:st.optional().describe("How the UI is currently displayed."),availableDisplayModes:x(st).optional().describe("Display modes the host supports."),containerDimensions:U([m({height:O().describe("Fixed container height in pixels.")}),m({maxHeight:U([O(),it()]).optional().describe("Maximum container height in pixels.")})]).and(U([m({width:O().describe("Fixed container width in pixels.")}),m({maxWidth:U([O(),it()]).optional().describe("Maximum container width in pixels.")})])).optional().describe(`Container dimensions. Represents the dimensions of the iframe or other
73
- container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:d().optional().describe("User's language and region preference in BCP 47 format."),timeZone:d().optional().describe("User's timezone in IANA format."),userAgent:d().optional().describe("Host application identifier."),platform:U([u("web"),u("desktop"),u("mobile")]).optional().describe("Platform type for responsive design decisions."),deviceCapabilities:m({touch:H().optional().describe("Whether the device supports touch input."),hover:H().optional().describe("Whether the device supports hover interactions.")}).optional().describe("Device input capabilities."),safeAreaInsets:m({top:O().describe("Top safe area inset in pixels."),right:O().describe("Right safe area inset in pixels."),bottom:O().describe("Bottom safe area inset in pixels."),left:O().describe("Left safe area inset in pixels.")}).optional().describe("Mobile safe area boundaries in pixels.")}).passthrough(),Ch=m({method:u("ui/notifications/host-context-changed"),params:Cl.describe("Partial context update containing only changed fields.")});m({method:u("ui/update-model-context"),params:m({content:x(jt).optional().describe("Context content blocks (text, image, etc.)."),structuredContent:A(d(),J().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.")})});m({method:u("ui/initialize"),params:m({appInfo:Kn.describe("App identification (name and version)."),appCapabilities:Uh.describe("Features and capabilities this app provides."),protocolVersion:d().describe("Protocol version this app supports.")})});var Zh=m({protocolVersion:d().describe('Negotiated protocol version string (e.g., "2025-11-21").'),hostInfo:Kn.describe("Host application identification and version."),hostCapabilities:Ph.describe("Features and capabilities provided by the host."),hostContext:Cl.describe("Rich context about the host environment.")}).passthrough(),Ah={target:"draft-2020-12"};async function ta(e,n){let r=e["~standard"];if(r.jsonSchema)return r.jsonSchema[n](Ah);if(r.vendor==="zod"){let{z:o}=await td(()=>Promise.resolve().then(()=>zp),void 0,import.meta.url);return o.toJSONSchema(e,{io:n})}throw Error(`Schema (vendor: ${r.vendor}) does not implement Standard JSON Schema (~standard.jsonSchema). Use a library that does (zod v4, ArkType, Valibot) or wrap your schema accordingly.`)}async function na(e,n,r=""){let o=await e["~standard"].validate(n);if(o.issues){let t=o.issues.map(i=>{var s;let a=(s=i.path)==null?void 0:s.map(c=>typeof c=="object"?c.key:c).join(".");return a?`${a}: ${i.message}`:i.message}).join("; ");throw Error(r+t)}return o.value}function Lh(e){let n=document.documentElement;n.setAttribute("data-theme",e),n.style.colorScheme=e}function Mh(e,n=document.documentElement){for(let[r,o]of Object.entries(e))o!==void 0&&n.style.setProperty(r,o)}function qh(e){if(document.getElementById("__mcp-host-fonts"))return;let n=document.createElement("style");n.id="__mcp-host-fonts",n.textContent=e,document.head.appendChild(n)}const Bt=class Bt extends gh{constructor(r,o={},t={autoResize:!0}){super(t);C(this,"_appInfo");C(this,"_capabilities");C(this,"options");C(this,"_hostCapabilities");C(this,"_hostInfo");C(this,"_hostContext");C(this,"_registeredTools",{});C(this,"_initializedSent",!1);C(this,"eventSchemas",{toolinput:zh,toolinputpartial:xh,toolresult:Rh,toolcancelled:jh,hostcontextchanged:Ch});C(this,"_everHadListener",new Set);C(this,"_toolHandlersInitialized",!1);C(this,"_onteardown");C(this,"_oncalltool");C(this,"_onlisttools");C(this,"sendOpenLink",this.openLink);this._appInfo=r,this._capabilities=o,this.options=t,t.allowUnsafeEval||ee({jitless:!0}),this.setRequestHandler(Gn,i=>(console.log("Received ping:",i.params),{})),this.setEventHandler("hostcontextchanged",void 0)}_assertInitialized(r){var t;if(this._initializedSent)return;let o=`[ext-apps] App.${r}() called before connect() completed the ui/initialize handshake. Await app.connect() before calling this method, or move data loading to an ontoolresult handler.`;if((t=this.options)!=null&&t.strict)throw Error(o);console.warn(`${o}. This will throw in a future release.`)}_assertHandlerTiming(r){var t;if(!Bt.ONE_SHOT_EVENTS.has(r)||this._everHadListener.has(r)||(this._everHadListener.add(r),!this._initializedSent))return;let o=`[ext-apps] "${String(r)}" handler registered after connect() completed the ui/initialize handshake. The host may have already sent this notification. Register handlers before calling app.connect().`;if((t=this.options)!=null&&t.strict)throw Error(o);console.warn(o)}setEventHandler(r,o){o&&this._assertHandlerTiming(r),super.setEventHandler(r,o)}addEventListener(r,o){this._assertHandlerTiming(r),super.addEventListener(r,o)}onEventDispatch(r,o){r==="hostcontextchanged"&&(this._hostContext={...this._hostContext,...o})}registerCapabilities(r){if(this.transport)throw Error("Cannot register capabilities after transport is established");this._capabilities=hh(this._capabilities,r)}registerTool(r,o,t){if(this._registeredTools[r])throw Error(`Tool ${r} is already registered`);let i=this,a=()=>{var p;i._initializedSent&&((p=i._capabilities.tools)!=null&&p.listChanged)&&i.sendToolListChanged()},s=o.inputSchema!==void 0,c={title:o.title,description:o.description,inputSchema:o.inputSchema,outputSchema:o.outputSchema,annotations:o.annotations,_meta:o._meta,enabled:!0,enable(){this.enabled=!0,a()},disable(){this.enabled=!1,a()},update(p){Object.assign(this,p),a()},remove(){i._registeredTools[r]===c&&(delete i._registeredTools[r],a())},handler:async(p,h)=>{if(!c.enabled)throw Error(`Tool ${r} is disabled`);let f;if(s){let _=c.inputSchema,w=_?await na(_,p??{},`Invalid input for tool ${r}: `):p??{};f=await t(w,h)}else f=await t(h);return c.outputSchema&&!f.isError&&(f.structuredContent=await na(c.outputSchema,f.structuredContent,`Invalid output for tool ${r}: `)),f}};return this._registeredTools[r]=c,!this._capabilities.tools&&!this.transport&&this.registerCapabilities({tools:{listChanged:!0}}),this.ensureToolHandlersInitialized(),a(),c}ensureToolHandlersInitialized(){this._toolHandlersInitialized||(this._toolHandlersInitialized=!0,this.oncalltool=async(r,o)=>{let t=this._registeredTools[r.name];if(!t)throw Error(`Tool ${r.name} not found`);return t.handler(r.arguments,o)},this.onlisttools=async(r,o)=>({tools:await Promise.all(Object.entries(this._registeredTools).filter(([t,i])=>i.enabled).map(async([t,i])=>{let a={name:t,title:i.title,description:i.description,inputSchema:i.inputSchema?await ta(i.inputSchema,"input"):{type:"object",properties:{}}};return i.outputSchema&&(a.outputSchema=await ta(i.outputSchema,"output")),i.annotations&&(a.annotations=i.annotations),i._meta&&(a._meta=i._meta),a}))}))}async sendToolListChanged(r={}){this._assertInitialized("sendToolListChanged"),await this.notification({method:"notifications/tools/list_changed",params:r})}getHostCapabilities(){return this._hostCapabilities}getHostVersion(){return this._hostInfo}getHostContext(){return this._hostContext}get ontoolinput(){return this.getEventHandler("toolinput")}set ontoolinput(r){this.setEventHandler("toolinput",r)}get ontoolinputpartial(){return this.getEventHandler("toolinputpartial")}set ontoolinputpartial(r){this.setEventHandler("toolinputpartial",r)}get ontoolresult(){return this.getEventHandler("toolresult")}set ontoolresult(r){this.setEventHandler("toolresult",r)}get ontoolcancelled(){return this.getEventHandler("toolcancelled")}set ontoolcancelled(r){this.setEventHandler("toolcancelled",r)}get onhostcontextchanged(){return this.getEventHandler("hostcontextchanged")}set onhostcontextchanged(r){this.setEventHandler("hostcontextchanged",r)}get onteardown(){return this._onteardown}set onteardown(r){this.warnIfRequestHandlerReplaced("onteardown",this._onteardown,r),this._onteardown=r,this.replaceRequestHandler(Th,(o,t)=>{if(!this._onteardown)throw Error("No onteardown handler set");return this._onteardown(o.params,t)})}get oncalltool(){return this._oncalltool}set oncalltool(r){this.warnIfRequestHandlerReplaced("oncalltool",this._oncalltool,r),this._oncalltool=r,this.replaceRequestHandler(Ul,(o,t)=>{if(!this._oncalltool)throw Error("No oncalltool handler set");return this._oncalltool(o.params,t)})}get onlisttools(){return this._onlisttools}set onlisttools(r){this.warnIfRequestHandlerReplaced("onlisttools",this._onlisttools,r),this._onlisttools=r,this.replaceRequestHandler(Pl,(o,t)=>{if(!this._onlisttools)throw Error("No onlisttools handler set");return this._onlisttools(o.params,t)})}assertCapabilityForMethod(r){var o;switch(r){case"sampling/createMessage":if(!((o=this._hostCapabilities)!=null&&o.sampling))throw Error(`Host does not support sampling (required for ${r})`);break}}assertRequestHandlerCapability(r){switch(r){case"tools/call":case"tools/list":if(!this._capabilities.tools)throw Error(`Client does not support tool capability (required for ${r})`);return;case"ping":case"ui/resource-teardown":return;default:throw Error(`No handler for method ${r} registered`)}}assertNotificationCapability(r){}assertTaskCapability(r){throw Error("Tasks are not supported in MCP Apps")}assertTaskHandlerCapability(r){throw Error("Task handlers are not supported in MCP Apps")}async callServerTool(r,o){if(this._assertInitialized("callServerTool"),typeof r=="string")throw Error(`callServerTool() expects an object as its first argument, but received a string ("${r}"). Did you mean: callServerTool({ name: "${r}", arguments: { ... } })?`);return await this.request({method:"tools/call",params:r},Qn,{onprogress:()=>{},resetTimeoutOnProgress:!0,...o})}async readServerResource(r,o){return this._assertInitialized("readServerResource"),await this.request({method:"resources/read",params:r},Nl,o)}async listServerResources(r,o){return this._assertInitialized("listServerResources"),await this.request({method:"resources/list",params:r},jl,o)}async createSamplingMessage(r,o){this._assertInitialized("createSamplingMessage");let t=r.tools?Rl:Dl;return await this.request({method:"sampling/createMessage",params:r},t,o)}sendMessage(r,o){return this._assertInitialized("sendMessage"),this.request({method:"ui/message",params:r},Ih,o)}sendLog(r){return this.notification({method:"notifications/message",params:r})}updateModelContext(r,o){return this._assertInitialized("updateModelContext"),this.request({method:"ui/update-model-context",params:r},lo,o)}openLink(r,o){return this._assertInitialized("openLink"),this.request({method:"ui/open-link",params:r},Sh,o)}downloadFile(r,o){return this._assertInitialized("downloadFile"),this.request({method:"ui/download-file",params:r},wh,o)}requestTeardown(r={}){return this.notification({method:"ui/notifications/request-teardown",params:r})}requestDisplayMode(r,o){return this._assertInitialized("requestDisplayMode"),this.request({method:"ui/request-display-mode",params:r},Eh,o)}sendSizeChanged(r){return this.notification({method:"ui/notifications/size-changed",params:r})}setupSizeChangedNotifications(){let r=!1,o=0,t=0,i=()=>{r||(r=!0,requestAnimationFrame(()=>{r=!1;let s=document.documentElement,c=s.style.height;s.style.height="max-content";let p=Math.ceil(s.getBoundingClientRect().height);s.style.height=c;let h=Math.ceil(window.innerWidth);(h!==o||p!==t)&&(o=h,t=p,this.sendSizeChanged({width:h,height:p}))}))};i();let a=new ResizeObserver(i);return a.observe(document.documentElement),a.observe(document.body),()=>a.disconnect()}async connect(r=new bh(window.parent,window.parent),o){var t;if(this.transport)throw Error("App is already connected. Call close() before connecting again.");this._initializedSent=!1,await super.connect(r);try{let i=await this.request({method:"ui/initialize",params:{appCapabilities:this._capabilities,appInfo:this._appInfo,protocolVersion:vh}},Zh,o);if(i===void 0)throw Error(`Server sent invalid initialize result: ${i}`);this._hostCapabilities=i.hostCapabilities,this._hostInfo=i.hostInfo,this._hostContext=i.hostContext,await this.notification({method:"ui/notifications/initialized"}),this._initializedSent=!0,(t=this.options)!=null&&t.autoResize&&this.setupSizeChangedNotifications()}catch(i){throw this.close(),i}}};C(Bt,"ONE_SHOT_EVENTS",new Set(["toolinput","toolinputpartial","toolresult","toolcancelled"]));let dr=Bt;function ra(e){const n=e&&e.structuredContent;if(n&&typeof n=="object"){if(typeof n.snapshot=="string")try{return JSON.parse(n.snapshot)}catch{}return n}const r=(e&&e.content||[]).find(o=>o&&o.type==="text");if(r!=null&&r.text)try{return JSON.parse(r.text)}catch{return{}}return{}}class Hh{constructor(){C(this,"onhostcontextchanged");C(this,"onerror");C(this,"ontoolresult")}async connect(){var n,r;try{const[o,t]=await Promise.all([this.callServerTool({name:"project_config",arguments:{status:!0}}),this.callServerTool({name:"runtime",arguments:{action:"status"}})]),i=ra(o),a=ra(t),s=Array.isArray(i.projects)?i.projects:[],c={projects:s,projects_total:s.length,projects_ready:s.filter(p=>p&&p.ready).length,x_connected:!!i.x_connected,x_state:i.x_state||"",x_handle:i.x_handle??null,version:i.mcp_version||"",latest_version:i.latest_version??null,update_available:!!i.update_available,runtime_ready:typeof a.runtime_ready=="boolean"?a.runtime_ready:!0,runtime_provisioning:!!a.provisioning,onboarding:a.onboarding||i.onboarding};(n=this.ontoolresult)==null||n.call(this,{structuredContent:{snapshot:JSON.stringify(c)}})}catch(o){(r=this.onerror)==null||r.call(this,o)}}getHostContext(){}async callServerTool(n){const r=await fetch(`/tool/${encodeURIComponent(n.name)}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n.arguments??{})});if(!r.ok){let o=`HTTP ${r.status}`;try{o=await r.text()||o}catch{}return{isError:!0,content:[{type:"text",text:o}]}}return r.json()}async sendMessage(){return{isError:!0}}}function Fh(){return globalThis.__S4L_BRIDGE__==="http"?new Hh:new dr({name:"S4L Panel",version:"1.0.0"})}function Zl(e){const n=e.structuredContent;if(n&&typeof n=="object"){if(typeof n.snapshot=="string")try{return JSON.parse(n.snapshot)}catch{}return n}const r=(e.content||[]).find(o=>o.type==="text");if(r!=null&&r.text)try{return JSON.parse(r.text)}catch{return{_raw:r.text}}return{}}const D=e=>document.getElementById(e),Al=D("ver"),Xe=D("btn-setup"),Ht=D("btn-schedule"),Pt=D("stats-grid"),er=D("stats-toggle"),Jh=D("log"),Bh=D("install-card"),Ne=D("setup-summary"),Ll=D("onboarding-details"),Vh=D("onboarding-steps"),tr=D("onboarding-blocker"),ia=D("onboarding-count"),Wh=D("onboarding-bar-fill"),Kh=D("live-card"),Gh=D("stats-card"),oa=D("install-steps"),Ae=D("install-err"),_e=D("btn-install"),Qh=D("menubar-banner"),aa=D("btn-menubar-restart"),No=D("btn-live"),Oo=D("btn-live-stop"),sa=D("btn-live-front"),Le=D("live-status"),Ft=D("live-img"),Ml=D("switch-personal"),ql=D("switch-promo"),ve=D("switch-pause"),Yh=D("pause-name"),Xh=D("split-row"),eg=D("split-desc"),$e=D("split-slider"),Ce=D("posting-volume-select"),tg=D("posting-volume-desc"),ng=D("mode-sub"),rg=D("settings-card"),nr=D("settings-toggle"),fe=D("settings-body");let E=null,rr=!1,Ut=!1,ir=!1,ke=!1,Yn=!1,Qe=!1,De=!1,or=!1;function F(e){Jh.textContent=e}function ig(e){switch(e){case"done":return"✓";case"running":return"…";case"error":return"×";default:return"·"}}function To(e){if(!e||!Array.isArray(e.steps)){oa.innerHTML="";return}oa.innerHTML=e.steps.map(n=>{const r=n.detail&&n.status!=="pending"?` <span class="detail">${n.status==="error"?n.detail:""}</span>`:"";return`<li class="${n.status}"><span class="glyph">${ig(n.status)}</span><span>${n.label}${r}</span></li>`}).join(""),e.error?(Ae.textContent=e.error,Ae.hidden=!1):Ae.hidden=!0}const og={environment_checked:"Environment checked",runtime_ready:"Runtime ready",x_connected:"X connected",profile_scanned:"Profile scanned",project_ready:"Project ready",topics_seeded:"Topics seeded",tasks_scheduled:"Tasks scheduled"};function ag(e){switch(e){case"complete":return"✓";case"in_progress":return"…";case"blocked":return"×";default:return"·"}}function mr(){Ll.hidden=!ke,Ne.setAttribute("aria-expanded",String(ke)),Ne.classList.toggle("expanded",ke)}function Hl(e){return e?e.setup_complete!==void 0?!!e.setup_complete:!!e.runtime_ready&&(e.projects_ready||0)>0&&!!e.x_connected:!1}function sg(e){if(!e||!Array.isArray(e.milestones)){Ne.hidden=!0,Ll.hidden=!0;return}const n=Hl(E);Ne.hidden=n,n&&ke&&(ke=!1,mr());const r=e.milestones.length,o=e.milestones.filter(i=>i.status==="complete").length,t=!!e.current_blocker&&!n;Ne.classList.toggle("complete",n),Ne.classList.toggle("blocked",t),ia.hidden=n,ia.textContent=t?`${o}/${r} · needs you`:Ut?`${o}/${r} · setting up…`:`${o}/${r}`,Wh.style.width=r>0?`${Math.round(o/r*100)}%`:"0%",Vh.innerHTML=e.milestones.map(i=>{const a=og[i.id]||i.id,s=i.attempts>1?` <span class="detail">${i.attempts} attempts</span>`:"";return`<li class="${i.status}"><span class="glyph">${ag(i.status)}</span><span>${a}${s}</span></li>`}).join(""),e.current_blocker?(tr.textContent=`Current blocker: ${e.current_blocker.message}`,tr.hidden=!1,ke=!0):tr.hidden=!0,mr()}function Jt(){if(!E)return;sg(E.onboarding),Al.innerHTML=E.update_available&&E.latest_version?`v${E.version} · <button id="btn-update" class="update-btn">Update to ${E.latest_version}</button>`:`v${E.version}`;const e=!E.runtime_ready;Bh.hidden=!e,Qh.hidden=e||E.menubar_running!==!1;const n=Hl(E);Xe.hidden=n,Xe.disabled=!1,Xe.classList.toggle("primary",!n);const r=n&&(E.schedule_state==="missing"||E.schedule_state==="disabled"||E.schedule_state==="stalled");Ht.hidden=!r,Ht.classList.toggle("primary",r);const o=E.flags||(E.mode==="promotion"?{personal_brand:!1,promotion:!0}:{personal_brand:!0,promotion:!1}),t=!!o.personal_brand,i=!!o.promotion;Ml.setAttribute("aria-checked",String(t)),ql.setAttribute("aria-checked",String(i));const a=Math.round((typeof E.personal_brand_share=="number"?E.personal_brand_share:.5)*100);Xh.hidden=!(t&&i),Yn||($e.value=String(a),Vl(a)),ng.textContent=t&&i?`Both lanes on: cycles split ${a}/${100-a} personal/promotion.`:!t&&!i?"No lane on; the cycle falls back to personal brand.":"";const s=E.paused===!0;ve.setAttribute("aria-checked",String(!s)),ve.setAttribute("aria-label",s?"Resume S4L":"Pause S4L"),ve.title=s?"Resume S4L":"Pause S4L",Yh.textContent=s?"Paused":"Running",Kh.hidden=!n,Gh.hidden=!n,rg.hidden=e}function be(e){E={...E||{},...e},Jt()}function cg(e){const n=Array.isArray(e.projects)?e.projects:[];return{projects:n,projects_total:n.length,projects_ready:n.filter(r=>r.ready).length,x_connected:!!e.x_connected,x_state:e.x_state||"",x_handle:e.x_handle??null,...e.setup_complete!==void 0?{setup_complete:!!e.setup_complete}:{},version:e.mcp_version||(E==null?void 0:E.version)||"",latest_version:e.latest_version??null,update_available:!!e.update_available,mode:e.mode??(E==null?void 0:E.mode),flags:e.flags??(E==null?void 0:E.flags),personal_brand_share:e.personal_brand_share??(E==null?void 0:E.personal_brand_share),onboarding:e.onboarding}}const Ie=Fh();function Fl(e){var n,r,o;e.theme&&Lh(e.theme),(n=e.styles)!=null&&n.variables&&Mh(e.styles.variables),(o=(r=e.styles)==null?void 0:r.css)!=null&&o.fonts&&qh(e.styles.css.fonts)}Ie.onhostcontextchanged=Fl;Ie.onerror=e=>console.error(e);Ie.ontoolresult=e=>{const n=Zl(e);n&&typeof n.projects_total=="number"&&(be(n),n.runtime_ready?Jl():n.runtime_provisioning&&Po())};async function Y(e,n={}){const r=await Ie.callServerTool({name:e,arguments:n});return Zl(r)}async function ge(){F("Refreshing…");try{const[e,n]=await Promise.all([Y("project_config",{status:!0}),Y("runtime",{action:"status"}).catch(()=>({}))]);be({...cg(e),...typeof n.runtime_ready=="boolean"?{runtime_ready:n.runtime_ready}:{},...typeof n.menubar_running=="boolean"?{menubar_running:n.menubar_running}:{},...typeof n.paused=="boolean"?{paused:n.paused}:{},onboarding:n.onboarding||e.onboarding||(E==null?void 0:E.onboarding)}),E&&!E.runtime_ready&&n.provisioning&&Po(),F(""),Jl()}catch(e){F("Refresh failed: "+((e==null?void 0:e.message)||e))}}async function Po(){if(!rr){rr=!0,_e.disabled=!0,_e.textContent="Installing…";try{for(;;){const e=await Y("runtime",{action:"status"}).catch(()=>({}));if(To(e.progress??null),e.onboarding&&be({onboarding:e.onboarding}),e.runtime_ready){be({runtime_ready:!0}),F("Runtime installed; you're ready to set up."),ge();return}const n=e.progress??null;if(n&&n.done&&!n.ok){_e.disabled=!1,_e.textContent="Retry install",F("Install failed; see the step above, then Retry.");return}await new Promise(r=>setTimeout(r,1500))}}finally{rr=!1}}}async function ug(){var r;if(Ut)return;Ut=!0,Jt();const e=Date.now(),n=1200*1e3;try{for(;;){const o=await Y("runtime",{action:"status"}).catch(()=>({}));o.progress&&To(o.progress);const t={};if(typeof o.runtime_ready=="boolean"&&(t.runtime_ready=o.runtime_ready),o.onboarding&&(t.onboarding=o.onboarding),Object.keys(t).length&&be(t),(r=o.onboarding)!=null&&r.complete){await ge(),F("Setup complete.");break}if(Date.now()-e>n)break;await new Promise(i=>setTimeout(i,2e3))}}finally{Ut=!1,Jt()}}async function Jl(){try{const e=await Y("get_stats",{days:7}),n=Array.isArray(e.projects)?e.projects[0]:null,r=n==null?void 0:n.posts;if(!r){Pt.innerHTML='<div class="muted">No stats yet.</div>';return}const o=[["Posts",r.total??0],["Active",r.active??0],["Views",r.views_period_total??r.views??0],["Replies",r.comments_period_total??r.comments??0],["Clicks",r.post_clicks_period_total??0]];Pt.innerHTML=o.map(([t,i])=>`<div class="stat"><div class="n">${i}</div><div class="l">${t}</div></div>`).join("")}catch(e){Pt.innerHTML=`<div class="muted">Stats unavailable: ${(e==null?void 0:e.message)||e}</div>`}}function Xn(e,n,r){const o=e.textContent;e.disabled=!0,e.textContent=n,r().finally(()=>{e.disabled=!1,e.textContent=o,Jt()})}Xe.addEventListener("click",()=>Xn(Xe,"Starting…",async()=>{F("Asking Claude to run setup…");try{const e=await Ie.sendMessage({role:"user",content:[{type:"text",text:"Set up S4L plugin end to end now. Inspect and repair the runtime, auto-detect and connect my X session, scan my profile, discover and research my product, then infer and save a complete project with seeded search topics. Keep going without asking me to approve each safe setup step. Ask only if I must interactively sign in or no product can be identified. Keep every reply to me extremely concise: a few short sentences at most, no step-by-step narration or long status walls. If you must ask me something (e.g. the product URL), make it one short question."}]});e!=null&&e.isError?F("The host rejected the setup request — type “set up S4L” in the chat instead."):(F("Setup is running in the chat. It will only stop for an unavoidable login or missing product."),ug())}catch(e){F("Couldn’t start setup: "+((e==null?void 0:e.message)||e))}}));Ht.addEventListener("click",()=>Xn(Ht,"Setting up…",async()=>{F("Asking Claude to schedule the draft tasks for this account…"),Y("client_event",{event:"rearm_clicked",surface:"panel"}).catch(()=>{});try{const e=await Ie.sendMessage({role:"user",content:[{type:"text",text:`Set up the S4L draft autopilot schedule for this Claude account. If queue_setup is available, call it; then for s4l-worker call the host tool create_scheduled_task with taskId, cronExpression "* * * * *", notifyOnCompletion false (REQUIRED — the default true pops a notification every minute), and the prompt — read it from ~/.claude/scheduled-tasks/s4l-worker/SKILL.md (already on disk). If the task already exists, call update_scheduled_task with taskId s4l-worker and notifyOnCompletion false instead. Before calling create_scheduled_task/list_scheduled_tasks/update_scheduled_task, run (via Bash, best-effort, ignore any error and continue if the path doesn't exist): python3 ~/.social-autoposter-mcp/repo/package/scripts/mark_event.py start <that tool's name>; immediately after it returns, run the same with 'end' instead of 'start'. Do NOT redo my X connection or project setup. Keep replies to me very short.`}]});e!=null&&e.isError?F("The host rejected it — type “set up the draft schedule” in the chat instead."):F("Scheduling is running in the chat. The draft tasks will register for this account.")}catch(e){F("Couldn’t start scheduling: "+((e==null?void 0:e.message)||e))}}));function Bl(e,n){e.addEventListener("click",async()=>{if(!e.disabled){e.disabled=!0,e.setAttribute("aria-checked",String(e.getAttribute("aria-checked")!=="true"));try{const r=await Y("engagement_mode",{action:"toggle",lane:n});r&&r.flags&&be({flags:r.flags}),await ge()}catch(r){F("Couldn’t switch lane: "+((r==null?void 0:r.message)||r)),await ge()}finally{e.disabled=!1}}})}Bl(Ml,"personal_brand");Bl(ql,"promotion");ve.addEventListener("click",async()=>{if(ve.disabled)return;const e=ve.getAttribute("aria-checked")!=="true",n=e?"resume":"pause";ve.disabled=!0,ve.setAttribute("aria-checked",String(e));try{const r=await Y("pause_s4l",{action:n});F(r!=null&&r.ok?n==="pause"?"S4L paused — drafting and posting are stopped.":"S4L resumed.":`Couldn’t ${n} S4L${r!=null&&r.detail?": "+r.detail:"."}`),await ge()}catch(r){F(`Couldn’t ${n} S4L: `+((r==null?void 0:r.message)||r)),await ge()}finally{ve.disabled=!1}});function Vl(e){eg.textContent=`${e}% personal brand / ${100-e}% promotion`}$e.addEventListener("pointerdown",()=>{Yn=!0});$e.addEventListener("input",()=>{Yn=!0,Vl(Number($e.value))});$e.addEventListener("change",async()=>{const e=Number($e.value);$e.disabled=!0;try{const n=await Y("engagement_mode",{action:"split",split:e});n&&typeof n.personal_brand_share=="number"&&be({personal_brand_share:n.personal_brand_share}),await ge()}catch(n){F("Couldn’t set the lane split: "+((n==null?void 0:n.message)||n)),await ge()}finally{$e.disabled=!1,Yn=!1}});let Wl=!1;function lg(e){if(!e||e.error)return;const n=e.mode||"";Ce.value=n;const r=Array.isArray(e.rates)?e.rates:[],o={high:"Aggressive",medium:"Steady",low:"Chill"};for(const t of r){const i=Ce.querySelector(`option[value="${t.mode}"]`);if(i&&t.est_posts_per_day!==null&&t.est_posts_per_day!==void 0){const a=Number(t.est_posts_per_day),s=o[t.mode]||t.mode;i.textContent=`${s} (~${a>=10?Math.round(a):a}/day)`}}tg.textContent=n?"How many drafts per day the cycle produces. Applies from the next cycle.":"How many drafts per day the cycle produces."}async function pr(){try{const e=await Y("posting_volume",{action:"get"});Wl=!0,lg(e)}catch{}}Ce.addEventListener("change",async()=>{const e=Ce.value||"default";Ce.disabled=!0;try{const n=await Y("posting_volume",{action:"set",mode:e});n&&n.error?F("Couldn’t change posting volume: "+n.error):F(e==="default"?"Posting volume back to the default cycle setting.":`Posting volume set to ${{high:"Aggressive",medium:"Steady",low:"Chill"}[e]||e}. Applies from the next cycle.`),await pr()}catch(n){F("Couldn’t change posting volume: "+((n==null?void 0:n.message)||n)),await pr()}finally{Ce.disabled=!1}});Ne.addEventListener("click",()=>{ke=!ke,mr()});er.addEventListener("click",()=>{Qe=!Qe,Pt.hidden=!Qe,er.setAttribute("aria-expanded",String(Qe)),er.classList.toggle("expanded",Qe)});Al.addEventListener("click",e=>{const n=e.target;n&&n.id==="btn-update"&&dg()});async function dg(){if(ir)return;ir=!0;const e=document.getElementById("btn-update");e&&(e.disabled=!0,e.textContent="Updating…"),F("Installing the latest release… this can take a minute.");try{const n=await Y("runtime",{action:"update"});n.ok?(F(`Updated to ${n.latest_published||"the latest version"}. ${n.takes_effect||"Restart the client to apply."}`),e&&(e.textContent="Update installed — restart to apply")):(F("Update failed (exit "+(n.exit_code??"?")+"). Try `npx social-autoposter@latest update` in a terminal."),e&&(e.disabled=!1,e.textContent="Retry update"))}catch(n){F("Update failed: "+((n==null?void 0:n.message)||n)),e&&(e.disabled=!1,e.textContent="Retry update")}finally{ir=!1}}_e.addEventListener("click",async()=>{Ae.hidden=!0,_e.disabled=!0,_e.textContent="Starting…",F("Installing the runtime — this is a one-time download (~150MB+).");try{const e=await Y("runtime",{action:"install"});if(e.runtime_ready){be({runtime_ready:!0}),ge();return}To(e.progress??null),Po()}catch(e){_e.disabled=!1,_e.textContent="Retry install",Ae.textContent="Couldn't start install: "+((e==null?void 0:e.message)||e),Ae.hidden=!1}});aa.addEventListener("click",()=>Xn(aa,"Restarting…",async()=>{F("Restarting the S4L menu bar…");try{const e=await Y("restart_menubar");typeof e.menubar_running=="boolean"&&be({menubar_running:e.menubar_running}),F(e.menubar_running?"Menu bar restarted.":"Couldn’t confirm the menu bar came back"+(e.detail?": "+e.detail:"."))}catch(e){F("Couldn’t restart the menu bar: "+((e==null?void 0:e.message)||e))}}));const mg=["website","description","icp","voice","differentiator","search_topics","get_started_link","content_guardrails"],pg=["description","voice","search_topics","content_angle","content_guardrails"],fr={website:"Website",description:"What it does",icp:"Target audience",voice:"Voice",differentiator:"Differentiator",search_topics:"Search topics",get_started_link:"Get-started link",content_guardrails:"Content guardrails",content_angle:"Content angle"},ca=new Set(["website","description","icp","voice","differentiator","search_topics","get_started_link","content_guardrails"]),fg=new Set(["website","get_started_link"]);function et(e){const n=e.replace(/[_-]+/g," ").trim();return n?n.charAt(0).toUpperCase()+n.slice(1):e}function Kl(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function ua(e){return Array.isArray(e)&&e.every(n=>typeof n=="string")?{kind:"list",text:e.join(`
73
+ container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:d().optional().describe("User's language and region preference in BCP 47 format."),timeZone:d().optional().describe("User's timezone in IANA format."),userAgent:d().optional().describe("Host application identifier."),platform:U([u("web"),u("desktop"),u("mobile")]).optional().describe("Platform type for responsive design decisions."),deviceCapabilities:m({touch:H().optional().describe("Whether the device supports touch input."),hover:H().optional().describe("Whether the device supports hover interactions.")}).optional().describe("Device input capabilities."),safeAreaInsets:m({top:O().describe("Top safe area inset in pixels."),right:O().describe("Right safe area inset in pixels."),bottom:O().describe("Bottom safe area inset in pixels."),left:O().describe("Left safe area inset in pixels.")}).optional().describe("Mobile safe area boundaries in pixels.")}).passthrough(),Ch=m({method:u("ui/notifications/host-context-changed"),params:Cl.describe("Partial context update containing only changed fields.")});m({method:u("ui/update-model-context"),params:m({content:x(jt).optional().describe("Context content blocks (text, image, etc.)."),structuredContent:A(d(),J().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.")})});m({method:u("ui/initialize"),params:m({appInfo:Kn.describe("App identification (name and version)."),appCapabilities:Uh.describe("Features and capabilities this app provides."),protocolVersion:d().describe("Protocol version this app supports.")})});var Zh=m({protocolVersion:d().describe('Negotiated protocol version string (e.g., "2025-11-21").'),hostInfo:Kn.describe("Host application identification and version."),hostCapabilities:Ph.describe("Features and capabilities provided by the host."),hostContext:Cl.describe("Rich context about the host environment.")}).passthrough(),Ah={target:"draft-2020-12"};async function ta(e,n){let r=e["~standard"];if(r.jsonSchema)return r.jsonSchema[n](Ah);if(r.vendor==="zod"){let{z:o}=await td(()=>Promise.resolve().then(()=>zp),void 0,import.meta.url);return o.toJSONSchema(e,{io:n})}throw Error(`Schema (vendor: ${r.vendor}) does not implement Standard JSON Schema (~standard.jsonSchema). Use a library that does (zod v4, ArkType, Valibot) or wrap your schema accordingly.`)}async function na(e,n,r=""){let o=await e["~standard"].validate(n);if(o.issues){let t=o.issues.map(i=>{var s;let a=(s=i.path)==null?void 0:s.map(c=>typeof c=="object"?c.key:c).join(".");return a?`${a}: ${i.message}`:i.message}).join("; ");throw Error(r+t)}return o.value}function Lh(e){let n=document.documentElement;n.setAttribute("data-theme",e),n.style.colorScheme=e}function Mh(e,n=document.documentElement){for(let[r,o]of Object.entries(e))o!==void 0&&n.style.setProperty(r,o)}function qh(e){if(document.getElementById("__mcp-host-fonts"))return;let n=document.createElement("style");n.id="__mcp-host-fonts",n.textContent=e,document.head.appendChild(n)}const Bt=class Bt extends gh{constructor(r,o={},t={autoResize:!0}){super(t);C(this,"_appInfo");C(this,"_capabilities");C(this,"options");C(this,"_hostCapabilities");C(this,"_hostInfo");C(this,"_hostContext");C(this,"_registeredTools",{});C(this,"_initializedSent",!1);C(this,"eventSchemas",{toolinput:zh,toolinputpartial:xh,toolresult:Rh,toolcancelled:jh,hostcontextchanged:Ch});C(this,"_everHadListener",new Set);C(this,"_toolHandlersInitialized",!1);C(this,"_onteardown");C(this,"_oncalltool");C(this,"_onlisttools");C(this,"sendOpenLink",this.openLink);this._appInfo=r,this._capabilities=o,this.options=t,t.allowUnsafeEval||ee({jitless:!0}),this.setRequestHandler(Gn,i=>(console.log("Received ping:",i.params),{})),this.setEventHandler("hostcontextchanged",void 0)}_assertInitialized(r){var t;if(this._initializedSent)return;let o=`[ext-apps] App.${r}() called before connect() completed the ui/initialize handshake. Await app.connect() before calling this method, or move data loading to an ontoolresult handler.`;if((t=this.options)!=null&&t.strict)throw Error(o);console.warn(`${o}. This will throw in a future release.`)}_assertHandlerTiming(r){var t;if(!Bt.ONE_SHOT_EVENTS.has(r)||this._everHadListener.has(r)||(this._everHadListener.add(r),!this._initializedSent))return;let o=`[ext-apps] "${String(r)}" handler registered after connect() completed the ui/initialize handshake. The host may have already sent this notification. Register handlers before calling app.connect().`;if((t=this.options)!=null&&t.strict)throw Error(o);console.warn(o)}setEventHandler(r,o){o&&this._assertHandlerTiming(r),super.setEventHandler(r,o)}addEventListener(r,o){this._assertHandlerTiming(r),super.addEventListener(r,o)}onEventDispatch(r,o){r==="hostcontextchanged"&&(this._hostContext={...this._hostContext,...o})}registerCapabilities(r){if(this.transport)throw Error("Cannot register capabilities after transport is established");this._capabilities=hh(this._capabilities,r)}registerTool(r,o,t){if(this._registeredTools[r])throw Error(`Tool ${r} is already registered`);let i=this,a=()=>{var p;i._initializedSent&&((p=i._capabilities.tools)!=null&&p.listChanged)&&i.sendToolListChanged()},s=o.inputSchema!==void 0,c={title:o.title,description:o.description,inputSchema:o.inputSchema,outputSchema:o.outputSchema,annotations:o.annotations,_meta:o._meta,enabled:!0,enable(){this.enabled=!0,a()},disable(){this.enabled=!1,a()},update(p){Object.assign(this,p),a()},remove(){i._registeredTools[r]===c&&(delete i._registeredTools[r],a())},handler:async(p,h)=>{if(!c.enabled)throw Error(`Tool ${r} is disabled`);let f;if(s){let _=c.inputSchema,w=_?await na(_,p??{},`Invalid input for tool ${r}: `):p??{};f=await t(w,h)}else f=await t(h);return c.outputSchema&&!f.isError&&(f.structuredContent=await na(c.outputSchema,f.structuredContent,`Invalid output for tool ${r}: `)),f}};return this._registeredTools[r]=c,!this._capabilities.tools&&!this.transport&&this.registerCapabilities({tools:{listChanged:!0}}),this.ensureToolHandlersInitialized(),a(),c}ensureToolHandlersInitialized(){this._toolHandlersInitialized||(this._toolHandlersInitialized=!0,this.oncalltool=async(r,o)=>{let t=this._registeredTools[r.name];if(!t)throw Error(`Tool ${r.name} not found`);return t.handler(r.arguments,o)},this.onlisttools=async(r,o)=>({tools:await Promise.all(Object.entries(this._registeredTools).filter(([t,i])=>i.enabled).map(async([t,i])=>{let a={name:t,title:i.title,description:i.description,inputSchema:i.inputSchema?await ta(i.inputSchema,"input"):{type:"object",properties:{}}};return i.outputSchema&&(a.outputSchema=await ta(i.outputSchema,"output")),i.annotations&&(a.annotations=i.annotations),i._meta&&(a._meta=i._meta),a}))}))}async sendToolListChanged(r={}){this._assertInitialized("sendToolListChanged"),await this.notification({method:"notifications/tools/list_changed",params:r})}getHostCapabilities(){return this._hostCapabilities}getHostVersion(){return this._hostInfo}getHostContext(){return this._hostContext}get ontoolinput(){return this.getEventHandler("toolinput")}set ontoolinput(r){this.setEventHandler("toolinput",r)}get ontoolinputpartial(){return this.getEventHandler("toolinputpartial")}set ontoolinputpartial(r){this.setEventHandler("toolinputpartial",r)}get ontoolresult(){return this.getEventHandler("toolresult")}set ontoolresult(r){this.setEventHandler("toolresult",r)}get ontoolcancelled(){return this.getEventHandler("toolcancelled")}set ontoolcancelled(r){this.setEventHandler("toolcancelled",r)}get onhostcontextchanged(){return this.getEventHandler("hostcontextchanged")}set onhostcontextchanged(r){this.setEventHandler("hostcontextchanged",r)}get onteardown(){return this._onteardown}set onteardown(r){this.warnIfRequestHandlerReplaced("onteardown",this._onteardown,r),this._onteardown=r,this.replaceRequestHandler(Th,(o,t)=>{if(!this._onteardown)throw Error("No onteardown handler set");return this._onteardown(o.params,t)})}get oncalltool(){return this._oncalltool}set oncalltool(r){this.warnIfRequestHandlerReplaced("oncalltool",this._oncalltool,r),this._oncalltool=r,this.replaceRequestHandler(Ul,(o,t)=>{if(!this._oncalltool)throw Error("No oncalltool handler set");return this._oncalltool(o.params,t)})}get onlisttools(){return this._onlisttools}set onlisttools(r){this.warnIfRequestHandlerReplaced("onlisttools",this._onlisttools,r),this._onlisttools=r,this.replaceRequestHandler(Pl,(o,t)=>{if(!this._onlisttools)throw Error("No onlisttools handler set");return this._onlisttools(o.params,t)})}assertCapabilityForMethod(r){var o;switch(r){case"sampling/createMessage":if(!((o=this._hostCapabilities)!=null&&o.sampling))throw Error(`Host does not support sampling (required for ${r})`);break}}assertRequestHandlerCapability(r){switch(r){case"tools/call":case"tools/list":if(!this._capabilities.tools)throw Error(`Client does not support tool capability (required for ${r})`);return;case"ping":case"ui/resource-teardown":return;default:throw Error(`No handler for method ${r} registered`)}}assertNotificationCapability(r){}assertTaskCapability(r){throw Error("Tasks are not supported in MCP Apps")}assertTaskHandlerCapability(r){throw Error("Task handlers are not supported in MCP Apps")}async callServerTool(r,o){if(this._assertInitialized("callServerTool"),typeof r=="string")throw Error(`callServerTool() expects an object as its first argument, but received a string ("${r}"). Did you mean: callServerTool({ name: "${r}", arguments: { ... } })?`);return await this.request({method:"tools/call",params:r},Qn,{onprogress:()=>{},resetTimeoutOnProgress:!0,...o})}async readServerResource(r,o){return this._assertInitialized("readServerResource"),await this.request({method:"resources/read",params:r},Nl,o)}async listServerResources(r,o){return this._assertInitialized("listServerResources"),await this.request({method:"resources/list",params:r},jl,o)}async createSamplingMessage(r,o){this._assertInitialized("createSamplingMessage");let t=r.tools?Rl:Dl;return await this.request({method:"sampling/createMessage",params:r},t,o)}sendMessage(r,o){return this._assertInitialized("sendMessage"),this.request({method:"ui/message",params:r},Ih,o)}sendLog(r){return this.notification({method:"notifications/message",params:r})}updateModelContext(r,o){return this._assertInitialized("updateModelContext"),this.request({method:"ui/update-model-context",params:r},lo,o)}openLink(r,o){return this._assertInitialized("openLink"),this.request({method:"ui/open-link",params:r},Sh,o)}downloadFile(r,o){return this._assertInitialized("downloadFile"),this.request({method:"ui/download-file",params:r},wh,o)}requestTeardown(r={}){return this.notification({method:"ui/notifications/request-teardown",params:r})}requestDisplayMode(r,o){return this._assertInitialized("requestDisplayMode"),this.request({method:"ui/request-display-mode",params:r},Eh,o)}sendSizeChanged(r){return this.notification({method:"ui/notifications/size-changed",params:r})}setupSizeChangedNotifications(){let r=!1,o=0,t=0,i=()=>{r||(r=!0,requestAnimationFrame(()=>{r=!1;let s=document.documentElement,c=s.style.height;s.style.height="max-content";let p=Math.ceil(s.getBoundingClientRect().height);s.style.height=c;let h=Math.ceil(window.innerWidth);(h!==o||p!==t)&&(o=h,t=p,this.sendSizeChanged({width:h,height:p}))}))};i();let a=new ResizeObserver(i);return a.observe(document.documentElement),a.observe(document.body),()=>a.disconnect()}async connect(r=new bh(window.parent,window.parent),o){var t;if(this.transport)throw Error("App is already connected. Call close() before connecting again.");this._initializedSent=!1,await super.connect(r);try{let i=await this.request({method:"ui/initialize",params:{appCapabilities:this._capabilities,appInfo:this._appInfo,protocolVersion:vh}},Zh,o);if(i===void 0)throw Error(`Server sent invalid initialize result: ${i}`);this._hostCapabilities=i.hostCapabilities,this._hostInfo=i.hostInfo,this._hostContext=i.hostContext,await this.notification({method:"ui/notifications/initialized"}),this._initializedSent=!0,(t=this.options)!=null&&t.autoResize&&this.setupSizeChangedNotifications()}catch(i){throw this.close(),i}}};C(Bt,"ONE_SHOT_EVENTS",new Set(["toolinput","toolinputpartial","toolresult","toolcancelled"]));let dr=Bt;function ra(e){const n=e&&e.structuredContent;if(n&&typeof n=="object"){if(typeof n.snapshot=="string")try{return JSON.parse(n.snapshot)}catch{}return n}const r=(e&&e.content||[]).find(o=>o&&o.type==="text");if(r!=null&&r.text)try{return JSON.parse(r.text)}catch{return{}}return{}}class Hh{constructor(){C(this,"onhostcontextchanged");C(this,"onerror");C(this,"ontoolresult")}async connect(){var n,r;try{const[o,t]=await Promise.all([this.callServerTool({name:"project_config",arguments:{status:!0}}),this.callServerTool({name:"runtime",arguments:{action:"status"}})]),i=ra(o),a=ra(t),s=Array.isArray(i.projects)?i.projects:[],c={projects:s,projects_total:s.length,projects_ready:s.filter(p=>p&&p.ready).length,x_connected:!!i.x_connected,x_state:i.x_state||"",x_handle:i.x_handle??null,version:i.mcp_version||"",latest_version:i.latest_version??null,update_available:!!i.update_available,runtime_ready:typeof a.runtime_ready=="boolean"?a.runtime_ready:!0,runtime_provisioning:!!a.provisioning,onboarding:a.onboarding||i.onboarding};(n=this.ontoolresult)==null||n.call(this,{structuredContent:{snapshot:JSON.stringify(c)}})}catch(o){(r=this.onerror)==null||r.call(this,o)}}getHostContext(){}async callServerTool(n){const r=await fetch(`/tool/${encodeURIComponent(n.name)}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n.arguments??{})});if(!r.ok){let o=`HTTP ${r.status}`;try{o=await r.text()||o}catch{}return{isError:!0,content:[{type:"text",text:o}]}}return r.json()}async sendMessage(){return{isError:!0}}}function Fh(){return globalThis.__S4L_BRIDGE__==="http"?new Hh:new dr({name:"S4L Panel",version:"1.0.0"})}function Zl(e){const n=e.structuredContent;if(n&&typeof n=="object"){if(typeof n.snapshot=="string")try{return JSON.parse(n.snapshot)}catch{}return n}const r=(e.content||[]).find(o=>o.type==="text");if(r!=null&&r.text)try{return JSON.parse(r.text)}catch{return{_raw:r.text}}return{}}const D=e=>document.getElementById(e),Al=D("ver"),Xe=D("btn-setup"),Ht=D("btn-schedule"),Pt=D("stats-grid"),er=D("stats-toggle"),Jh=D("log"),Bh=D("install-card"),Ne=D("setup-summary"),Ll=D("onboarding-details"),Vh=D("onboarding-steps"),tr=D("onboarding-blocker"),ia=D("onboarding-count"),Wh=D("onboarding-bar-fill"),Kh=D("live-card"),Gh=D("stats-card"),oa=D("install-steps"),Ae=D("install-err"),_e=D("btn-install"),Qh=D("menubar-banner"),aa=D("btn-menubar-restart"),No=D("btn-live"),Oo=D("btn-live-stop"),sa=D("btn-live-front"),Le=D("live-status"),Ft=D("live-img"),Ml=D("switch-personal"),ql=D("switch-promo"),ve=D("switch-pause"),Yh=D("pause-name"),Xh=D("split-row"),eg=D("split-desc"),$e=D("split-slider"),Ce=D("posting-volume-select"),tg=D("posting-volume-desc"),ng=D("mode-sub"),rg=D("settings-card"),nr=D("settings-toggle"),fe=D("settings-body");let E=null,rr=!1,Ut=!1,ir=!1,ke=!1,Yn=!1,Qe=!1,De=!1,or=!1;function F(e){Jh.textContent=e}function ig(e){switch(e){case"done":return"✓";case"running":return"…";case"error":return"×";default:return"·"}}function To(e){if(!e||!Array.isArray(e.steps)){oa.innerHTML="";return}oa.innerHTML=e.steps.map(n=>{const r=n.detail&&n.status!=="pending"?` <span class="detail">${n.status==="error"?n.detail:""}</span>`:"";return`<li class="${n.status}"><span class="glyph">${ig(n.status)}</span><span>${n.label}${r}</span></li>`}).join(""),e.error?(Ae.textContent=e.error,Ae.hidden=!1):Ae.hidden=!0}const og={environment_checked:"Environment checked",runtime_ready:"Runtime ready",x_connected:"X connected",profile_scanned:"Profile scanned",project_ready:"Project ready",topics_seeded:"Topics seeded",tasks_scheduled:"Tasks scheduled"};function ag(e){switch(e){case"complete":return"✓";case"in_progress":return"…";case"blocked":return"×";default:return"·"}}function mr(){Ll.hidden=!ke,Ne.setAttribute("aria-expanded",String(ke)),Ne.classList.toggle("expanded",ke)}function Hl(e){return e?e.setup_complete!==void 0?!!e.setup_complete:!!e.runtime_ready&&(e.projects_ready||0)>0&&!!e.x_connected:!1}function sg(e){if(!e||!Array.isArray(e.milestones)){Ne.hidden=!0,Ll.hidden=!0;return}const n=Hl(E);Ne.hidden=n,n&&ke&&(ke=!1,mr());const r=e.milestones.length,o=e.milestones.filter(i=>i.status==="complete").length,t=!!e.current_blocker&&!n;Ne.classList.toggle("complete",n),Ne.classList.toggle("blocked",t),ia.hidden=n,ia.textContent=t?`${o}/${r} · needs you`:Ut?`${o}/${r} · setting up…`:`${o}/${r}`,Wh.style.width=r>0?`${Math.round(o/r*100)}%`:"0%",Vh.innerHTML=e.milestones.map(i=>{const a=og[i.id]||i.id,s=i.attempts>1?` <span class="detail">${i.attempts} attempts</span>`:"";return`<li class="${i.status}"><span class="glyph">${ag(i.status)}</span><span>${a}${s}</span></li>`}).join(""),e.current_blocker?(tr.textContent=`Current blocker: ${e.current_blocker.message}`,tr.hidden=!1,ke=!0):tr.hidden=!0,mr()}function Jt(){if(!E)return;sg(E.onboarding),Al.innerHTML=E.update_available&&E.latest_version?`v${E.version} · <button id="btn-update" class="update-btn">Update to ${E.latest_version}</button>`:`v${E.version}`;const e=!E.runtime_ready;Bh.hidden=!e,Qh.hidden=e||E.menubar_running!==!1;const n=Hl(E);Xe.hidden=n,Xe.disabled=!1,Xe.classList.toggle("primary",!n);const r=n&&(E.schedule_state==="missing"||E.schedule_state==="disabled"||E.schedule_state==="stalled");Ht.hidden=!r,Ht.classList.toggle("primary",r);const o=E.flags||(E.mode==="promotion"?{personal_brand:!1,promotion:!0}:{personal_brand:!0,promotion:!1}),t=!!o.personal_brand,i=!!o.promotion;Ml.setAttribute("aria-checked",String(t)),ql.setAttribute("aria-checked",String(i));const a=Math.round((typeof E.personal_brand_share=="number"?E.personal_brand_share:.5)*100);Xh.hidden=!(t&&i),Yn||($e.value=String(a),Vl(a)),ng.textContent=t&&i?`Both lanes on: cycles split ${a}/${100-a} personal/promotion.`:!t&&!i?"No lane on; the cycle falls back to personal brand.":"";const s=E.paused===!0;ve.setAttribute("aria-checked",String(!s)),ve.setAttribute("aria-label",s?"Resume S4L":"Pause S4L"),ve.title=s?"Resume S4L":"Pause S4L",Yh.textContent=s?"Paused":"Running",Kh.hidden=!n,Gh.hidden=!n,rg.hidden=e}function be(e){E={...E||{},...e},Jt()}function cg(e){const n=Array.isArray(e.projects)?e.projects:[];return{projects:n,projects_total:n.length,projects_ready:n.filter(r=>r.ready).length,x_connected:!!e.x_connected,x_state:e.x_state||"",x_handle:e.x_handle??null,...e.setup_complete!==void 0?{setup_complete:!!e.setup_complete}:{},version:e.mcp_version||(E==null?void 0:E.version)||"",latest_version:e.latest_version??null,update_available:!!e.update_available,mode:e.mode??(E==null?void 0:E.mode),flags:e.flags??(E==null?void 0:E.flags),personal_brand_share:e.personal_brand_share??(E==null?void 0:E.personal_brand_share),onboarding:e.onboarding}}const Ie=Fh();function Fl(e){var n,r,o;e.theme&&Lh(e.theme),(n=e.styles)!=null&&n.variables&&Mh(e.styles.variables),(o=(r=e.styles)==null?void 0:r.css)!=null&&o.fonts&&qh(e.styles.css.fonts)}Ie.onhostcontextchanged=Fl;Ie.onerror=e=>console.error(e);Ie.ontoolresult=e=>{const n=Zl(e);n&&typeof n.projects_total=="number"&&(be(n),n.runtime_ready?Jl():n.runtime_provisioning&&Po())};async function Y(e,n={}){const r=await Ie.callServerTool({name:e,arguments:n});return Zl(r)}async function ge(){F("Refreshing…");try{const[e,n]=await Promise.all([Y("project_config",{status:!0}),Y("runtime",{action:"status"}).catch(()=>({}))]);be({...cg(e),...typeof n.runtime_ready=="boolean"?{runtime_ready:n.runtime_ready}:{},...typeof n.menubar_running=="boolean"?{menubar_running:n.menubar_running}:{},...typeof n.paused=="boolean"?{paused:n.paused}:{},onboarding:n.onboarding||e.onboarding||(E==null?void 0:E.onboarding)}),E&&!E.runtime_ready&&n.provisioning&&Po(),F(""),Jl()}catch(e){F("Refresh failed: "+((e==null?void 0:e.message)||e))}}async function Po(){if(!rr){rr=!0,_e.disabled=!0,_e.textContent="Installing…";try{for(;;){const e=await Y("runtime",{action:"status"}).catch(()=>({}));if(To(e.progress??null),e.onboarding&&be({onboarding:e.onboarding}),e.runtime_ready){be({runtime_ready:!0}),F("Runtime installed; you're ready to set up."),ge();return}const n=e.progress??null;if(n&&n.done&&!n.ok){_e.disabled=!1,_e.textContent="Retry install",F("Install failed; see the step above, then Retry.");return}await new Promise(r=>setTimeout(r,1500))}}finally{rr=!1}}}async function ug(){var r;if(Ut)return;Ut=!0,Jt();const e=Date.now(),n=1200*1e3;try{for(;;){const o=await Y("runtime",{action:"status"}).catch(()=>({}));o.progress&&To(o.progress);const t={};if(typeof o.runtime_ready=="boolean"&&(t.runtime_ready=o.runtime_ready),o.onboarding&&(t.onboarding=o.onboarding),Object.keys(t).length&&be(t),(r=o.onboarding)!=null&&r.complete){await ge(),F("Setup complete.");break}if(Date.now()-e>n)break;await new Promise(i=>setTimeout(i,2e3))}}finally{Ut=!1,Jt()}}async function Jl(){try{const e=await Y("get_stats",{days:7}),n=Array.isArray(e.projects)?e.projects[0]:null,r=n==null?void 0:n.posts;if(!r){Pt.innerHTML='<div class="muted">No stats yet.</div>';return}const o=[["Posts",r.total??0],["Active",r.active??0],["Views",r.views_period_total??r.views??0],["Replies",r.comments_period_total??r.comments??0],["Clicks",r.post_clicks_period_total??0]];Pt.innerHTML=o.map(([t,i])=>`<div class="stat"><div class="n">${i}</div><div class="l">${t}</div></div>`).join("")}catch(e){Pt.innerHTML=`<div class="muted">Stats unavailable: ${(e==null?void 0:e.message)||e}</div>`}}function Xn(e,n,r){const o=e.textContent;e.disabled=!0,e.textContent=n,r().finally(()=>{e.disabled=!1,e.textContent=o,Jt()})}Xe.addEventListener("click",()=>Xn(Xe,"Starting…",async()=>{F("Asking Claude to run setup…");try{const e=await Ie.sendMessage({role:"user",content:[{type:"text",text:"Set up S4L plugin end to end now. Inspect and repair the runtime, auto-detect and connect my X session, scan my profile, discover and research my product, then infer and save a complete project with seeded search topics. Keep going without asking me to approve each safe setup step. Ask only if I must interactively sign in or no product can be identified. Keep every reply to me extremely concise: a few short sentences at most, no step-by-step narration or long status walls. If you must ask me something (e.g. the product URL), make it one short question."}]});e!=null&&e.isError?F("The host rejected the setup request — type “set up S4L” in the chat instead."):(F("Setup is running in the chat. It will only stop for an unavoidable login or missing product."),ug())}catch(e){F("Couldn’t start setup: "+((e==null?void 0:e.message)||e))}}));Ht.addEventListener("click",()=>Xn(Ht,"Setting up…",async()=>{F("Asking Claude to schedule the draft tasks for this account…"),Y("client_event",{event:"rearm_clicked",surface:"panel"}).catch(()=>{});try{const e=await Ie.sendMessage({role:"user",content:[{type:"text",text:`Set up the S4L draft autopilot schedule for this Claude account. If queue_setup is available, call it; then for s4l-worker call the host tool create_scheduled_task with taskId, cronExpression "* * * * *", notifyOnCompletion false (REQUIRED — the default true pops a notification every minute), and the prompt — read it from ~/.claude/scheduled-tasks/s4l-worker/SKILL.md (already on disk). If the task already exists, call update_scheduled_task with taskId s4l-worker and notifyOnCompletion false instead. Before calling create_scheduled_task/list_scheduled_tasks/update_scheduled_task, run (via Bash, best-effort, ignore any error and continue if the path doesn't exist): python3 ~/.social-autoposter-mcp/repo/package/scripts/mark_event.py start <that tool's name>; immediately after it returns, run the same with 'end' instead of 'start'. Do NOT redo my X connection or project setup. Keep replies to me very short.`}]});e!=null&&e.isError?F("The host rejected it — type “set up the draft schedule” in the chat instead."):F("Scheduling is running in the chat. The draft tasks will register for this account.")}catch(e){F("Couldn’t start scheduling: "+((e==null?void 0:e.message)||e))}}));function Bl(e,n){e.addEventListener("click",async()=>{if(!e.disabled){e.disabled=!0,e.setAttribute("aria-checked",String(e.getAttribute("aria-checked")!=="true"));try{const r=await Y("engagement_mode",{action:"toggle",lane:n});r&&r.flags&&be({flags:r.flags}),await ge()}catch(r){F("Couldn’t switch lane: "+((r==null?void 0:r.message)||r)),await ge()}finally{e.disabled=!1}}})}Bl(Ml,"personal_brand");Bl(ql,"promotion");ve.addEventListener("click",async()=>{if(ve.disabled)return;const e=ve.getAttribute("aria-checked")!=="true",n=e?"resume":"pause";ve.disabled=!0,ve.setAttribute("aria-checked",String(e));try{const r=await Y("pause_s4l",{action:n});F(r!=null&&r.ok?n==="pause"?"S4L paused — drafting and posting are stopped.":"S4L resumed.":`Couldn’t ${n} S4L${r!=null&&r.detail?": "+r.detail:"."}`),await ge()}catch(r){F(`Couldn’t ${n} S4L: `+((r==null?void 0:r.message)||r)),await ge()}finally{ve.disabled=!1}});function Vl(e){eg.textContent=`${e}% personal brand / ${100-e}% promotion`}$e.addEventListener("pointerdown",()=>{Yn=!0});$e.addEventListener("input",()=>{Yn=!0,Vl(Number($e.value))});$e.addEventListener("change",async()=>{const e=Number($e.value);$e.disabled=!0;try{const n=await Y("engagement_mode",{action:"split",split:e});n&&typeof n.personal_brand_share=="number"&&be({personal_brand_share:n.personal_brand_share}),await ge()}catch(n){F("Couldn’t set the lane split: "+((n==null?void 0:n.message)||n)),await ge()}finally{$e.disabled=!1,Yn=!1}});let Wl=!1;function lg(e){if(!e||e.error)return;const n=e.mode||"medium";Ce.value=n;const r=Array.isArray(e.rates)?e.rates:[],o={high:"Aggressive",medium:"Steady",low:"Chill"};for(const t of r){const i=Ce.querySelector(`option[value="${t.mode}"]`);if(i&&t.est_posts_per_day!==null&&t.est_posts_per_day!==void 0){const a=Number(t.est_posts_per_day),s=o[t.mode]||t.mode;i.textContent=`${s} (~${a>=10?Math.round(a):a}/day)`}}tg.textContent="How many drafts per day the cycle produces."}async function pr(){try{const e=await Y("posting_volume",{action:"get"});Wl=!0,lg(e)}catch{}}Ce.addEventListener("change",async()=>{const e=Ce.value;Ce.disabled=!0;try{const n=await Y("posting_volume",{action:"set",mode:e});n&&n.error?F("Couldn’t change posting volume: "+n.error):F(`Posting volume set to ${{high:"Aggressive",medium:"Steady",low:"Chill"}[e]||e}. Applies from the next cycle.`),await pr()}catch(n){F("Couldn’t change posting volume: "+((n==null?void 0:n.message)||n)),await pr()}finally{Ce.disabled=!1}});Ne.addEventListener("click",()=>{ke=!ke,mr()});er.addEventListener("click",()=>{Qe=!Qe,Pt.hidden=!Qe,er.setAttribute("aria-expanded",String(Qe)),er.classList.toggle("expanded",Qe)});Al.addEventListener("click",e=>{const n=e.target;n&&n.id==="btn-update"&&dg()});async function dg(){if(ir)return;ir=!0;const e=document.getElementById("btn-update");e&&(e.disabled=!0,e.textContent="Updating…"),F("Installing the latest release… this can take a minute.");try{const n=await Y("runtime",{action:"update"});n.ok?(F(`Updated to ${n.latest_published||"the latest version"}. ${n.takes_effect||"Restart the client to apply."}`),e&&(e.textContent="Update installed — restart to apply")):(F("Update failed (exit "+(n.exit_code??"?")+"). Try `npx social-autoposter@latest update` in a terminal."),e&&(e.disabled=!1,e.textContent="Retry update"))}catch(n){F("Update failed: "+((n==null?void 0:n.message)||n)),e&&(e.disabled=!1,e.textContent="Retry update")}finally{ir=!1}}_e.addEventListener("click",async()=>{Ae.hidden=!0,_e.disabled=!0,_e.textContent="Starting…",F("Installing the runtime — this is a one-time download (~150MB+).");try{const e=await Y("runtime",{action:"install"});if(e.runtime_ready){be({runtime_ready:!0}),ge();return}To(e.progress??null),Po()}catch(e){_e.disabled=!1,_e.textContent="Retry install",Ae.textContent="Couldn't start install: "+((e==null?void 0:e.message)||e),Ae.hidden=!1}});aa.addEventListener("click",()=>Xn(aa,"Restarting…",async()=>{F("Restarting the S4L menu bar…");try{const e=await Y("restart_menubar");typeof e.menubar_running=="boolean"&&be({menubar_running:e.menubar_running}),F(e.menubar_running?"Menu bar restarted.":"Couldn’t confirm the menu bar came back"+(e.detail?": "+e.detail:"."))}catch(e){F("Couldn’t restart the menu bar: "+((e==null?void 0:e.message)||e))}}));const mg=["website","description","icp","voice","differentiator","search_topics","get_started_link","content_guardrails"],pg=["description","voice","search_topics","content_angle","content_guardrails"],fr={website:"Website",description:"What it does",icp:"Target audience",voice:"Voice",differentiator:"Differentiator",search_topics:"Search topics",get_started_link:"Get-started link",content_guardrails:"Content guardrails",content_angle:"Content angle"},ca=new Set(["website","description","icp","voice","differentiator","search_topics","get_started_link","content_guardrails"]),fg=new Set(["website","get_started_link"]);function et(e){const n=e.replace(/[_-]+/g," ").trim();return n?n.charAt(0).toUpperCase()+n.slice(1):e}function Kl(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function ua(e){return Array.isArray(e)&&e.every(n=>typeof n=="string")?{kind:"list",text:e.join(`
74
74
  `)}:e==null||typeof e=="string"?{kind:"text",text:String(e??"")}:{kind:"json",text:JSON.stringify(e,null,2)}}function la(e,n,r){const o=document.createElement("div");o.className="settings-field";const t=document.createElement("label");t.textContent=e+(n.kind==="list"?" (one per line)":""),o.appendChild(t);let i;if(n.kind==="text"&&r)i=document.createElement("input"),i.type="text",i.className="settings-input";else{const a=document.createElement("textarea"),s=n.text?n.text.split(`
75
75
  `).reduce((c,p)=>c+Math.max(1,Math.ceil(p.length/60)),0):1;a.rows=Math.min(10,Math.max(2,s)),a.className="settings-textarea"+(n.kind==="json"?" mono":""),i=a}return i.value=n.text,n.text||(i.placeholder="Not set"),i.dataset.orig=n.text,o.appendChild(i),{wrap:o,el:i}}function hg(e){const n=document.createElement("div");n.className="settings-project";const r=document.createElement("div");r.className="settings-project-head";const o=document.createElement("span");if(o.className="settings-project-name",o.textContent=e.name,r.appendChild(o),e.persona){const f=document.createElement("span");f.className="settings-project-tag",f.textContent="personal brand",r.appendChild(f)}const t=document.createElement("span");t.className="settings-project-state",t.textContent=e.ready?"ready":"missing: "+e.missing_required.join(", "),r.appendChild(t),n.appendChild(r);const i=[],a=e.persona?pg:mg;for(const f of a){const _=e.fields[f];if(Kl(_)&&Object.keys(_).length){const z=document.createElement("fieldset");z.className="settings-group";const S=document.createElement("legend");S.textContent=fr[f]||et(f),z.appendChild(S);for(const[g,$]of Object.entries(_)){const y=ua($),I=la(et(g),y,!1);z.appendChild(I.wrap),i.push({key:f,sub:g,kind:y.kind,el:I.el,orig:y.text})}n.appendChild(z);continue}const w=f==="search_topics"?{kind:"list",text:Array.isArray(_)?_.map(String).join(`
76
76
  `):String(_??"")}:ua(_),b=la(fr[f]||et(f),w,fg.has(f));n.appendChild(b.wrap),i.push({key:f,kind:w.kind,el:b.el,orig:w.text})}if(e.extra_keys.length){const f=document.createElement("div");f.className="settings-extra",f.textContent="Advanced (edit via chat): "+e.extra_keys.join(", "),n.appendChild(f)}const s=document.createElement("div");s.className="settings-actions";const c=document.createElement("button");c.className="primary",c.textContent="Save changes",c.disabled=!0;const p=document.createElement("span");p.className="settings-status",s.appendChild(c),s.appendChild(p),n.appendChild(s);const h=()=>{c.disabled=!i.some(f=>f.el.value!==f.orig)};for(const f of i)f.el.addEventListener("input",h);return c.addEventListener("click",()=>void gg(e,i,c,p)),n}async function gg(e,n,r,o){const t=n.filter(h=>h.el.value!==h.orig);if(!t.length)return;const i={name:e.name},a={},s=h=>(fr[h.key]||et(h.key))+(h.sub?` · ${et(h.sub)}`:""),c=[...new Set(t.filter(h=>h.sub!==void 0).map(h=>h.key))];for(const h of c){const f=Kl(e.fields[h])?{...e.fields[h]}:{};for(const _ of n.filter(w=>w.key===h&&w.sub!==void 0)){const w=_.el.value;if(_.kind==="list"){const b=w.split(`
@@ -190,9 +190,8 @@ container holding the app. Specify either width or maxWidth, and either height o
190
190
  <div class="lane-desc" id="posting-volume-desc">How many drafts per day the cycle produces.</div>
191
191
  </div>
192
192
  <select id="posting-volume-select" aria-label="Posting volume">
193
- <option value="">Default</option>
194
193
  <option value="high">Aggressive</option>
195
- <option value="medium">Steady</option>
194
+ <option value="medium" selected>Steady</option>
196
195
  <option value="low">Chill</option>
197
196
  </select>
198
197
  </div>
@@ -1,4 +1,4 @@
1
1
  {
2
- "version": "1.7.5-rc.7",
3
- "installedAt": "2026-07-14T21:26:17.558Z"
2
+ "version": "1.7.5-rc.9",
3
+ "installedAt": "2026-07-14T22:38:40.417Z"
4
4
  }
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.5-rc.7",
5
+ "version": "1.7.5-rc.9",
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": {
@@ -1151,13 +1151,14 @@ class S4LMenuBar(rumps.App):
1151
1151
  )
1152
1152
 
1153
1153
  # Posting volume (2026-07-13): server-side per-install throttle for the
1154
- # twitter cycle's virality bar. None = driver default. The labels carry
1155
- # ballpark rates; exact per-install estimates live on the dashboard's
1156
- # Posting Volume section (Stats tab). Internal values stay high|medium|low
1157
- # (DB CHECK + API enum); Aggressive/Steady/Chill are display names only
1158
- # (renamed 2026-07-14 per user).
1154
+ # twitter cycle's virality bar. The labels carry ballpark rates; exact
1155
+ # per-install estimates live on the dashboard's Posting Volume section
1156
+ # (Stats tab). Internal values stay high|medium|low (DB CHECK + API enum);
1157
+ # Aggressive/Steady/Chill are display names only (renamed 2026-07-14).
1158
+ # There is NO separate "default" state (removed 2026-07-14 per user): an
1159
+ # install that never chose resolves to Steady server-side, so the menu
1160
+ # shows Steady checked.
1159
1161
  POSTING_MODE_PRESETS = (
1160
- (None, "Default (cycle setting)"),
1161
1162
  ("high", "Aggressive (100+ posts/day)"),
1162
1163
  ("medium", "Steady (~30 posts/day)"),
1163
1164
  ("low", "Chill (~5 posts/day)"),
@@ -1188,11 +1189,11 @@ class S4LMenuBar(rumps.App):
1188
1189
  sys.stderr.flush()
1189
1190
 
1190
1191
  def _posting_mode_label(self, mode):
1191
- """Menu copy for a posting-volume mode value."""
1192
+ """Menu copy for a posting-volume mode value (unknown/unset = Steady)."""
1192
1193
  for preset, label in self.POSTING_MODE_PRESETS:
1193
1194
  if preset == mode:
1194
1195
  return label
1195
- return "Default (cycle setting)"
1196
+ return "Steady (~30 posts/day)"
1196
1197
 
1197
1198
  def _on_posting_mode_preset(self, mode, _sender=None):
1198
1199
  """Set the server-side posting-volume mode (network POST; a click may
@@ -2213,6 +2214,14 @@ class S4LMenuBar(rumps.App):
2213
2214
  ):
2214
2215
  label = self._posting_label
2215
2216
  if label:
2217
+ # Menu bar stays CONCISE (user rule 2026-07-14): strip the trailing
2218
+ # elapsed-duration token the producer encodes in the label ("draft
2219
+ # 2m", "draft ⧖18m") from the TITLE only. The full label (with the
2220
+ # duration the stall detector's _label_elapsed_secs parses) still
2221
+ # lives in activity.json — this is display-side trimming, never a
2222
+ # change to the data.
2223
+ import re as _re
2224
+ label = _re.sub(r"\s*⧖?\s*\d+\s*[sm]\s*$", "", str(label)).strip() or label
2216
2225
  # The update arrow must stay visible even while a tool runs, so the
2217
2226
  # "update available" signal is never masked by activity. _tick skips the
2218
2227
  # title repaint while the spinner owns it, so the arrow is injected here.
@@ -3571,10 +3580,10 @@ class S4LMenuBar(rumps.App):
3571
3580
  items.append(cadence_menu)
3572
3581
 
3573
3582
  # Posting volume: server-side per-install throttle (virality bar).
3574
- # read_posting_mode() is cached and non-blocking; until the first
3575
- # refresh lands the submenu shows Default checked, then corrects on
3576
- # the next rebuild.
3577
- pv_mode = st.read_posting_mode()
3583
+ # read_posting_mode() is cached and non-blocking; unknown/unset shows
3584
+ # as Steady (the server resolves unset the same way), corrected on the
3585
+ # rebuild after the first refresh lands.
3586
+ pv_mode = st.read_posting_mode() or "medium"
3578
3587
  pv_menu = rumps.MenuItem(
3579
3588
  f"Posting volume: {self._posting_mode_label(pv_mode).split(' (')[0].lower()}"
3580
3589
  )
package/mcp/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m13v/s4l-mcp",
3
- "version": "1.7.5-rc.7",
3
+ "version": "1.7.5-rc.9",
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m13v/s4l",
3
- "version": "1.7.5-rc.7",
3
+ "version": "1.7.5-rc.9",
4
4
  "description": "Automated social posting pipeline for Reddit, X/Twitter, LinkedIn, and Moltbook. Install as a Claude Code agent skill.",
5
5
  "bin": {
6
6
  "social-autoposter": "bin/cli.js",
@@ -88,7 +88,10 @@ TYPE_TO_ACTIVITY = {
88
88
  "invent-topic": ("learning", "new topic"),
89
89
  "invent-queries": ("learning", "new queries"),
90
90
  "twitter-link-tail": ("drafting", "link bridge"),
91
- "reddit-draft": ("drafting", "reddit draft"),
91
+ # Same bare "draft" label as twitter-prep ON PURPOSE: the menu bar title
92
+ # must stay concise (user rule 2026-07-14); the platform shows on the
93
+ # review card itself, not in the tray.
94
+ "reddit-draft": ("drafting", "draft"),
92
95
  }
93
96
 
94
97
  # queue type -> execution notes PREPENDED to the prompt sidecar at claim time.
package/scripts/stats.py CHANGED
@@ -441,6 +441,26 @@ def fetch_reddit_json(url, user_agent, max_retries=2, timeout=15):
441
441
  (success AND error) into _reddit_rate_state so the caller can pace.
442
442
  On 429, honors Retry-After (capped to 120s) and retries.
443
443
  """
444
+ # 2026-07-14 transport fix: Reddit started 403-blocking plain urllib on
445
+ # *.json (2026-05-28), which silently killed this scan (every poll came
446
+ # back as an HTML block page -> 'empty'/'error', zero rows patched since).
447
+ # Route through the logged-in harness browser first, the same transport
448
+ # reddit_tools._do_request has used since 2026-05-29 (the replies pass in
449
+ # this file already goes through it via batch_fetch_info and kept working).
450
+ # urllib below stays as the fallback; REDDIT_FETCH_BACKEND=urllib forces it.
451
+ if os.environ.get("REDDIT_FETCH_BACKEND", "harness").lower() != "urllib":
452
+ try:
453
+ from reddit_browser_fetch import browser_get_json
454
+ body, code = browser_get_json(url)
455
+ if code == 200 and body:
456
+ try:
457
+ return ("ok", json.loads(body))
458
+ except Exception:
459
+ pass # non-JSON body -> fall through to urllib
460
+ elif code == 404:
461
+ return ("not_found", None)
462
+ except Exception:
463
+ pass
444
464
  req = urllib.request.Request(url, headers={"User-Agent": user_agent})
445
465
  for attempt in range(max_retries + 1):
446
466
  try: