locoagent-social-media-automation

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

LocoAgent Social Media Automation

LocoAgent 社交媒体自动化

Skill by ara.so — AI Agent Skills collection.
LocoAgent is an AI-powered social media agent that autonomously operates social media accounts through real browser automation. It combines an LLM-driven agentic loop with Chrome DevTools Protocol (CDP) to perceive, decide, and act on live web pages — performing tasks like liking posts, writing replies, following users, and publishing content.
Key capabilities:
  • Real browser automation with Chrome CDP (uses actual login sessions)
  • Platform skill system (32+ operations for X.com built-in)
  • Workflow engine for deterministic automation pipelines
  • Operation log for persistent deduplication across sessions
  • Multi-provider LLM support (OpenRouter, DeepSeek, Ollama, etc.)
ara.so提供的Skill — AI Agent技能合集。
LocoAgent是一款基于AI的社交媒体Agent,通过真实浏览器自动化自主运营社交媒体账号。它将LLM驱动的智能循环与Chrome DevTools Protocol (CDP)相结合,能够感知、决策并操作实时网页,执行点赞帖子、撰写回复、关注用户、发布内容等任务。
核心功能:
  • 基于Chrome CDP的真实浏览器自动化(使用实际登录会话)
  • 平台技能系统(内置32+种X.com操作)
  • 用于确定性自动化流水线的工作流引擎
  • 跨会话持久去重的操作日志
  • 多提供商LLM支持(OpenRouter、DeepSeek、Ollama等)

Installation

安装步骤

Prerequisites

前置条件

Install required dependencies:
bash
undefined
安装所需依赖:
bash
undefined

Install Bun runtime

Install Bun runtime

curl -fsSL https://bun.sh/install | bash
curl -fsSL https://bun.sh/install | bash

Install agent-browser CLI

Install agent-browser CLI

npm install -g @vercel/agent-browser
undefined
npm install -g @vercel/agent-browser
undefined

Project Setup

项目设置

bash
git clone https://github.com/LocoreMind/locoagent.git
cd locoagent
bun install
bash
git clone https://github.com/LocoreMind/locoagent.git
cd locoagent
bun install

Configuration

配置

Create
.env
file in project root:
env
undefined
在项目根目录创建
.env
文件:
env
undefined

OpenRouter (recommended - access 200+ models)

OpenRouter (recommended - access 200+ models)

CLAUDE_CODE_USE_OPENAI=1 OPENAI_API_KEY=sk-or-v1-... OPENAI_BASE_URL=https://openrouter.ai/api/v1 OPENAI_MODEL=anthropic/claude-sonnet-4.5
CLAUDE_CODE_USE_OPENAI=1 OPENAI_API_KEY=sk-or-v1-... OPENAI_BASE_URL=https://openrouter.ai/api/v1 OPENAI_MODEL=anthropic/claude-sonnet-4.5

Required for automated mode

Required for automated mode

SKIP_PERMISSIONS=1

Alternative provider configurations:

```env
SKIP_PERMISSIONS=1

其他提供商配置示例:

```env

DeepSeek (with thinking mode)

DeepSeek (with thinking mode)

CLAUDE_CODE_USE_OPENAI=1 OPENAI_API_KEY=<DEEPSEEK_API_KEY> OPENAI_BASE_URL=https://api.deepseek.com OPENAI_MODEL=deepseek-v4-flash
CLAUDE_CODE_USE_OPENAI=1 OPENAI_API_KEY=<DEEPSEEK_API_KEY> OPENAI_BASE_URL=https://api.deepseek.com OPENAI_MODEL=deepseek-v4-flash

Ollama (local models)

Ollama (local models)

CLAUDE_CODE_USE_OPENAI=1 OPENAI_API_KEY=ollama OPENAI_BASE_URL=http://localhost:11434/v1 OPENAI_MODEL=llama3.2
CLAUDE_CODE_USE_OPENAI=1 OPENAI_API_KEY=ollama OPENAI_BASE_URL=http://localhost:11434/v1 OPENAI_MODEL=llama3.2

Anthropic direct (native SDK)

Anthropic direct (native SDK)

ANTHROPIC_API_KEY=<ANTHROPIC_API_KEY>
undefined
ANTHROPIC_API_KEY=<ANTHROPIC_API_KEY>
undefined

Browser Setup

浏览器设置

bash
undefined
bash
undefined

One-time: copy Chrome profile and launch with CDP

One-time: copy Chrome profile and launch with CDP

bun run setup-chrome
bun run setup-chrome

Connect agent-browser to running Chrome

Connect agent-browser to running Chrome

agent-browser connect 9222
undefined
agent-browser connect 9222
undefined

Core Commands

核心命令

Interactive Mode

交互模式

bash
undefined
bash
undefined

Start interactive session

Start interactive session

bun start
bun start

Load X.com skill and execute task

Load X.com skill and execute task

/x-com open home timeline, like first 3 posts about AI
/x-com open home timeline, like first 3 posts about AI

Check operation history

Check operation history

/operation-log recent --limit 20
undefined
/operation-log recent --limit 20
undefined

Headless Mode

无头模式

bash
undefined
bash
undefined

Single query execution

Single query execution

bun start -p "open X.com and like the first post about AI agents"
bun start -p "open X.com and like the first post about AI agents"

With specific model

With specific model

bun start --model anthropic/claude-sonnet-4.5 -p "/x-com like 5 posts about LLMs"
bun start --model anthropic/claude-sonnet-4.5 -p "/x-com like 5 posts about LLMs"

Platform-specific task

Platform-specific task

bun start -p "/x-com like 5 posts about 'large language models', then follow the authors"
undefined
bun start -p "/x-com like 5 posts about 'large language models', then follow the authors"
undefined

Platform Skills

平台技能

Skills inject complete operation playbooks into the agent's context.
技能会将完整的操作手册注入Agent的上下文。

X.com Skill

X.com技能

bash
undefined
bash
undefined

Interactive

Interactive

/x-com open home timeline, like first 3 posts about AI, reply to the best one
/x-com open home timeline, like first 3 posts about AI, reply to the best one

Headless

Headless

bun start -p "/x-com like 5 posts about 'machine learning', follow authors with >1k followers"

Available X.com operations (32+):
- Navigation: home, notifications, messages, profile, search
- Engagement: like, retweet, reply, quote tweet
- Social graph: follow, unfollow, mute, block
- Content: post tweet, post thread, upload media
- Profile: edit bio, change avatar, update banner
- Lists: create, add members, view
bun start -p "/x-com like 5 posts about 'machine learning', follow authors with >1k followers"

可用的X.com操作(32+种):
- 导航:首页、通知、消息、个人主页、搜索
- 互动:点赞、转发、回复、引用转发
- 社交关系:关注、取消关注、静音、屏蔽
- 内容:发布推文、发布推文线程、上传媒体
- 个人资料:编辑简介、更换头像、更新横幅
- 列表:创建、添加成员、查看

Creating Custom Skills

创建自定义技能

Create
skills/linkedin/SKILL.md
:
markdown
---
description: "LinkedIn platform operations playbook"
allowed-tools:
  - Bash
user-invocable: true
---
创建
skills/linkedin/SKILL.md
markdown
---
description: "LinkedIn platform operations playbook"
allowed-tools:
  - Bash
user-invocable: true
---

LinkedIn Operations

LinkedIn Operations

1. Navigation

1. Navigation

Open Home Feed

Open Home Feed

bash
agent-browser open https://www.linkedin.com/feed
bash
agent-browser open https://www.linkedin.com/feed

Search Posts

Search Posts

bash
agent-browser open "https://www.linkedin.com/search/results/content/?keywords=AI%20agents"
agent-browser snapshot -i -c -s 'div[data-post-id]'
bash
agent-browser open "https://www.linkedin.com/search/results/content/?keywords=AI%20agents"
agent-browser snapshot -i -c -s 'div[data-post-id]'

2. Engagement

2. Engagement

Like Post

Like Post

  1. Find post element with
    agent-browser snapshot -i
  2. Locate like button (usually
    button[aria-label*="Like"]
    )
  3. Click:
    agent-browser click @e<ref>
  1. Find post element with
    agent-browser snapshot -i
  2. Locate like button (usually
    button[aria-label*="Like"]
    )
  3. Click:
    agent-browser click @e<ref>

Comment on Post

Comment on Post

  1. Find comment input (usually
    div[role="textbox"]
    )
  2. Click to focus:
    agent-browser click @e<ref>
  3. Type comment:
    agent-browser fill @e<ref> "Insightful post!"
  4. Find submit button and click

Load the skill:

```bash
bun start
> /linkedin search for posts about 'AI safety', like top 3
  1. Find comment input (usually
    div[role="textbox"]
    )
  2. Click to focus:
    agent-browser click @e<ref>
  3. Type comment:
    agent-browser fill @e<ref> "Insightful post!"
  4. Find submit button and click

加载技能:

```bash
bun start
> /linkedin search for posts about 'AI safety', like top 3

Workflow Engine

工作流引擎

Workflows are deterministic browser-automation pipelines that run without LLM involvement.
工作流是无需LLM参与即可运行的确定性浏览器自动化流水线。

Built-in Workflows

内置工作流

bash
undefined
bash
undefined

List all workflows

List all workflows

bun run workflow list
bun run workflow list

Run once (blocking)

Run once (blocking)

bun run workflow run --id hf-papers-to-x
bun run workflow run --id hf-papers-to-x

Run once (background)

Run once (background)

bun run workflow start --id hf-papers-to-x
bun run workflow start --id hf-papers-to-x

Daemon mode (every 3 minutes)

Daemon mode (every 3 minutes)

bun run workflow daemon --id x-search-reply --interval 3
bun run workflow daemon --id x-search-reply --interval 3

Stop workflow

Stop workflow

bun run workflow stop --id x-search-reply
bun run workflow stop --id x-search-reply

View status

View status

bun run workflow status
bun run workflow status

View execution history

View execution history

bun run workflow history --id hf-papers-to-x
undefined
bun run workflow history --id hf-papers-to-x
undefined

Creating Custom Workflows

创建自定义工作流

Step 1: Create workflow definition
workflows/linkedin-engagement.json
:
json
{
  "id": "linkedin-engagement",
  "name": "LinkedIn Daily Engagement",
  "description": "Search for AI posts on LinkedIn and engage",
  "schedule": "daily",
  "executor": "executors/linkedin-engagement.ts",
  "config": {
    "searchQuery": "artificial intelligence",
    "maxPosts": 5,
    "cdpPort": 9222
  }
}
Step 2: Create executor
workflows/executors/linkedin-engagement.ts
:
typescript
#!/usr/bin/env bun
import { execSync } from 'node:child_process'

// Parse config from workflow engine
const configArg = process.argv.find((_, i, a) => a[i - 1] === '--config')
const config = JSON.parse(configArg!)

// agent-browser helper
function ab(cmd: string): string {
  return execSync(`agent-browser --cdp ${config.cdpPort} ${cmd}`, {
    encoding: 'utf-8', 
    timeout: 30000,
  }).trim()
}

// Helper to check operation log
function hasEngaged(postUrl: string): boolean {
  try {
    execSync(`bun run scripts/log-operation.ts check --platform linkedin --action like --url "${postUrl}"`, {
      encoding: 'utf-8',
      stdio: 'ignore'
    })
    return true // exit 0 = already done
  } catch {
    return false // exit 1 = not done
  }
}

// Helper to log operation
function logOperation(postUrl: string, action: string, status: string, note: string) {
  execSync(`bun run scripts/log-operation.ts add --platform linkedin --action ${action} --url "${postUrl}" --status ${status} --note "${note}"`, {
    encoding: 'utf-8',
    stdio: 'inherit'
  })
}

console.error('[linkedin-engagement] Starting workflow...')

// Step 1: Navigate to search
console.error(`[linkedin-engagement] Searching for: ${config.searchQuery}`)
const searchUrl = `https://www.linkedin.com/search/results/content/?keywords=${encodeURIComponent(config.searchQuery)}`
ab(`open "${searchUrl}"`)
ab('wait 3000')

// Step 2: Get posts
console.error('[linkedin-engagement] Getting posts...')
const snapshot = ab('snapshot -i -c -s \'div[data-post-id]\'')
const posts = JSON.parse(snapshot)

let engaged = 0
const stepsTotal = Math.min(posts.length, config.maxPosts)

// Step 3: Engage with posts
for (let i = 0; i < stepsTotal; i++) {
  const post = posts[i]
  const postUrl = post.attributes?.['data-urn'] || `post-${i}`
  
  // Check if already engaged
  if (hasEngaged(postUrl)) {
    console.error(`[linkedin-engagement] Already engaged with ${postUrl}, skipping`)
    continue
  }
  
  // Find like button
  const likeButton = post.children?.find((el: any) => 
    el.attributes?.['aria-label']?.includes('Like')
  )
  
  if (likeButton?.ref) {
    ab(`click ${likeButton.ref}`)
    logOperation(postUrl, 'like', 'success', `Workflow: ${config.searchQuery}`)
    engaged++
    console.error(`[linkedin-engagement] Liked post ${i + 1}/${stepsTotal}`)
    ab('wait 2000') // Rate limiting
  }
}

// Output final summary (required)
console.log(JSON.stringify({ 
  stepsCompleted: engaged, 
  stepsTotal,
  searchQuery: config.searchQuery 
}))
Step 3: Run workflow:
bash
bun run workflow run --id linkedin-engagement
步骤1: 创建工作流定义
workflows/linkedin-engagement.json
json
{
  "id": "linkedin-engagement",
  "name": "LinkedIn Daily Engagement",
  "description": "Search for AI posts on LinkedIn and engage",
  "schedule": "daily",
  "executor": "executors/linkedin-engagement.ts",
  "config": {
    "searchQuery": "artificial intelligence",
    "maxPosts": 5,
    "cdpPort": 9222
  }
}
步骤2: 创建执行器
workflows/executors/linkedin-engagement.ts
typescript
#!/usr/bin/env bun
import { execSync } from 'node:child_process'

// Parse config from workflow engine
const configArg = process.argv.find((_, i, a) => a[i - 1] === '--config')
const config = JSON.parse(configArg!)

// agent-browser helper
function ab(cmd: string): string {
  return execSync(`agent-browser --cdp ${config.cdpPort} ${cmd}`, {
    encoding: 'utf-8', 
    timeout: 30000,
  }).trim()
}

// Helper to check operation log
function hasEngaged(postUrl: string): boolean {
  try {
    execSync(`bun run scripts/log-operation.ts check --platform linkedin --action like --url "${postUrl}"`, {
      encoding: 'utf-8',
      stdio: 'ignore'
    })
    return true // exit 0 = already done
  } catch {
    return false // exit 1 = not done
  }
}

// Helper to log operation
function logOperation(postUrl: string, action: string, status: string, note: string) {
  execSync(`bun run scripts/log-operation.ts add --platform linkedin --action ${action} --url "${postUrl}" --status ${status} --note "${note}"`, {
    encoding: 'utf-8',
    stdio: 'inherit'
  })
}

console.error('[linkedin-engagement] Starting workflow...')

// Step 1: Navigate to search
console.error(`[linkedin-engagement] Searching for: ${config.searchQuery}`)
const searchUrl = `https://www.linkedin.com/search/results/content/?keywords=${encodeURIComponent(config.searchQuery)}`
ab(`open "${searchUrl}"`)
ab('wait 3000')

// Step 2: Get posts
console.error('[linkedin-engagement] Getting posts...')
const snapshot = ab('snapshot -i -c -s \'div[data-post-id]\'')
const posts = JSON.parse(snapshot)

let engaged = 0
const stepsTotal = Math.min(posts.length, config.maxPosts)

// Step 3: Engage with posts
for (let i = 0; i < stepsTotal; i++) {
  const post = posts[i]
  const postUrl = post.attributes?.['data-urn'] || `post-${i}`
  
  // Check if already engaged
  if (hasEngaged(postUrl)) {
    console.error(`[linkedin-engagement] Already engaged with ${postUrl}, skipping`)
    continue
  }
  
  // Find like button
  const likeButton = post.children?.find((el: any) => 
    el.attributes?.['aria-label']?.includes('Like')
  )
  
  if (likeButton?.ref) {
    ab(`click ${likeButton.ref}`)
    logOperation(postUrl, 'like', 'success', `Workflow: ${config.searchQuery}`)
    engaged++
    console.error(`[linkedin-engagement] Liked post ${i + 1}/${stepsTotal}`)
    ab('wait 2000') // Rate limiting
  }
}

// Output final summary (required)
console.log(JSON.stringify({ 
  stepsCompleted: engaged, 
  stepsTotal,
  searchQuery: config.searchQuery 
}))
步骤3: 运行工作流:
bash
bun run workflow run --id linkedin-engagement

Operation Log

操作日志

Persistent memory prevents duplicate actions across sessions.
持久化记忆可防止跨会话重复执行操作。

Check Before Acting

操作前检查

typescript
import { execSync } from 'node:child_process'

function hasLiked(postUrl: string): boolean {
  try {
    execSync(`bun run scripts/log-operation.ts check --platform x --action like --url "${postUrl}"`, {
      encoding: 'utf-8',
      stdio: 'ignore'
    })
    return true // exit 0 = already done
  } catch {
    return false // exit 1 = not done
  }
}

const url = "https://x.com/user/status/123"
if (hasLiked(url)) {
  console.log("Already liked this post")
} else {
  // Perform like action
  execSync(`agent-browser click @e5`)
  
  // Log operation
  execSync(`bun run scripts/log-operation.ts add --platform x --action like --url "${url}" --status success --note "AI research post"`)
}
typescript
import { execSync } from 'node:child_process'

function hasLiked(postUrl: string): boolean {
  try {
    execSync(`bun run scripts/log-operation.ts check --platform x --action like --url "${postUrl}"`, {
      encoding: 'utf-8',
      stdio: 'ignore'
    })
    return true // exit 0 = already done
  } catch {
    return false // exit 1 = not done
  }
}

const url = "https://x.com/user/status/123"
if (hasLiked(url)) {
  console.log("Already liked this post")
} else {
  // Perform like action
  execSync(`agent-browser click @e5`)
  
  // Log operation
  execSync(`bun run scripts/log-operation.ts add --platform x --action like --url "${url}" --status success --note "AI research post"`)
}

CLI Operations

CLI操作

bash
undefined
bash
undefined

Check if operation was performed (exit 0 = done, exit 1 = not done)

Check if operation was performed (exit 0 = done, exit 1 = not done)

bun run scripts/log-operation.ts check
--platform x
--action like
--url "https://x.com/user/status/123"
bun run scripts/log-operation.ts check
--platform x
--action like
--url "https://x.com/user/status/123"

Record operation

Record operation

bun run scripts/log-operation.ts add
--platform x
--action like
--url "https://x.com/user/status/123"
--status success
--note "AI agents research post"
bun run scripts/log-operation.ts add
--platform x
--action like
--url "https://x.com/user/status/123"
--status success
--note "AI agents research post"

View recent operations

View recent operations

bun run scripts/log-operation.ts recent --limit 20
bun run scripts/log-operation.ts recent --limit 20

30-day summary

30-day summary

bun run scripts/log-operation.ts summary --days 30

State stored in `persona/operation-log.json`.
bun run scripts/log-operation.ts summary --days 30

状态存储在`persona/operation-log.json`中。

Task Scheduling

任务调度

Structure daily/weekly tasks instead of ad-hoc prompts.
将日常/每周任务结构化,而非使用临时提示词。

Define Tasks

定义任务

Edit
persona/tasks.md
:
markdown
undefined
编辑
persona/tasks.md
markdown
undefined

Daily Tasks

Daily Tasks

  1. Engage with AI research content (like 5-10 posts)
  2. Monitor project mentions and respond
  3. Leave 1-2 technical comments on relevant posts
  1. Engage with AI research content (like 5-10 posts)
  2. Monitor project mentions and respond
  3. Leave 1-2 technical comments on relevant posts

Weekly Tasks (Monday)

Weekly Tasks (Monday)

  1. Follow 3-5 relevant researchers or developers
  2. Post 1 original tweet about recent findings
  1. Follow 3-5 relevant researchers or developers
  2. Post 1 original tweet about recent findings

Session Constraints

Session Constraints

ActionMax per session
Likes10
Comments2
Follows5
Posts1
undefined
ActionMax per session
Likes10
Comments2
Follows5
Posts1
undefined

Run Tasks

运行任务

bash
undefined
bash
undefined

Execute today's tasks

Execute today's tasks

bun run run-tasks
bun run run-tasks

Preview prompt without running

Preview prompt without running

bun run run-tasks:dry
bun run run-tasks:dry

Restrict to one platform

Restrict to one platform

bun run run-tasks -- --platform x
undefined
bun run run-tasks -- --platform x
undefined

Real-time Trajectory Monitor

实时轨迹监控

Watch live execution status instead of black-box
--print
mode.
bash
undefined
无需使用黑盒
--print
模式,即可查看实时执行状态。
bash
undefined

Terminal 1: start monitor

Terminal 1: start monitor

bun run tail
bun run tail

Terminal 2: run agent

Terminal 2: run agent

bun start -p "/x-com open timeline, like first post"

Output shows live execution:
═══ New Task ═══ /x-com open timeline, like first post
[6:30:47 PM] ⚡ Bash: agent-browser connect 9222 [6:30:47 PM] ✓ Result: Done [6:31:10 PM] ⚡ Bash: agent-browser open https://x.com/home [6:31:27 PM] ⚡ Bash: agent-browser snapshot -i -c -s 'article' [6:31:44 PM] ● Agent: Found first post, like button ref=e136 [6:31:44 PM] ⚡ Bash: agent-browser click e136 [6:31:45 PM] ✓ Result: Done

Additional commands:

```bash
bun start -p "/x-com open timeline, like first post"

输出会显示实时执行过程:
═══ New Task ═══ /x-com open timeline, like first post
[6:30:47 PM] ⚡ Bash: agent-browser connect 9222 [6:30:47 PM] ✓ Result: Done [6:31:10 PM] ⚡ Bash: agent-browser open https://x.com/home [6:31:27 PM] ⚡ Bash: agent-browser snapshot -i -c -s 'article' [6:31:44 PM] ● Agent: Found first post, like button ref=e136 [6:31:44 PM] ⚡ Bash: agent-browser click e136 [6:31:45 PM] ✓ Result: Done

其他命令:

```bash

Replay latest session from beginning

Replay latest session from beginning

bun run tail:history
bun run tail:history

List recent sessions

List recent sessions

bun run tail:list
bun run tail:list

Watch specific session

Watch specific session

bun run tail <session-id>
undefined
bun run tail <session-id>
undefined

Common Patterns

常见模式

Pattern: Safe Engagement Loop

模式:安全互动循环

typescript
#!/usr/bin/env bun
import { execSync } from 'node:child_process'

function ab(cmd: string): string {
  return execSync(`agent-browser --cdp 9222 ${cmd}`, {
    encoding: 'utf-8',
    timeout: 30000,
  }).trim()
}

function hasEngaged(platform: string, action: string, url: string): boolean {
  try {
    execSync(`bun run scripts/log-operation.ts check --platform ${platform} --action ${action} --url "${url}"`, {
      stdio: 'ignore'
    })
    return true
  } catch {
    return false
  }
}

function logEngagement(platform: string, action: string, url: string, note: string) {
  execSync(`bun run scripts/log-operation.ts add --platform ${platform} --action ${action} --url "${url}" --status success --note "${note}"`, {
    stdio: 'inherit'
  })
}

// Navigate to page
ab('open https://x.com/search?q=AI%20agents&f=live')
ab('wait 3000')

// Get posts
const snapshot = JSON.parse(ab('snapshot -i -c -s \'article\''))
const posts = snapshot.slice(0, 5)

for (const post of posts) {
  const postUrl = post.attributes?.['data-testid'] || `post-${Math.random()}`
  
  // Skip if already engaged
  if (hasEngaged('x', 'like', postUrl)) {
    console.error(`Already liked ${postUrl}`)
    continue
  }
  
  // Find like button
  const likeBtn = post.children?.find((el: any) => 
    el.attributes?.['data-testid'] === 'like'
  )
  
  if (likeBtn?.ref) {
    ab(`click ${likeBtn.ref}`)
    logEngagement('x', 'like', postUrl, 'AI agents search result')
    ab('wait 2000') // Rate limiting
  }
}
typescript
#!/usr/bin/env bun
import { execSync } from 'node:child_process'

function ab(cmd: string): string {
  return execSync(`agent-browser --cdp 9222 ${cmd}`, {
    encoding: 'utf-8',
    timeout: 30000,
  }).trim()
}

function hasEngaged(platform: string, action: string, url: string): boolean {
  try {
    execSync(`bun run scripts/log-operation.ts check --platform ${platform} --action ${action} --url "${url}"`, {
      stdio: 'ignore'
    })
    return true
  } catch {
    return false
  }
}

function logEngagement(platform: string, action: string, url: string, note: string) {
  execSync(`bun run scripts/log-operation.ts add --platform ${platform} --action ${action} --url "${url}" --status success --note "${note}"`, {
    stdio: 'inherit'
  })
}

// Navigate to page
ab('open https://x.com/search?q=AI%20agents&f=live')
ab('wait 3000')

// Get posts
const snapshot = JSON.parse(ab('snapshot -i -c -s \'article\''))
const posts = snapshot.slice(0, 5)

for (const post of posts) {
  const postUrl = post.attributes?.['data-testid'] || `post-${Math.random()}`
  
  // Skip if already engaged
  if (hasEngaged('x', 'like', postUrl)) {
    console.error(`Already liked ${postUrl}`)
    continue
  }
  
  // Find like button
  const likeBtn = post.children?.find((el: any) => 
    el.attributes?.['data-testid'] === 'like'
  )
  
  if (likeBtn?.ref) {
    ab(`click ${likeBtn.ref}`)
    logEngagement('x', 'like', postUrl, 'AI agents search result')
    ab('wait 2000') // Rate limiting
  }
}

Pattern: Multi-Step Workflow with Checkpoints

模式:带检查点的多步骤工作流

typescript
#!/usr/bin/env bun
import { execSync } from 'node:child_process'
import { writeFileSync, readFileSync, existsSync } from 'fs'

const CHECKPOINT_FILE = '/tmp/workflow-checkpoint.json'

function loadCheckpoint(): any {
  if (existsSync(CHECKPOINT_FILE)) {
    return JSON.parse(readFileSync(CHECKPOINT_FILE, 'utf-8'))
  }
  return { step: 0, data: {} }
}

function saveCheckpoint(step: number, data: any) {
  writeFileSync(CHECKPOINT_FILE, JSON.stringify({ step, data }))
}

const checkpoint = loadCheckpoint()
let currentStep = checkpoint.step

// Step 1: Fetch data
if (currentStep === 0) {
  console.error('[workflow] Step 1: Fetching data...')
  const data = { papers: ['paper1', 'paper2', 'paper3'] }
  saveCheckpoint(1, data)
  currentStep = 1
}

// Step 2: Process data
if (currentStep === 1) {
  console.error('[workflow] Step 2: Processing data...')
  const { data } = loadCheckpoint()
  // Process papers
  saveCheckpoint(2, { ...data, processed: true })
  currentStep = 2
}

// Step 3: Post to social
if (currentStep === 2) {
  console.error('[workflow] Step 3: Posting to social...')
  const { data } = loadCheckpoint()
  // Post each paper
  saveCheckpoint(3, data)
  currentStep = 3
}

// Cleanup checkpoint on success
if (existsSync(CHECKPOINT_FILE)) {
  execSync(`rm ${CHECKPOINT_FILE}`)
}

console.log(JSON.stringify({ stepsCompleted: 3, stepsTotal: 3 }))
typescript
#!/usr/bin/env bun
import { execSync } from 'node:child_process'
import { writeFileSync, readFileSync, existsSync } from 'fs'

const CHECKPOINT_FILE = '/tmp/workflow-checkpoint.json'

function loadCheckpoint(): any {
  if (existsSync(CHECKPOINT_FILE)) {
    return JSON.parse(readFileSync(CHECKPOINT_FILE, 'utf-8'))
  }
  return { step: 0, data: {} }
}

function saveCheckpoint(step: number, data: any) {
  writeFileSync(CHECKPOINT_FILE, JSON.stringify({ step, data }))
}

const checkpoint = loadCheckpoint()
let currentStep = checkpoint.step

// Step 1: Fetch data
if (currentStep === 0) {
  console.error('[workflow] Step 1: Fetching data...')
  const data = { papers: ['paper1', 'paper2', 'paper3'] }
  saveCheckpoint(1, data)
  currentStep = 1
}

// Step 2: Process data
if (currentStep === 1) {
  console.error('[workflow] Step 2: Processing data...')
  const { data } = loadCheckpoint()
  // Process papers
  saveCheckpoint(2, { ...data, processed: true })
  currentStep = 2
}

// Step 3: Post to social
if (currentStep === 2) {
  console.error('[workflow] Step 3: Posting to social...')
  const { data } = loadCheckpoint()
  // Post each paper
  saveCheckpoint(3, data)
  currentStep = 3
}

// Cleanup checkpoint on success
if (existsSync(CHECKPOINT_FILE)) {
  execSync(`rm ${CHECKPOINT_FILE}`)
}

console.log(JSON.stringify({ stepsCompleted: 3, stepsTotal: 3 }))

Troubleshooting

故障排查

Browser Connection Issues

浏览器连接问题

bash
undefined
bash
undefined

Check if Chrome is running with CDP

Check if Chrome is running with CDP

ps aux | grep chrome | grep remote-debugging-port
ps aux | grep chrome | grep remote-debugging-port

Kill existing Chrome and restart

Kill existing Chrome and restart

pkill -f chrome bun run setup-chrome
pkill -f chrome bun run setup-chrome

Verify CDP port is accessible

Verify CDP port is accessible

Operation Log Not Working

操作日志无法正常工作

bash
undefined
bash
undefined

Check log file exists and is readable

Check log file exists and is readable

cat persona/operation-log.json
cat persona/operation-log.json

Reset log if corrupted

Reset log if corrupted

echo '[]' > persona/operation-log.json
echo '[]' > persona/operation-log.json

Verify log script works

Verify log script works

bun run scripts/log-operation.ts recent --limit 5
undefined
bun run scripts/log-operation.ts recent --limit 5
undefined

Workflow Execution Fails

工作流执行失败

bash
undefined
bash
undefined

Check workflow status

Check workflow status

bun run workflow status
bun run workflow status

View detailed logs

View detailed logs

bun run workflow history --id <workflow-id>
bun run workflow history --id <workflow-id>

Run with debug output

Run with debug output

DEBUG=1 bun run workflow run --id <workflow-id>
DEBUG=1 bun run workflow run --id <workflow-id>

Check executor is executable

Check executor is executable

chmod +x workflows/executors/<executor>.ts
undefined
chmod +x workflows/executors/<executor>.ts
undefined

LLM Provider Errors

LLM提供商错误

bash
undefined
bash
undefined

Verify API key is set

Verify API key is set

echo $OPENAI_API_KEY
echo $OPENAI_API_KEY

Test connection

Test connection

curl -H "Authorization: Bearer $OPENAI_API_KEY"
$OPENAI_BASE_URL/models
curl -H "Authorization: Bearer $OPENAI_API_KEY"
$OPENAI_BASE_URL/models

Check model availability

Check model availability

bun start --model <model-name> -p "test"
undefined
bun start --model <model-name> -p "test"
undefined

Agent Not Finding Elements

Agent无法找到元素

bash
undefined
bash
undefined

Get detailed snapshot

Get detailed snapshot

agent-browser snapshot -i -c -s 'article' > snapshot.json
agent-browser snapshot -i -c -s 'article' > snapshot.json

Check element refs are valid

Check element refs are valid

cat snapshot.json | jq '.[] | .ref'
cat snapshot.json | jq '.[] | .ref'

Try broader selector

Try broader selector

agent-browser snapshot -i -c -s 'div'
agent-browser snapshot -i -c -s 'div'

Wait for page to load

Wait for page to load

agent-browser wait 5000 agent-browser snapshot -i
undefined
agent-browser wait 5000 agent-browser snapshot -i
undefined