Files
EveryPublish/client/core/main.go
T

174 lines
4.8 KiB
Go

// agent-core 客户端核心:设备身份 + 凭据保险库 + WSS 长连接 + 平台执行器。
// 常驻模型:WinUI 壳 spawn 本进程 → 壳读 data/local.json(随机端口+token)→
// 经 localhost HTTP 控制(状态/配对/挑战)。CLI 亦可独立运行(-user/-pass 自动配对)。
package main
import (
"context"
"errors"
"flag"
"log"
"os"
"os/signal"
"path/filepath"
"strings"
"sync"
"syscall"
"everypublish/agentcore/internal/config"
"everypublish/agentcore/internal/device"
"everypublish/agentcore/internal/exec"
"everypublish/agentcore/internal/exec/bilibili"
"everypublish/agentcore/internal/exec/douyin"
"everypublish/agentcore/internal/exec/fake"
"everypublish/agentcore/internal/exec/kuaishou"
"everypublish/agentcore/internal/exec/xiaohongshu"
"everypublish/agentcore/internal/localserver"
"everypublish/agentcore/internal/pairing"
"everypublish/agentcore/internal/vault"
"everypublish/agentcore/internal/wsclient"
)
func main() {
var (
server = flag.String("server", "", "服务器地址(默认取配置)")
dir = flag.String("dir", "./data", "数据目录(设备密钥/保险库/配置)")
name = flag.String("name", "", "设备名")
user = flag.String("user", "", "开发便利:自动配对登录邮箱")
password = flag.String("pass", "", "开发便利:自动配对登录密码")
simMS = flag.Int("sim", 0, "假执行器模拟耗时 ms(0 用配置值)")
token = flag.String("local-token", "", "本机控制服务 token(默认随机)")
)
flag.Parse()
cfg, err := config.Load(*dir)
if err != nil {
log.Fatalf("config: %v", err)
}
if *server != "" {
cfg.Server = *server
}
if *name != "" {
cfg.Name = *name
}
if *simMS > 0 {
cfg.SimMS = *simMS
}
_ = cfg.Save(*dir)
// 设备身份(私钥本机持久化 0600)
id, err := device.LoadOrCreate(*dir, cfg.Name)
if err != nil {
log.Fatalf("device: %v", err)
}
cfg.Name = id.Name
// 凭据保险库(平台 cookie/token 加密落盘)
v, err := vault.Open(*dir)
if err != nil {
log.Fatalf("vault: %v", err)
}
// 执行器注册:B站无浏览器投稿 + 抖音/快手/小红书 CDP(D10/D11);
// fakeAll=true 时全部走假执行器(无真实账号的安装联调)。
registry := exec.NewRegistry()
if !cfg.FakeAll {
browserRoot := filepath.Join(*dir, "browser") // 每账号独立档案
registry.Register(bilibili.New(v))
registry.Register(douyin.New(v, browserRoot, cfg.Proxy, "", false))
registry.Register(kuaishou.New(v, browserRoot, cfg.Proxy, "", false))
registry.Register(xiaohongshu.New(v, browserRoot, cfg.Proxy, "", false))
}
registry.SetFallback(fake.Executor{SimMS: cfg.SimMS})
// WSS 客户端(重连/幂等/心跳)
client := wsclient.New(cfg.Server, id, registry)
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
// 连接启动(配对后;未配对时等壳调 /pair)
var runOnce sync.Once
startConn := func() {
if id.DeviceID == 0 {
return
}
runOnce.Do(func() {
go func() { _ = client.Run(ctx) }()
})
}
// 配对回调(本机控制服务 /pair 触发)
pairFn := func(code, name string) error {
if id.DeviceID > 0 {
return errors.New("设备已配对")
}
name = strings.TrimSpace(name)
if name == "" {
name = cfg.Name
}
if name == "" {
name, _ = os.Hostname()
}
if name == "" {
name = "macOS Agent"
}
did, err := pairing.PairWithCode(cfg.Server, code, name, "core", "0.2.0", id.PublicKey)
if err != nil {
return err
}
id.DeviceID = did
if name != "" {
id.Name = name
cfg.Name = name
}
if err = id.Save(*dir); err != nil {
return err
}
log.Printf("paired device %d (name %s)", id.DeviceID, id.Name)
startConn()
return nil
}
// 开发便利:CLI 自动配对
if id.DeviceID == 0 && *user != "" && *password != "" {
did, err := pairing.AutoPair(cfg.Server, *user, *password, cfg.Name, id.PublicKey)
if err != nil {
log.Fatalf("auto pair: %v", err)
}
id.DeviceID = did
if err = id.Save(*dir); err != nil {
log.Fatalf("save identity: %v", err)
}
log.Printf("auto paired device %d", id.DeviceID)
}
startConn()
// 本机控制服务(WinUI 壳入口)
localToken := *token
if localToken == "" {
localToken = localserver.RandToken()
}
ls := localserver.New(client, pairFn, localToken, *dir, func() map[string]interface{} {
return map[string]interface{}{
"server": cfg.Server,
"deviceId": id.DeviceID,
"name": id.Name,
}
})
go func() {
if err = ls.Listen(); err != nil {
log.Fatalf("local server: %v", err)
}
}()
if id.DeviceID == 0 {
log.Printf("agent-core started UNPAIRED: server=%s(请在客户端输入网页端配对码)", cfg.Server)
} else {
log.Printf("agent-core started: server=%s device=%d", cfg.Server, id.DeviceID)
}
<-ctx.Done()
log.Println("agent-core stopped")
}