TaskAgent is a Go 1.24 module that keeps Android capture devices busy by polling Feishu Bitable tasks, dispatching them through a pluggable job runner, and feeding status back to Feishu. The library is designed to be embedded in internal agents or schedulers that already know how to execute tasks once a device serial and payload are provided.
- Feishu-native task source –
feishupackage parses Bitable links, fetches pending rows, and updates statuses without duplicating schemas in code. - Device pool orchestration –
pool.DevicePoolAgentrefreshes connected devices, batches tasks, and invokes yourJobRunnerimplementation with cancelable contexts. - Provider & manager interfaces – swap in custom
DeviceProviderorTaskManagerimplementations while the defaults cover ADB devices and Feishu queues. - Lean dependency stack – relies on
httprunner/v5'sgadb,zerolog, and the official Lark Open Platform SDK only when needed. - Lifecycle callbacks –
TaskLifecycleemitsOnTaskStarted(pending/dispatched → running) andOnTaskResult(running → success/failed) so Feishu status rows and device tables stay consistent without business code rewiring.
pool.go/pool_test.go– device scheduling loop, job lifecycle hooks, and unit coverage.tasks.go– Feishu-backedTaskManagerplus helpers for query filters and status updates.feishu/– API client, Bitable schema structs, spreadsheet helpers, and dedicated README for module-level details.providers/adb/– thin wrapper overgadbthat lists available Android serial numbers.AGENTS.md– contributor-focused workflows, coding standards, and security guardrails.
- Go 1.24 or newer (
go env GOVERSIONto verify). - A Feishu custom app with API access plus the following environment variables:
FEISHU_APP_ID,FEISHU_APP_SECRET, optionalFEISHU_TENANT_KEY,FEISHU_BASE_URL, andFEISHU_LIVE_TEST(toggles integration tests). - A Feishu task Bitable that stores pending tasks (surface its link via
TASK_BITABLE_URLor inject it directly intopool.Config.BitableURL). - Optional observability tables:
DEVICE_BITABLE_URL(设备信息表) andDEVICE_TASK_BITABLE_URL(设备任务表) for recording device heartbeat/dispatch snapshots. Leave empty to disable recording. Column names are customizable viaDEVICE_FIELD_*/DEVICE_TASK_FIELD_*; defaults match the sample schemas below. - Result write throttling: Feishu结果表写入内置全局限速器,默认 1 RPS(可通过
FEISHU_REPORT_RPS配置浮点值),避免多设备并发写表触发频控。 - Access to the result Bitables you plan to push to, if any.
- Android Debug Bridge (ADB) on the PATH when using the default provider.
- Clone the repository and install dependencies:
git clone [email protected]:httprunner/TaskAgent.git cd TaskAgent go mod download
- Create a
.envfile (loaded bygodotenv) and populate Feishu credentials plus Bitable URLs such asTASK_BITABLE_URL. - Run tests to validate the setup:
go test ./... # Run Feishu live tests only when you have real tables configured FEISHU_LIVE_TEST=1 go test ./feishu -run Live
- Embed the agent by implementing
pool.JobRunner(import the module with an alias to match its package name):If you configurepackage main import ( "context" "log" "os" "time" pool "github.com/httprunner/TaskAgent" "github.com/httprunner/TaskAgent/pkg/feishu" ) type MyRunner struct{} var _ pool.JobRunner = (*MyRunner)(nil) func (MyRunner) RunJob(ctx context.Context, req pool.JobRequest) error { // Operate on req.DeviceSerial and req.Tasks. for _, task := range req.Tasks { if req.Lifecycle != nil && req.Lifecycle.OnTaskStarted != nil { req.Lifecycle.OnTaskStarted(task) // optional if you need custom side effects } // TODO: execute payload... if req.Lifecycle != nil && req.Lifecycle.OnTaskResult != nil { req.Lifecycle.OnTaskResult(task, nil) } } return nil } func main() { cfg := pool.Config{ PollInterval: 30 * time.Second, MaxTasksPerJob: 2, BitableURL: os.Getenv(feishu.EnvTaskBitableURL), DeviceRecorder: mustRecorder, // optional: write device info / job rows to Feishu AgentVersion: "v0.0.0", // propagate to recorder rows } if cfg.BitableURL == "" { log.Fatal("set TASK_BITABLE_URL before starting the pool agent") } runner := &MyRunner{} agent, err := pool.NewDevicePoolAgent(cfg, runner) if err != nil { log.Fatal(err) } ctx, cancel := context.WithCancel(context.Background()) defer cancel() if err := agent.Start(ctx, "capture-app"); err != nil { log.Fatal(err) } }
DEVICE_BITABLE_URL/DEVICE_TASK_BITABLE_URL, the pool will upsert device heartbeats (Status/LastSeenAt) and create one row per dispatch (JobID${serial}-YYMMDDHHMM, AssignedTasks, Start/End, State, ErrorMessage). Leave the URLs empty to skip recording.MyRunnermust satisfypool.JobRunnerso the agent can callRunJobper device batch; decode the Feishu payload fromreq.Tasks[n].Payload. Pass theappargument that matches the Feishu target-tableAppcolumn so the built-inFeishuTaskClientfilters and updates the correct rows (statuses transition throughdispatched,success, andfailed).
TaskAgent keeps Feishu task rows and device snapshots in sync via a four-step lifecycle:
pending / failed (Feishu filter)
↓ (FetchAvailableTasks)
dispatched (OnTasksDispatched)
↓ (TaskLifecycle.OnTaskStarted)
running + device RunningTask/PendingTasks updated
↓ (TaskLifecycle.OnTaskResult + OnTasksCompleted)
success / failed
TaskLifecycle.OnTaskStartedfires the moment aJobRunnerbegins a specific task; the defaultFeishuTaskClientimplementation writes therunningstatus, while the built-in recorder updates the device table so a TaskID never appears in bothRunningTaskandPendingTasks.TaskLifecycle.OnTaskResultfires right after each task finishes. DevicePoolAgent setstask.ResultStatus = success/failedbased on the error and later callsTaskManager.OnTasksCompletedso Feishu reflects the final state.- Device recorder hooks (
pkg/devrecorder) continue to upsert device heartbeats (idle/running/offline) and pending queues even if you disable Feishu tables (simply point the URLs to empty strings).
By centralizing these hooks in TaskAgent, embedded agents (such as fox search) no longer need to manage per-task status transitions themselves—plug in a JobRunner, and the lifecycle wiring happens automatically.
- Format/lint:
go fmt ./...andgo vet ./...to keep style consistent. - Unit tests:
go test ./...covers pool orchestration and Feishu helpers via mocks. - Live integration: enable
FEISHU_LIVE_TEST=1before runninggo test ./feishu -run Live; these tests will touch real Bitables, so only run them in disposable environments.
- Contributor guide: see
AGENTS.mdfor coding standards, testing expectations, and security notes. - Feishu integration:
feishu/README.mddetails schema expectations, sample payloads, and troubleshooting steps. - Issues & ideas: open a GitHub issue with logs (scrub secrets) and the command/output you ran (
go test,go build, etc.).