diff --git a/client/core/go.mod b/client/core/go.mod new file mode 100644 index 0000000..a833738 --- /dev/null +++ b/client/core/go.mod @@ -0,0 +1,20 @@ +module everypublish/agentcore + +go 1.23 + +require ( + everypublish/shared v0.0.0 + github.com/coder/websocket v1.8.15 + github.com/go-rod/rod v0.116.2 + github.com/go-rod/stealth v0.4.9 +) + +require ( + github.com/ysmood/fetchup v0.2.3 // indirect + github.com/ysmood/goob v0.4.0 // indirect + github.com/ysmood/got v0.40.0 // indirect + github.com/ysmood/gson v0.7.3 // indirect + github.com/ysmood/leakless v0.9.0 // indirect +) + +replace everypublish/shared => ../../shared diff --git a/client/core/go.sum b/client/core/go.sum new file mode 100644 index 0000000..a672423 --- /dev/null +++ b/client/core/go.sum @@ -0,0 +1,24 @@ +github.com/coder/websocket v1.8.15 h1:6B2JPeOGlpff2Uz6vOEH1Vzpi0iUz20A+lPVhPHtNUA= +github.com/coder/websocket v1.8.15/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= +github.com/go-rod/rod v0.113.0/go.mod h1:aiedSEFg5DwG/fnNbUOTPMTTWX3MRj6vIs/a684Mthw= +github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA= +github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg= +github.com/go-rod/stealth v0.4.9 h1:X2PmQk4DUF2wzw6GOsWjW/glb8K5ebnftbEvLh7MlZ4= +github.com/go-rod/stealth v0.4.9/go.mod h1:eAzyvw8c0iAd5nJJsSWeh0fQ5z94vCIfdi1hUmYDimc= +github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ= +github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns= +github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= +github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= +github.com/ysmood/gop v0.0.2/go.mod h1:rr5z2z27oGEbyB787hpEcx4ab8cCiPnKxn0SUHt6xzk= +github.com/ysmood/gop v0.2.0 h1:+tFrG0TWPxT6p9ZaZs+VY+opCvHU8/3Fk6BaNv6kqKg= +github.com/ysmood/gop v0.2.0/go.mod h1:rr5z2z27oGEbyB787hpEcx4ab8cCiPnKxn0SUHt6xzk= +github.com/ysmood/got v0.34.1/go.mod h1:yddyjq/PmAf08RMLSwDjPyCvHvYed+WjHnQxpH851LM= +github.com/ysmood/got v0.40.0 h1:ZQk1B55zIvS7zflRrkGfPDrPG3d7+JOza1ZkNxcc74Q= +github.com/ysmood/got v0.40.0/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg= +github.com/ysmood/gotrace v0.6.0 h1:SyI1d4jclswLhg7SWTL6os3L1WOKeNn/ZtzVQF8QmdY= +github.com/ysmood/gotrace v0.6.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM= +github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE= +github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= +github.com/ysmood/leakless v0.8.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= +github.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU= +github.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= diff --git a/client/core/internal/config/config.go b/client/core/internal/config/config.go new file mode 100644 index 0000000..4ebea80 --- /dev/null +++ b/client/core/internal/config/config.go @@ -0,0 +1,45 @@ +package config + +import ( + "encoding/json" + "os" + "path/filepath" +) + +// Config 客户端配置(data/config.json) +type Config struct { + Server string `json:"server"` + DeviceID uint64 `json:"deviceId"` + Name string `json:"name"` + Proxy string `json:"proxy"` + SimMS int `json:"simMs"` + FakeAll bool `json:"fakeAll"` // 全平台模拟执行(无真实账号联调用) +} + +// Default 默认配置 +func Default() *Config { + return &Config{Server: "http://127.0.0.1:8090", SimMS: 50} +} + +// Load 读取配置(不存在则返回默认) +func Load(dir string) (*Config, error) { + cfg := Default() + raw, err := os.ReadFile(filepath.Join(dir, "config.json")) + if err != nil { + if os.IsNotExist(err) { + return cfg, nil + } + return nil, err + } + _ = json.Unmarshal(raw, cfg) + return cfg, nil +} + +// Save 持久化配置 +func (c *Config) Save(dir string) error { + if err := os.MkdirAll(dir, 0o700); err != nil { + return err + } + raw, _ := json.MarshalIndent(c, "", " ") + return os.WriteFile(filepath.Join(dir, "config.json"), raw, 0o600) +} diff --git a/client/core/internal/device/device.go b/client/core/internal/device/device.go new file mode 100644 index 0000000..56ce458 --- /dev/null +++ b/client/core/internal/device/device.go @@ -0,0 +1,72 @@ +package device + +import ( + "crypto/ed25519" + "crypto/rand" + "encoding/base64" + "encoding/hex" + "encoding/json" + "errors" + "os" + "path/filepath" +) + +// Identity 设备身份:Ed25519 密钥对 + 服务器分配的设备 ID +type Identity struct { + DeviceID uint64 `json:"deviceId"` + Name string `json:"name"` + PublicKey string `json:"publicKey"` + priv ed25519.PrivateKey `json:"-"` +} + +// LoadOrCreate 加载或生成本机设备身份(私钥 hex 文件 0600) +func LoadOrCreate(dir string, name string) (*Identity, error) { + keyPath := filepath.Join(dir, "device.key") + idPath := filepath.Join(dir, "identity.json") + if err := os.MkdirAll(dir, 0o700); err != nil { + return nil, err + } + var priv ed25519.PrivateKey + if raw, err := os.ReadFile(keyPath); err == nil { + seed, err := hex.DecodeString(string(raw)) + if err != nil || len(seed) != ed25519.SeedSize { + return nil, errors.New("device key corrupted") + } + priv = ed25519.NewKeyFromSeed(seed) + } else { + _, priv, err = ed25519.GenerateKey(rand.Reader) + if err != nil { + return nil, err + } + if err = os.WriteFile(keyPath, []byte(hex.EncodeToString(priv.Seed())), 0o600); err != nil { + return nil, err + } + } + id := &Identity{ + Name: name, + PublicKey: base64.StdEncoding.EncodeToString(priv.Public().(ed25519.PublicKey)), + priv: priv, + } + if raw, err := os.ReadFile(idPath); err == nil { + _ = json.Unmarshal(raw, id) + id.priv = priv + if id.Name == "" { + id.Name = name + } + } + return id, nil +} + +// Save 持久化设备 ID(配对成功后调用) +func (i *Identity) Save(dir string) error { + raw, err := json.MarshalIndent(i, "", " ") + if err != nil { + return err + } + return os.WriteFile(filepath.Join(dir, "identity.json"), raw, 0o600) +} + +// Sign 对载荷签名(hello 握手用) +func (i *Identity) Sign(payload []byte) string { + return base64.StdEncoding.EncodeToString(ed25519.Sign(i.priv, payload)) +} diff --git a/client/core/internal/device/device_test.go b/client/core/internal/device/device_test.go new file mode 100644 index 0000000..95be1f9 --- /dev/null +++ b/client/core/internal/device/device_test.go @@ -0,0 +1,38 @@ +package device + +import ( + "crypto/ed25519" + "encoding/base64" + "testing" +) + +func TestLoadOrCreatePersist(t *testing.T) { + dir := t.TempDir() + id1, err := LoadOrCreate(dir, "test-device") + if err != nil { + t.Fatalf("create: %v", err) + } + // 再次加载应得到同一公钥 + id2, err := LoadOrCreate(dir, "test-device") + if err != nil { + t.Fatalf("reload: %v", err) + } + if id1.PublicKey != id2.PublicKey { + t.Fatal("public key should persist across reloads") + } +} + +func TestSignVerify(t *testing.T) { + dir := t.TempDir() + id, _ := LoadOrCreate(dir, "d") + payload := []byte("nonce-1|123456") + sigB64 := id.Sign(payload) + sig, _ := base64.StdEncoding.DecodeString(sigB64) + pub, _ := base64.StdEncoding.DecodeString(id.PublicKey) + if !ed25519.Verify(ed25519.PublicKey(pub), payload, sig) { + t.Fatal("signature should verify") + } + if ed25519.Verify(ed25519.PublicKey(pub), []byte("tampered"), sig) { + t.Fatal("tampered payload should fail verify") + } +} diff --git a/client/core/internal/exec/bilibili/bilibili.go b/client/core/internal/exec/bilibili/bilibili.go new file mode 100644 index 0000000..16ce8d3 --- /dev/null +++ b/client/core/internal/exec/bilibili/bilibili.go @@ -0,0 +1,458 @@ +// bilibili B站执行器(无浏览器投稿 + API 扫码登录)。 +// 协议依据:ForgQi/biliup(MIT)bili_webup.py 的 upos/bda2 上传链路。 +package bilibili + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "everypublish/agentcore/internal/exec" + "everypublish/agentcore/internal/vault" + "everypublish/shared/proto" +) + +// Executor B站投稿执行器 +type Executor struct { + Vault *vault.Vault + HTTP *http.Client + MemberBase string // 投稿 API 基址(测试可替换) + PassportBase string // 登录 API 基址(测试可替换) + UpOSScheme string // upos 上传协议(默认 https,测试 http) + PollTimeout time.Duration // 扫码轮询总时长(默认 3 分钟) +} + +// New 构造执行器 +func New(v *vault.Vault) *Executor { + return &Executor{ + Vault: v, + HTTP: &http.Client{Timeout: 30 * time.Second}, + MemberBase: "https://member.bilibili.com", + PassportBase: "https://passport.bilibili.com", + UpOSScheme: "https", + PollTimeout: 3 * time.Minute, + } +} + +// Platform 平台标识 +func (e *Executor) Platform() string { return "bilibili" } + +// credentials 凭据(保险库 JSON) +type credentials struct { + Cookies string `json:"cookies"` +} + +// Execute 发布:下载素材 → upos 上传 → 投稿 → 返回作品链接 +func (e *Executor) Execute(ctx context.Context, task exec.Task) exec.Result { + fail := func(err string) exec.Result { + return exec.Result{TaskID: task.TaskID, Status: "failed", Error: err} + } + creds, err := e.creds(task.AccountID) + if err != nil { + return fail("B站账号未登录(本地无凭据),请先在客户端完成扫码绑定") + } + if len(task.MaterialURLs) == 0 { + return fail("任务缺少素材") + } + videoPath, cleanup, err := downloadFile(ctx, e.HTTP, task.MaterialURLs[0]) + if err != nil { + return fail("素材下载失败: " + err.Error()) + } + defer cleanup() + part, err := e.uploadVideo(ctx, creds.Cookies, videoPath) + if err != nil { + return fail("视频上传失败: " + err.Error()) + } + bvid, err := e.submit(ctx, creds.Cookies, task, part) + if err != nil { + return fail("投稿提交失败: " + err.Error()) + } + return exec.Result{ + TaskID: task.TaskID, + Status: "success", + PublishedURL: "https://www.bilibili.com/video/" + bvid, + } +} + +func (e *Executor) creds(accountID string) (*credentials, error) { + raw, err := e.Vault.Load(accountID) + if err != nil { + return nil, err + } + var c credentials + if err = json.Unmarshal(raw, &c); err != nil { + return nil, err + } + if c.Cookies == "" { + return nil, fmt.Errorf("empty cookies") + } + return &c, nil +} + +// uploadVideo 上传视频(upos/bda2 分片),返回投稿所需的 part 信息 +func (e *Executor) uploadVideo(ctx context.Context, cookies, filePath string) (map[string]interface{}, error) { + stat, err := os.Stat(filePath) + if err != nil { + return nil, err + } + name := filepath.Base(filePath) + total := stat.Size() + + // 1) preupload 获取上传地址 + q := url.Values{} + q.Set("r", "upos") + q.Set("profile", "ugcupos/bup") + q.Set("ssl", "0") + q.Set("version", "2.8.12") + q.Set("build", "2081200") + q.Set("name", name) + q.Set("size", strconv.FormatInt(total, 10)) + preURL := e.MemberBase + "/preupload?upcdn=bda2&probe_version=20221109&" + q.Encode() + var pre struct { + OK int `json:"OK"` + Endpoint string `json:"endpoint"` + Auth string `json:"auth"` + BizID int `json:"biz_id"` + UposURI string `json:"upos_uri"` + ChunkSize int `json:"chunk_size"` + } + if err = e.getJSON(ctx, preURL, cookies, &pre); err != nil { + return nil, err + } + if pre.OK != 1 || pre.UposURI == "" { + return nil, fmt.Errorf("preupload 返回异常") + } + upURL := e.UpOSScheme + ":" + pre.Endpoint + "/" + strings.TrimPrefix(pre.UposURI, "upos://") + authH := map[string]string{"X-Upos-Auth": pre.Auth} + + // 2) 申请 upload_id + var up struct { + UploadID string `json:"upload_id"` + } + if err = e.postJSON(ctx, upURL+"?uploads&output=json", cookies, authH, nil, &up); err != nil { + return nil, fmt.Errorf("申请 upload_id 失败: %w", err) + } + + // 3) 分片上传 + chunkSize := pre.ChunkSize + if chunkSize <= 0 { + chunkSize = 7 * 1024 * 1024 + } + chunks := int((total + int64(chunkSize) - 1) / int64(chunkSize)) + f, err := os.Open(filePath) + if err != nil { + return nil, err + } + defer f.Close() + parts := make([]map[string]interface{}, 0, chunks) + buf := make([]byte, chunkSize) + for i := 0; i < chunks; i++ { + n, rerr := io.ReadFull(f, buf) + if rerr != nil && rerr != io.ErrUnexpectedEOF && rerr != io.EOF { + return nil, rerr + } + start := i * chunkSize + end := start + n + pq := url.Values{} + pq.Set("uploadId", up.UploadID) + pq.Set("partNumber", strconv.Itoa(i+1)) + pq.Set("chunk", strconv.Itoa(i)) + pq.Set("chunks", strconv.Itoa(chunks)) + pq.Set("size", strconv.Itoa(n)) + pq.Set("start", strconv.Itoa(start)) + pq.Set("end", strconv.Itoa(end)) + pq.Set("total", strconv.FormatInt(total, 10)) + if err = e.putBody(ctx, upURL+"?"+pq.Encode(), cookies, authH, buf[:n]); err != nil { + return nil, fmt.Errorf("分片 %d 上传失败: %w", i+1, err) + } + parts = append(parts, map[string]interface{}{"partNumber": i + 1, "eTag": "etag"}) + } + + // 4) 合并分片 + mq := url.Values{} + mq.Set("name", name) + mq.Set("uploadId", up.UploadID) + mq.Set("biz_id", strconv.Itoa(pre.BizID)) + mq.Set("output", "json") + mq.Set("profile", "ugcupos/bup") + var merged struct { + OK int `json:"OK"` + } + if err = e.postJSON(ctx, upURL+"?"+mq.Encode(), cookies, authH, map[string]interface{}{"parts": parts}, &merged); err != nil { + return nil, err + } + if merged.OK != 1 { + return nil, fmt.Errorf("分片合并返回异常") + } + return map[string]interface{}{ + "title": strings.TrimSuffix(name, filepath.Ext(name)), + "filename": strings.TrimSuffix(filepath.Base(pre.UposURI), filepath.Ext(filepath.Base(pre.UposURI))), + "desc": "", + }, nil +} + +// submit 投稿(x/vu/web/add) +func (e *Executor) submit(ctx context.Context, cookies string, task exec.Task, part map[string]interface{}) (string, error) { + csrf := cookieValue(cookies, "bili_jct") + // 风控预热(失败不影响) + _, _ = e.get(ctx, e.MemberBase+"/x/geetest/pre/add", cookies) + tag := strings.Join(task.Tags, ",") + if tag == "" { + tag = "日常" + } + body := map[string]interface{}{ + "title": truncateRunes(task.Title, 80), + "desc": task.Content, + "desc_v2": []map[string]interface{}{{"raw_text": task.Content, "type": 1, "biz_id": ""}}, + "copyright": 1, + "source": "", + "tid": 174, + "tag": tag, + "dynamic": "", + "videos": []map[string]interface{}{part}, + } + var ret struct { + Code int `json:"code"` + Message string `json:"message"` + Data struct { + Bvid string `json:"bvid"` + } `json:"data"` + } + if err := e.postJSON(ctx, e.MemberBase+"/x/vu/web/add?csrf="+csrf, cookies, nil, body, &ret); err != nil { + return "", err + } + if ret.Code != 0 { + return "", fmt.Errorf("code=%d %s", ret.Code, ret.Message) + } + return ret.Data.Bvid, nil +} + +// SolveChallenge B站扫码登录:生成二维码 → onQR 展示 → 轮询 → 保存 cookie +func (e *Executor) SolveChallenge(ctx context.Context, ch proto.Challenge, onQR func(qrToken, qrURL string)) (bool, string) { + key, qrURL, err := e.generateQR(ctx) + if err != nil { + return false, "" + } + if onQR != nil { + onQR(key, qrURL) + } + deadline := time.Now().Add(e.PollTimeout) + for time.Now().Before(deadline) { + select { + case <-ctx.Done(): + return false, "" + default: + } + code, cookieURL, perr := e.pollQR(ctx, key) + if perr != nil { + return false, "" + } + switch code { + case 0: + cookies := cookiesFromCrossDomainURL(cookieURL) + if cookies == "" { + return false, "" + } + _ = e.Vault.Save(ch.AccountID, []byte(`{"cookies":"`+jsonEscape(cookies)+`"}`)) + return true, "confirmed" + case 86090: + case 86101: + default: + return false, "" + } + time.Sleep(2 * time.Second) + } + return false, "" +} + +// generateQR 生成登录二维码 +func (e *Executor) generateQR(ctx context.Context) (key, qrURL string, err error) { + var ret struct { + Code int `json:"code"` + Data struct { + URL string `json:"url"` + QrcodeKey string `json:"qrcode_key"` + } `json:"data"` + } + if err = e.getJSON(ctx, e.PassportBase+"/x/passport-login/web/qrcode/generate", "", &ret); err != nil { + return "", "", err + } + if ret.Code != 0 || ret.Data.QrcodeKey == "" { + return "", "", fmt.Errorf("生成二维码失败 code=%d", ret.Code) + } + return ret.Data.QrcodeKey, ret.Data.URL, nil +} + +// pollQR 轮询扫码状态:86101 未扫 / 86090 已扫待确认 / 0 成功(返回跨域 cookie URL) +func (e *Executor) pollQR(ctx context.Context, key string) (int, string, error) { + var ret struct { + Code int `json:"code"` + Data struct { + URL string `json:"url"` + } `json:"data"` + } + if err := e.getJSON(ctx, e.PassportBase+"/x/passport-login/web/qrcode/poll?qrcode_key="+key, "", &ret); err != nil { + return 0, "", err + } + return ret.Code, ret.Data.URL, nil +} + +// ---- HTTP 辅助 ---- + +func (e *Executor) get(ctx context.Context, rawURL, cookies string) ([]byte, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, rawURL, nil) + if err != nil { + return nil, err + } + if cookies != "" { + req.Header.Set("Cookie", cookies) + } + req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120 Safari/537.36") + req.Header.Set("Referer", "https://member.bilibili.com") + resp, err := e.HTTP.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + return io.ReadAll(resp.Body) +} + +func (e *Executor) getJSON(ctx context.Context, rawURL, cookies string, out interface{}) error { + raw, err := e.get(ctx, rawURL, cookies) + if err != nil { + return err + } + return json.Unmarshal(raw, out) +} + +func (e *Executor) postJSON(ctx context.Context, rawURL, cookies string, extraH map[string]string, body interface{}, out interface{}) error { + var buf bytes.Buffer + if body != nil { + raw, _ := json.Marshal(body) + buf.Write(raw) + } + req, err := http.NewRequestWithContext(ctx, http.MethodPost, rawURL, &buf) + if err != nil { + return err + } + if cookies != "" { + req.Header.Set("Cookie", cookies) + } + for k, v := range extraH { + req.Header.Set(k, v) + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120 Safari/537.36") + req.Header.Set("Referer", "https://member.bilibili.com") + resp, err := e.HTTP.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + raw, _ := io.ReadAll(resp.Body) + if resp.StatusCode >= 400 { + return fmt.Errorf("http %d", resp.StatusCode) + } + if out == nil { + return nil + } + return json.Unmarshal(raw, out) +} + +func (e *Executor) putBody(ctx context.Context, rawURL, cookies string, extraH map[string]string, data []byte) error { + req, err := http.NewRequestWithContext(ctx, http.MethodPut, rawURL, bytes.NewReader(data)) + if err != nil { + return err + } + if cookies != "" { + req.Header.Set("Cookie", cookies) + } + for k, v := range extraH { + req.Header.Set(k, v) + } + resp, err := e.HTTP.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode >= 400 { + return fmt.Errorf("http %d", resp.StatusCode) + } + return nil +} + +// downloadFile 下载素材到临时文件 +func downloadFile(ctx context.Context, client *http.Client, rawURL string) (string, func(), error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, rawURL, nil) + if err != nil { + return "", nil, err + } + resp, err := client.Do(req) + if err != nil { + return "", nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return "", nil, fmt.Errorf("下载 http %d", resp.StatusCode) + } + tmp, err := os.CreateTemp("", "ep-material-*") + if err != nil { + return "", nil, err + } + _, err = io.Copy(tmp, resp.Body) + tmp.Close() + if err != nil { + os.Remove(tmp.Name()) + return "", nil, err + } + return tmp.Name(), func() { _ = os.Remove(tmp.Name()) }, nil +} + +// cookieValue 从 cookie 串取值 +func cookieValue(cookies, key string) string { + for _, part := range strings.Split(cookies, ";") { + part = strings.TrimSpace(part) + if i := strings.Index(part, "="); i > 0 && part[:i] == key { + return part[i+1:] + } + } + return "" +} + +// cookiesFromCrossDomainURL 从跨域跳转 URL 提取登录 cookie +func cookiesFromCrossDomainURL(rawURL string) string { + u, err := url.Parse(rawURL) + if err != nil { + return "" + } + q := u.Query() + sess := q.Get("SESSDATA") + jct := q.Get("bili_jct") + dede := q.Get("DedeUserID") + if sess == "" { + return "" + } + return "SESSDATA=" + sess + "; bili_jct=" + jct + "; DedeUserID=" + dede +} + +func truncateRunes(s string, max int) string { + r := []rune(s) + if len(r) <= max { + return s + } + return string(r[:max]) +} + +func jsonEscape(s string) string { + raw, _ := json.Marshal(s) + return string(raw[1 : len(raw)-1]) +} diff --git a/client/core/internal/exec/bilibili/bilibili_test.go b/client/core/internal/exec/bilibili/bilibili_test.go new file mode 100644 index 0000000..2d92fa9 --- /dev/null +++ b/client/core/internal/exec/bilibili/bilibili_test.go @@ -0,0 +1,187 @@ +package bilibili + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + "everypublish/agentcore/internal/exec" + "everypublish/agentcore/internal/vault" + "everypublish/shared/proto" +) + +// stubBili 桩:preupload + upos(uploads/分片/合并)+ 投稿 add,记录关键请求 +func stubBili(t *testing.T) (*httptest.Server, *struct { + PartQuery string + PartsLen int + AddBody map[string]interface{} + AddURL string + XUposAuth string +}) { + rec := &struct { + PartQuery string + PartsLen int + AddBody map[string]interface{} + AddURL string + XUposAuth string + }{} + var srv *httptest.Server + srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + switch { + case r.URL.Path == "/preupload": + // endpoint 指向桩服务器自身 + endpoint := "//" + strings.TrimPrefix(srv.URL, "http://") + _ = json.NewEncoder(w).Encode(map[string]interface{}{ + "OK": 1, "endpoint": endpoint, "auth": "auth-x", "biz_id": 1000, + "upos_uri": "upos://video/av123", "chunk_size": 5, + }) + case strings.Contains(r.URL.RawQuery, "uploads"): + _ = json.NewEncoder(w).Encode(map[string]string{"upload_id": "upload-1"}) + case r.Method == http.MethodPut: + rec.PartQuery = r.URL.RawQuery + rec.XUposAuth = r.Header.Get("X-Upos-Auth") + w.WriteHeader(http.StatusOK) + case r.Method == http.MethodPost && strings.Contains(r.URL.RawQuery, "output=json"): + var body map[string]interface{} + _ = json.NewDecoder(r.Body).Decode(&body) + if parts, ok := body["parts"].([]interface{}); ok { + rec.PartsLen = len(parts) + } + _ = json.NewEncoder(w).Encode(map[string]int{"OK": 1}) + case r.URL.Path == "/x/vu/web/add": + rec.AddURL = r.URL.RawQuery + _ = json.NewDecoder(r.Body).Decode(&rec.AddBody) + _ = json.NewEncoder(w).Encode(map[string]interface{}{ + "code": 0, "data": map[string]string{"bvid": "BV1TEST123"}, + }) + default: + w.WriteHeader(http.StatusNotFound) + } + })) + return srv, rec +} + +func TestExecuteUploadFlow(t *testing.T) { + // 素材服务器 + matSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte("0123456789abcdef")) + })) + defer matSrv.Close() + + srv, rec := stubBili(t) + defer srv.Close() + + v, _ := vault.Open(t.TempDir()) + _ = v.Save("77", []byte(`{"cookies":"SESSDATA=sess; bili_jct=csrf-x; DedeUserID=1"}`)) + + e := New(v) + e.MemberBase = srv.URL + e.PassportBase = srv.URL + e.UpOSScheme = "http" + + res := e.Execute(context.Background(), exec.Task{ + TaskID: "9", AccountID: "77", Title: "测试标题", Content: "简介", + Tags: []string{"测试", "日常"}, MaterialURLs: []string{matSrv.URL + "/v.mp4"}, + }) + if res.Status != "success" { + t.Fatalf("expect success, got %+v", res) + } + if res.PublishedURL != "https://www.bilibili.com/video/BV1TEST123" { + t.Fatalf("published url wrong: %s", res.PublishedURL) + } + // rec.PartQuery 记录最后一片(partNumber=4 size=1 start=15 end=16) + if !strings.Contains(rec.PartQuery, "partNumber=4") || !strings.Contains(rec.PartQuery, "chunks=4") || + !strings.Contains(rec.PartQuery, "total=16") || !strings.Contains(rec.PartQuery, "size=1") { + t.Fatalf("chunk query wrong: %s", rec.PartQuery) + } + if rec.PartsLen != 4 { + t.Fatalf("expect 4 merged parts, got %d", rec.PartsLen) + } + if rec.AddURL != "csrf=csrf-x" { + t.Fatalf("add csrf wrong: %s", rec.AddURL) + } + if rec.AddBody["title"] != "测试标题" || rec.AddBody["copyright"] != float64(1) { + t.Fatalf("add body wrong: %+v", rec.AddBody) + } + if videos, ok := rec.AddBody["videos"].([]interface{}); !ok || len(videos) != 1 { + t.Fatal("videos missing") + } +} + +func TestSolveChallengeQRLogin(t *testing.T) { + pollAttempts := 0 + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + switch { + case strings.Contains(r.URL.Path, "generate"): + _ = json.NewEncoder(w).Encode(map[string]interface{}{ + "code": 0, "data": map[string]string{"url": "https://qr.example/x", "qrcode_key": "key-1"}, + }) + case strings.Contains(r.URL.Path, "poll"): + pollAttempts++ + if pollAttempts == 1 { + _ = json.NewEncoder(w).Encode(map[string]interface{}{"code": 86101, "data": map[string]string{}}) + } else { + _ = json.NewEncoder(w).Encode(map[string]interface{}{ + "code": 0, + "data": map[string]string{ + "url": "https://passport.biligame.com/x/passport-login/web/crossDomain?SESSDATA=sess123&bili_jct=jct456&DedeUserID=789", + }, + }) + } + default: + w.WriteHeader(http.StatusNotFound) + } + })) + defer srv.Close() + + v, _ := vault.Open(t.TempDir()) + e := New(v) + e.MemberBase = srv.URL + e.PassportBase = srv.URL + e.PollTimeout = 10 * time.Second + + var gotQR string + solved, value := e.SolveChallenge(context.Background(), proto.Challenge{ChallengeID: "1", AccountID: "77"}, func(token, qrURL string) { + gotQR = qrURL + }) + if !solved || value != "confirmed" { + t.Fatalf("expect solved, got %v %s", solved, value) + } + if gotQR == "" { + t.Fatal("onQR should be called") + } + raw, err := v.Load("77") + if err != nil { + t.Fatalf("vault should have cookies: %v", err) + } + if !strings.Contains(string(raw), "SESSDATA=sess123") { + t.Fatalf("cookies not saved: %s", raw) + } +} + +func TestPollTimeoutUnscanned(t *testing.T) { + // 一直未扫码:应超时返回未解决 + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + if strings.Contains(r.URL.Path, "generate") { + _ = json.NewEncoder(w).Encode(map[string]interface{}{"code": 0, "data": map[string]string{"url": "u", "qrcode_key": "k"}}) + return + } + _ = json.NewEncoder(w).Encode(map[string]interface{}{"code": 86101, "data": map[string]string{}}) + })) + defer srv.Close() + v, _ := vault.Open(t.TempDir()) + e := New(v) + e.MemberBase = srv.URL + e.PassportBase = srv.URL + e.PollTimeout = 3 * time.Second + if solved, _ := e.SolveChallenge(context.Background(), proto.Challenge{AccountID: "1"}, nil); solved { + t.Fatal("unscanned should timeout unsolved") + } +} diff --git a/client/core/internal/exec/cdnbase/base.go b/client/core/internal/exec/cdnbase/base.go new file mode 100644 index 0000000..4c789dc --- /dev/null +++ b/client/core/internal/exec/cdnbase/base.go @@ -0,0 +1,116 @@ +// cdnbase 三平台执行器公共底座:保险库 + 浏览器档案目录 + 代理 + 执行入口。 +package cdnbase + +import ( + "context" + "encoding/json" + "path/filepath" + + "everypublish/agentcore/internal/exec" + "everypublish/agentcore/internal/exec/cdp" + "everypublish/agentcore/internal/vault" + "everypublish/shared/proto" +) + +// Platform 三平台执行器公共底座 +type Platform struct { + Vault *vault.Vault + RootDir string // 浏览器档案根目录(每账号子目录) + Proxy string // 账号固定 IP 代理 + ChromePath string + Headless bool + ReceiptDir string // 截图回执目录 + Name string // 平台名(日志) + PublishCfg cdp.PublishConfig + LoginCfg cdp.QRLoginConfig + LoginDomain string // 登录 cookie 域名 +} + +// credentials 保险库凭据 +type credentials struct { + Cookies string `json:"cookies"` +} + +// loadCookies 读账号 cookie(无则空,触发扫码) +func (p *Platform) loadCookies(accountID string) string { + raw, err := p.Vault.Load(accountID) + if err != nil { + return "" + } + var c credentials + if json.Unmarshal(raw, &c) != nil { + return "" + } + return c.Cookies +} + +// profileDir 账号独立档案目录 +func (p *Platform) profileDir(accountID string) string { + return filepath.Join(p.RootDir, p.Name, accountID) +} + +// Publish 通用发布入口(下载素材 → 启动浏览器 → 发布 → 回传) +func (p *Platform) Publish(ctx context.Context, task exec.Task) exec.Result { + fail := func(err string) exec.Result { + return exec.Result{TaskID: task.TaskID, Status: "failed", Error: err} + } + cookies := p.loadCookies(task.AccountID) + if len(task.MaterialURLs) == 0 { + return fail("任务缺少素材") + } + videoPath, cleanup, err := cdp.DownloadFile(ctx, task.MaterialURLs[0]) + if err != nil { + return fail("素材下载失败: " + err.Error()) + } + defer cleanup() + + br, err := cdp.Launch(ctx, cdp.Options{ + UserDataDir: p.profileDir(task.AccountID), + Proxy: p.Proxy, + Headless: p.Headless, + ChromePath: p.ChromePath, + }) + if err != nil { + return fail("浏览器启动失败: " + err.Error()) + } + defer br.Close() + + cfg := p.PublishCfg + cfg.Platform = p.Name + cfg.ReceiptDir = p.ReceiptDir + res, err := br.Publish(ctx, cfg, videoPath, task.Title, task.Content, task.Tags, cookies, p.LoginDomain) + if err != nil { + return fail("发布失败: " + err.Error()) + } + return exec.Result{ + TaskID: task.TaskID, + Status: "success", + PublishedURL: res.URL, + Receipts: res.Receipts, + } +} + +// SolveChallenge CDP 扫码登录:二维码截图经 onQR 展示 → 轮询登录 → 保存 cookie +func (p *Platform) SolveChallenge(ctx context.Context, ch proto.Challenge, onQR func(qrToken, qrURL string)) (bool, string) { + br, err := cdp.Launch(ctx, cdp.Options{ + UserDataDir: p.profileDir(ch.AccountID), + Proxy: p.Proxy, + Headless: p.Headless, + ChromePath: p.ChromePath, + }) + if err != nil { + cdp.Log("%s: 扫码浏览器启动失败: %v", p.Name, err) + return false, "" + } + defer br.Close() + cookies, err := br.QRLogin(ctx, p.LoginCfg, onQR) + if err != nil { + cdp.Log("%s: 扫码登录失败: %v", p.Name, err) + return false, "" + } + if cookies == "" { + return false, "" + } + _ = p.Vault.Save(ch.AccountID, []byte(`{"cookies":"`+cdp.EscapeJSON(cookies)+`"}`)) + return true, "confirmed" +} diff --git a/client/core/internal/exec/cdp/cdp.go b/client/core/internal/exec/cdp/cdp.go new file mode 100644 index 0000000..f8c32fa --- /dev/null +++ b/client/core/internal/exec/cdp/cdp.go @@ -0,0 +1,231 @@ +// cdp 共享浏览器基础设施:独立档案启动(每账号一档案)、代理、指纹、扫码登录。 +// 一期驱动 rod-launcher 管理的 Chromium;ChromePath 给定则驱动系统 Chrome/Edge。 +package cdp + +import ( + "context" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "os" + "path/filepath" + "runtime" + "strings" + "time" + + "github.com/go-rod/rod" + "github.com/go-rod/rod/lib/launcher" + "github.com/go-rod/rod/lib/proto" + rodstealth "github.com/go-rod/stealth" +) + +// Options 浏览器启动选项 +type Options struct { + UserDataDir string // 每账号独立档案目录 + Proxy string // 账号固定 IP 代理(http://ip:port) + Headless bool + ChromePath string // 系统 Chrome/Edge 可执行文件(空=自动探测/下载) +} + +// Browser 浏览器封装 +type Browser struct { + rod *rod.Browser + Page *rod.Page +} + +// defaultChromePath 常见系统浏览器路径(Windows 优先,macOS 兜底) +func defaultChromePath() string { + candidates := []string{ + "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe", + "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe", + "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe", + "C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe", + } + if runtime.GOOS == "darwin" { + candidates = append(candidates, "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome") + } + for _, p := range candidates { + if _, err := os.Stat(p); err == nil { + return p + } + } + return "" +} + +// Launch 启动浏览器(独立档案 + 指纹 + 反自动化) +func Launch(ctx context.Context, opts Options) (*Browser, error) { + l := launcher.New() + if opts.UserDataDir != "" { + if err := os.MkdirAll(opts.UserDataDir, 0o700); err != nil { + return nil, err + } + l = l.UserDataDir(opts.UserDataDir) + } + if opts.Proxy != "" { + l = l.Set("proxy-server", opts.Proxy) + } + bin := opts.ChromePath + if bin == "" { + bin = defaultChromePath() + } + if bin != "" { + l = l.Bin(bin) + } + l = l.Headless(opts.Headless). + Set("no-first-run"). + Set("no-default-browser-check"). + Set("disable-blink-features", "AutomationControlled"). + Set("window-size", "1440,900") + url, err := l.Launch() + if err != nil { + return nil, err + } + browser := rod.New().ControlURL(url) + if err = browser.Connect(); err != nil { + return nil, err + } + // 指纹/反自动化(stealth 注入) + page, err := rodstealth.Page(browser) + if err != nil { + browser.Close() + return nil, err + } + return &Browser{rod: browser, Page: page}, nil +} + +// Close 关闭浏览器 +func (b *Browser) Close() { + _ = b.rod.Close() +} + +// Screenshot 截图回执(PNG 本地保存,返回路径;二期上传服务器) +func (b *Browser) Screenshot(receiptDir, name string) string { + if err := os.MkdirAll(receiptDir, 0o755); err != nil { + return "" + } + path := filepath.Join(receiptDir, name) + _ = b.Page.MustWaitLoad().MustScreenshot(path) + return path +} + +// InjectCookies 注入登录 cookie(免重复扫码) +func (b *Browser) InjectCookies(cookieStr, domain string) error { + for _, part := range strings.Split(cookieStr, ";") { + part = strings.TrimSpace(part) + if part == "" { + continue + } + i := strings.Index(part, "=") + if i <= 0 { + continue + } + name, value := part[:i], part[i+1:] + cookie := &proto.NetworkCookieParam{ + Name: name, + Value: value, + Domain: domain, + Path: "/", + } + _ = b.rod.SetCookies([]*proto.NetworkCookieParam{cookie}) + } + return nil +} + +// Cookies 导出当前浏览器 cookie(域名过滤) +func (b *Browser) Cookies(domain string) string { + cookies, err := b.rod.GetCookies() + if err != nil { + return "" + } + parts := make([]string, 0, len(cookies)) + for _, c := range cookies { + if domain != "" && !strings.Contains(c.Domain, domain) { + continue + } + parts = append(parts, c.Name+"="+c.Value) + } + return strings.Join(parts, "; ") +} + +// QRLoginConfig 扫码登录配置 +type QRLoginConfig struct { + LoginURL string + LoginPathMarker string // 登录页 URL 特征(登录成功=URL 离开该特征) + QRSelector string // 二维码元素选择器 + SuccessWait time.Duration +} + +// QRLogin 通用扫码登录:打开登录页 → 截图二维码经 onQR 展示 → 轮询 URL 直到登录成功 +func (b *Browser) QRLogin(ctx context.Context, cfg QRLoginConfig, onQR func(qrToken, qrURL string)) (string, error) { + if err := b.Page.Navigate(cfg.LoginURL); err != nil { + return "", err + } + _ = b.Page.WaitLoad() + // 截图二维码 → 临时 PNG → file:// URI → onQR(壳端 BitmapImage 展示) + qrEl, err := b.Page.Element(cfg.QRSelector) + if err == nil { + raw, serr := qrEl.Screenshot(proto.PageCaptureScreenshotFormatPng, 100) + if serr == nil && len(raw) > 0 { + qrPath := filepath.Join(os.TempDir(), "ep-qr-"+time.Now().Format("150405")+".png") + if werr := os.WriteFile(qrPath, raw, 0o600); werr == nil && onQR != nil { + onQR("", "file://"+qrPath) + } + } + } + // 轮询登录状态 + deadline := time.Now().Add(cfg.SuccessWait) + for time.Now().Before(deadline) { + select { + case <-ctx.Done(): + return "", fmt.Errorf("cancelled") + default: + } + info, ierr := b.Page.Info() + if ierr == nil && !strings.Contains(info.URL, cfg.LoginPathMarker) { + return b.Cookies(""), nil + } + time.Sleep(2 * time.Second) + } + return "", fmt.Errorf("扫码登录超时") +} + +// DownloadFile 下载素材到临时文件(返回路径与清理函数) +func DownloadFile(ctx context.Context, rawURL string) (string, func(), error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, rawURL, nil) + if err != nil { + return "", nil, err + } + client := &http.Client{Timeout: 10 * time.Minute} + resp, err := client.Do(req) + if err != nil { + return "", nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return "", nil, fmt.Errorf("下载 http %d", resp.StatusCode) + } + tmp, err := os.CreateTemp("", "ep-material-*") + if err != nil { + return "", nil, err + } + _, err = io.Copy(tmp, resp.Body) + tmp.Close() + if err != nil { + os.Remove(tmp.Name()) + return "", nil, err + } + return tmp.Name(), func() { _ = os.Remove(tmp.Name()) }, nil +} + +// EscapeJSON 转义字符串供嵌入 JSON +func EscapeJSON(s string) string { + raw, _ := json.Marshal(s) + return string(raw[1 : len(raw)-1]) +} + +// Log 调试输出 +func Log(format string, args ...interface{}) { + log.Printf("[cdp] "+format, args...) +} diff --git a/client/core/internal/exec/cdp/cdp_test.go b/client/core/internal/exec/cdp/cdp_test.go new file mode 100644 index 0000000..0d37ab7 --- /dev/null +++ b/client/core/internal/exec/cdp/cdp_test.go @@ -0,0 +1,64 @@ +package cdp + +import ( + "context" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" +) + +func TestEscapeJSON(t *testing.T) { + s := EscapeJSON("SESSDATA=ab\"c; bili_jct=d") + // 转义后应可嵌入 JSON 且还原一致 + var decoded string + if err := json.Unmarshal([]byte(`"`+s+`"`), &decoded); err != nil { + t.Fatalf("escaped string not valid json: %v", err) + } + if decoded != "SESSDATA=ab\"c; bili_jct=d" { + t.Fatalf("roundtrip mismatch: %q", decoded) + } +} + +func TestDownloadFile(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte("video-bytes-123")) + })) + defer srv.Close() + + path, cleanup, err := DownloadFile(context.Background(), srv.URL+"/v.mp4") + if err != nil { + t.Fatalf("download: %v", err) + } + defer cleanup() + raw, err := os.ReadFile(path) + if err != nil || string(raw) != "video-bytes-123" { + t.Fatalf("content mismatch: %s %v", raw, err) + } + cleanup() + if _, err = os.Stat(path); !os.IsNotExist(err) { + t.Fatal("cleanup should remove temp file") + } +} + +func TestDownloadFileHTTPError(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusGone) + _, _ = io.WriteString(w, "gone") + })) + defer srv.Close() + if _, _, err := DownloadFile(context.Background(), srv.URL); err == nil { + t.Fatal("410 should fail") + } +} + +func TestDefaultChromePath(t *testing.T) { + // 至少不 panic、返回字符串 + p := defaultChromePath() + if strings.TrimSpace(p) == "" && p != "" { + t.Fatal("unexpected") + } +} diff --git a/client/core/internal/exec/cdp/publish.go b/client/core/internal/exec/cdp/publish.go new file mode 100644 index 0000000..b4cc4b1 --- /dev/null +++ b/client/core/internal/exec/cdp/publish.go @@ -0,0 +1,144 @@ +// Publish 通用发布流程:进上传页 → 注入 cookie → 传视频 → 填表单 → 点发布 → 等成功跳转 → 截图回执。 +// 各平台选择器差异经 PublishConfig 配置;平台改版时只需调整配置。 +package cdp + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/go-rod/rod" + "github.com/go-rod/rod/lib/proto" +) + +// PublishConfig 发布流程配置(选择器按平台填写) +type PublishConfig struct { + Platform string + UploadURL string + SuccessMarker string // 发布成功后的 URL 特征 + FileInputSel string + TitleSel string + DescSel string + PublishBtnSel string + FormWait time.Duration // 等视频处理出表单的最长时间 + SuccessWait time.Duration + ReceiptDir string +} + +// PublishResult 发布结果 +type PublishResult struct { + URL string + Receipts []string +} + +// Publish 执行通用发布流程 +func (b *Browser) Publish(ctx context.Context, cfg PublishConfig, videoPath, title, content string, tags []string, cookieStr, cookieDomain string) (*PublishResult, error) { + if err := b.Page.Navigate(cfg.UploadURL); err != nil { + return nil, err + } + _ = b.Page.WaitLoad() + // 注入登录 cookie(有则免扫码) + if cookieStr != "" { + _ = b.InjectCookies(cookieStr, cookieDomain) + if err := b.Page.Navigate(cfg.UploadURL); err != nil { + return nil, err + } + _ = b.Page.WaitLoad() + } + + // 1) 传视频 + fileInput, err := b.Page.Element(cfg.FileInputSel) + if err != nil { + return nil, fmt.Errorf("找不到上传入口(%s): %w", cfg.FileInputSel, err) + } + if err = fileInput.SetFiles([]string{videoPath}); err != nil { + return nil, fmt.Errorf("选择文件失败: %w", err) + } + Log("%s: 视频已选择,等待平台处理...", cfg.Platform) + + // 2) 等表单渲染(视频转码后出现标题输入框) + titleEl, err := b.Page.ElementR(cfg.TitleSel, "") + if err != nil { + titleEl, err = b.waitElement(ctx, cfg.TitleSel, cfg.FormWait) + } + if err != nil { + b.Screenshot(cfg.ReceiptDir, cfg.Platform+"-form-fail.png") + return nil, fmt.Errorf("等表单渲染超时(%s): %w", cfg.TitleSel, err) + } + + // 3) 填标题/描述(话题并入描述) + if err = titleEl.Input(title); err != nil { + return nil, fmt.Errorf("填标题失败: %w", err) + } + desc := content + if len(tags) > 0 { + for _, t := range tags { + t = strings.TrimSpace(t) + if t != "" && !strings.HasPrefix(t, "#") { + t = "#" + t + } + desc += " " + t + } + } + if descEl, derr := b.Page.Element(cfg.DescSel); derr == nil { + _ = descEl.Input(desc) + } + + // 4) 点发布 + btn, err := b.waitElement(ctx, cfg.PublishBtnSel, 15*time.Second) + if err != nil { + b.Screenshot(cfg.ReceiptDir, cfg.Platform+"-btn-fail.png") + return nil, fmt.Errorf("找不到发布按钮(%s): %w", cfg.PublishBtnSel, err) + } + if err = btn.Click(proto.InputMouseButtonLeft, 1); err != nil { + return nil, fmt.Errorf("点发布失败: %w", err) + } + Log("%s: 已点发布,等待结果...", cfg.Platform) + + // 5) 等成功跳转 + if err = b.waitURL(ctx, cfg.SuccessMarker, cfg.SuccessWait); err != nil { + b.Screenshot(cfg.ReceiptDir, cfg.Platform+"-publish-fail.png") + return nil, err + } + info, _ := b.Page.Info() + receipt := b.Screenshot(cfg.ReceiptDir, cfg.Platform+"-success.png") + Log("%s: 发布成功 %s", cfg.Platform, info.URL) + return &PublishResult{URL: info.URL, Receipts: []string{receipt}}, nil +} + +// waitElement 轮询等待元素出现 +func (b *Browser) waitElement(ctx context.Context, selector string, timeout time.Duration) (*rod.Element, error) { + deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + select { + case <-ctx.Done(): + return nil, fmt.Errorf("cancelled") + default: + } + el, err := b.Page.Element(selector) + if err == nil { + return el, nil + } + time.Sleep(2 * time.Second) + } + return nil, fmt.Errorf("timeout waiting %s", selector) +} + +// waitURL 轮询等待 URL 命中特征 +func (b *Browser) waitURL(ctx context.Context, marker string, timeout time.Duration) error { + deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + select { + case <-ctx.Done(): + return fmt.Errorf("cancelled") + default: + } + info, err := b.Page.Info() + if err == nil && strings.Contains(info.URL, marker) { + return nil + } + time.Sleep(2 * time.Second) + } + return fmt.Errorf("timeout waiting url %s", marker) +} diff --git a/client/core/internal/exec/douyin/douyin.go b/client/core/internal/exec/douyin/douyin.go new file mode 100644 index 0000000..f40ffc2 --- /dev/null +++ b/client/core/internal/exec/douyin/douyin.go @@ -0,0 +1,62 @@ +// douyin 抖音执行器(CDP:creator.douyin.com)。选择器参考 dreammis/social-auto-upload(2026-06 版)。 +package douyin + +import ( + "context" + "time" + + "everypublish/agentcore/internal/exec" + "everypublish/agentcore/internal/exec/cdnbase" + "everypublish/agentcore/internal/exec/cdp" + "everypublish/agentcore/internal/vault" + "everypublish/shared/proto" +) + +// Executor 抖音发布执行器 +type Executor struct { + base *cdnbase.Platform +} + +// New 构造执行器(rootDir=浏览器档案根目录,proxy=账号固定IP代理) +func New(v *vault.Vault, rootDir, proxy, chromePath string, headless bool) *Executor { + p := &cdnbase.Platform{ + Vault: v, + RootDir: rootDir, + Proxy: proxy, + ChromePath: chromePath, + Headless: headless, + ReceiptDir: rootDir + "/receipts", + Name: "douyin", + LoginDomain: ".douyin.com", + PublishCfg: cdp.PublishConfig{ + UploadURL: "https://creator.douyin.com/creator-micro/content/upload", + SuccessMarker: "creator-micro/content/manage", + FileInputSel: "input[type=\"file\"]", + TitleSel: "input[placeholder*=\"填写作品标题\"]", + DescSel: "div.zone-container[contenteditable=\"true\"]", + PublishBtnSel: "button:has-text(\"发布\")", + FormWait: 180 * time.Second, // 视频转码渲染表单较慢 + SuccessWait: 120 * time.Second, + }, + LoginCfg: cdp.QRLoginConfig{ + LoginURL: "https://creator.douyin.com/", + LoginPathMarker: "creator.douyin.com/?", + QRSelector: ".qrcode-v2 img, .qrcode img, img[src*=qrcode]", + SuccessWait: 3 * time.Minute, + }, + } + return &Executor{base: p} +} + +// Platform 平台标识 +func (e *Executor) Platform() string { return "douyin" } + +// Execute 发布 +func (e *Executor) Execute(ctx context.Context, task exec.Task) exec.Result { + return e.base.Publish(ctx, task) +} + +// SolveChallenge 扫码登录(QR 截图经 onQR 展示) +func (e *Executor) SolveChallenge(ctx context.Context, ch proto.Challenge, onQR func(qrToken, qrURL string)) (bool, string) { + return e.base.SolveChallenge(ctx, ch, onQR) +} diff --git a/client/core/internal/exec/exec_test.go b/client/core/internal/exec/exec_test.go new file mode 100644 index 0000000..7a13102 --- /dev/null +++ b/client/core/internal/exec/exec_test.go @@ -0,0 +1,39 @@ +package exec_test + +import ( + "context" + "testing" + "time" + + "everypublish/agentcore/internal/exec" + "everypublish/agentcore/internal/exec/fake" + "everypublish/shared/proto" +) + +func TestFakeExecutor(t *testing.T) { + e := fake.Executor{SimMS: 5} + start := time.Now() + res := e.Execute(context.Background(), exec.Task{TaskID: "1", Platform: "douyin", Title: "t"}) + if time.Since(start) < 5*time.Millisecond { + t.Fatal("should simulate delay") + } + if res.Status != "success" || res.PublishedURL == "" { + t.Fatalf("expect success, got %+v", res) + } +} + +func TestFakeSolver(t *testing.T) { + e := fake.Executor{SimMS: 1} + solved, value := e.SolveChallenge(context.Background(), proto.Challenge{ChallengeID: "1"}, nil) + if !solved || value == "" { + t.Fatalf("expect solved, got %v %s", solved, value) + } +} + +func TestRegistryFallback(t *testing.T) { + r := exec.NewRegistry() + r.SetFallback(fake.Executor{SimMS: 1}) + if r.Get("douyin") == nil { + t.Fatal("fallback should serve any platform") + } +} diff --git a/client/core/internal/exec/executor.go b/client/core/internal/exec/executor.go new file mode 100644 index 0000000..ddb05d3 --- /dev/null +++ b/client/core/internal/exec/executor.go @@ -0,0 +1,87 @@ +package exec + +import ( + "context" + "sync" + + "everypublish/shared/proto" +) + +// Task 执行任务(由 task.push 转换而来) +type Task struct { + TaskID string + Platform string + AccountID string + AccountName string + Title string + Content string + Tags []string + MaterialURLs []string + ScheduleAt int64 + Priority int +} + +// Result 执行结果(回传 task.result) +type Result struct { + TaskID string + Status string + PublishedURL string + Receipts []string + Error string +} + +// Executor 平台执行器(每个平台一个实现;D10 起接入真实平台) +type Executor interface { + Platform() string + Execute(ctx context.Context, task Task) Result +} + +// ChallengeSolver 能自动完成挑战的平台执行器(扫码/验证码)。 +// onQR:平台生成二维码时回调(把 qrToken/qrUrl 推给壳展示,可为 nil)。 +type ChallengeSolver interface { + SolveChallenge(ctx context.Context, ch proto.Challenge, onQR func(qrToken, qrURL string)) (solved bool, value string) +} + +// Registry 执行器注册表(并发安全) +type Registry struct { + mu sync.RWMutex + m map[string]Executor + fallback Executor +} + +// NewRegistry 创建注册表 +func NewRegistry() *Registry { + return &Registry{m: make(map[string]Executor)} +} + +// Register 注册平台执行器 +func (r *Registry) Register(e Executor) { + r.mu.Lock() + defer r.mu.Unlock() + r.m[e.Platform()] = e +} + +// SetFallback 注册兜底执行器(未接入平台用) +func (r *Registry) SetFallback(e Executor) { + r.mu.Lock() + defer r.mu.Unlock() + r.fallback = e +} + +// Get 取执行器(无匹配返回 nil) +func (r *Registry) Get(platform string) Executor { + r.mu.RLock() + defer r.mu.RUnlock() + if e, ok := r.m[platform]; ok { + return e + } + return r.fallback +} + +// Solve 取挑战解决器(nil 表示不支持自动解决) +func (r *Registry) Solve(platform string) ChallengeSolver { + if s, ok := r.Get(platform).(ChallengeSolver); ok { + return s + } + return nil +} diff --git a/client/core/internal/exec/fake/fake.go b/client/core/internal/exec/fake/fake.go new file mode 100644 index 0000000..e73c675 --- /dev/null +++ b/client/core/internal/exec/fake/fake.go @@ -0,0 +1,46 @@ +// fake 假执行器:D8 闭环验收用,模拟平台执行(延迟后返回成功)。 +package fake + +import ( + "context" + "time" + + "everypublish/agentcore/internal/exec" + "everypublish/shared/proto" +) + +// Executor 假执行器(可作兜底:任何平台都返回成功) +type Executor struct { + SimMS int +} + +// Platform 空串表示通用(注册为兜底) +func (e Executor) Platform() string { return "" } + +// Execute 模拟执行:延迟 SimMS 后返回成功 +func (e Executor) Execute(ctx context.Context, task exec.Task) exec.Result { + select { + case <-time.After(time.Duration(e.SimMS) * time.Millisecond): + case <-ctx.Done(): + return exec.Result{TaskID: task.TaskID, Status: "failed", Error: "cancelled"} + } + return exec.Result{ + TaskID: task.TaskID, + Status: "success", + PublishedURL: "https://example.com/fake/" + task.TaskID, + Receipts: []string{}, + } +} + +// SolveChallenge 模拟扫码:延迟后返回已解决 +func (e Executor) SolveChallenge(ctx context.Context, ch proto.Challenge, onQR func(qrToken, qrURL string)) (bool, string) { + if onQR != nil { + onQR("fake-qr-token", "https://example.com/fake-qr") + } + select { + case <-time.After(time.Duration(e.SimMS) * time.Millisecond): + case <-ctx.Done(): + return false, "" + } + return true, "fake-qr-token" +} diff --git a/client/core/internal/exec/kuaishou/kuaishou.go b/client/core/internal/exec/kuaishou/kuaishou.go new file mode 100644 index 0000000..f8fb79c --- /dev/null +++ b/client/core/internal/exec/kuaishou/kuaishou.go @@ -0,0 +1,62 @@ +// kuaishou 快手执行器(CDP:cp.kuaishou.com)。 +package kuaishou + +import ( + "context" + "time" + + "everypublish/agentcore/internal/exec" + "everypublish/agentcore/internal/exec/cdnbase" + "everypublish/agentcore/internal/exec/cdp" + "everypublish/agentcore/internal/vault" + "everypublish/shared/proto" +) + +// Executor 快手发布执行器 +type Executor struct { + base *cdnbase.Platform +} + +// New 构造执行器 +func New(v *vault.Vault, rootDir, proxy, chromePath string, headless bool) *Executor { + p := &cdnbase.Platform{ + Vault: v, + RootDir: rootDir, + Proxy: proxy, + ChromePath: chromePath, + Headless: headless, + ReceiptDir: rootDir + "/receipts", + Name: "kuaishou", + LoginDomain: ".kuaishou.com", + PublishCfg: cdp.PublishConfig{ + UploadURL: "https://cp.kuaishou.com/article/publish/video", + SuccessMarker: "article/manage/video", + FileInputSel: "input[type=\"file\"]", + TitleSel: "input[placeholder*=\"标题\"]", + DescSel: "[contenteditable=\"true\"]", + PublishBtnSel: "button:has-text(\"发布\")", + FormWait: 180 * time.Second, + SuccessWait: 120 * time.Second, + }, + LoginCfg: cdp.QRLoginConfig{ + LoginURL: "https://cp.kuaishou.com/", + LoginPathMarker: "passport.kuaishou.com", + QRSelector: ".qrcode-img, img[src*=qrcode], .qrcode img", + SuccessWait: 3 * time.Minute, + }, + } + return &Executor{base: p} +} + +// Platform 平台标识 +func (e *Executor) Platform() string { return "kuaishou" } + +// Execute 发布 +func (e *Executor) Execute(ctx context.Context, task exec.Task) exec.Result { + return e.base.Publish(ctx, task) +} + +// SolveChallenge 扫码登录 +func (e *Executor) SolveChallenge(ctx context.Context, ch proto.Challenge, onQR func(qrToken, qrURL string)) (bool, string) { + return e.base.SolveChallenge(ctx, ch, onQR) +} diff --git a/client/core/internal/exec/xiaohongshu/xiaohongshu.go b/client/core/internal/exec/xiaohongshu/xiaohongshu.go new file mode 100644 index 0000000..dc46c1b --- /dev/null +++ b/client/core/internal/exec/xiaohongshu/xiaohongshu.go @@ -0,0 +1,62 @@ +// xiaohongshu 小红书执行器(CDP:creator.xiaohongshu.com)。 +package xiaohongshu + +import ( + "context" + "time" + + "everypublish/agentcore/internal/exec" + "everypublish/agentcore/internal/exec/cdnbase" + "everypublish/agentcore/internal/exec/cdp" + "everypublish/agentcore/internal/vault" + "everypublish/shared/proto" +) + +// Executor 小红书发布执行器 +type Executor struct { + base *cdnbase.Platform +} + +// New 构造执行器 +func New(v *vault.Vault, rootDir, proxy, chromePath string, headless bool) *Executor { + p := &cdnbase.Platform{ + Vault: v, + RootDir: rootDir, + Proxy: proxy, + ChromePath: chromePath, + Headless: headless, + ReceiptDir: rootDir + "/receipts", + Name: "xiaohongshu", + LoginDomain: ".xiaohongshu.com", + PublishCfg: cdp.PublishConfig{ + UploadURL: "https://creator.xiaohongshu.com/publish/publish", + SuccessMarker: "publish/success", + FileInputSel: "input[type=\"file\"]", + TitleSel: "input[placeholder*=\"填写标题\"]", + DescSel: "#post-textarea", + PublishBtnSel: "button:has-text(\"发布\")", + FormWait: 180 * time.Second, + SuccessWait: 120 * time.Second, + }, + LoginCfg: cdp.QRLoginConfig{ + LoginURL: "https://creator.xiaohongshu.com/login", + LoginPathMarker: "creator.xiaohongshu.com/login", + QRSelector: ".qrcode-img, img[src*=qrcode], .qrcode img", + SuccessWait: 3 * time.Minute, + }, + } + return &Executor{base: p} +} + +// Platform 平台标识 +func (e *Executor) Platform() string { return "xiaohongshu" } + +// Execute 发布 +func (e *Executor) Execute(ctx context.Context, task exec.Task) exec.Result { + return e.base.Publish(ctx, task) +} + +// SolveChallenge 扫码登录 +func (e *Executor) SolveChallenge(ctx context.Context, ch proto.Challenge, onQR func(qrToken, qrURL string)) (bool, string) { + return e.base.SolveChallenge(ctx, ch, onQR) +} diff --git a/client/core/internal/localserver/localserver.go b/client/core/internal/localserver/localserver.go new file mode 100644 index 0000000..bf71551 --- /dev/null +++ b/client/core/internal/localserver/localserver.go @@ -0,0 +1,163 @@ +// localserver 本机控制服务:仅 127.0.0.1,token 鉴权,供 WinUI 壳调用。 +// 端点:/status /challenges /challenges/{id} /pair;端口与 token 写入 data/local.json。 +package localserver + +import ( + "crypto/rand" + "crypto/subtle" + "encoding/hex" + "encoding/json" + "log" + "net" + "net/http" + "os" + "path/filepath" + "strings" + "sync" + + "everypublish/shared/proto" +) + +// CoreState 核心状态接口(由 wsclient.Client 实现) +type CoreState interface { + Online() bool + SessionID() string + Challenges() []proto.Challenge + SolveChallenge(challengeID, value string) error +} + +// PairFunc 配对回调(main 提供:配对成功保存身份并启动连接) +type PairFunc func(code, name string) error + +// Info 本地服务信息(写 local.json 供壳读取) +type Info struct { + Port int `json:"port"` + Token string `json:"token"` +} + +// ExtraFunc 额外状态字段(server/deviceId/name 等,闭包实时取值) +type ExtraFunc func() map[string]interface{} + +// Server 本机控制服务 +type Server struct { + state CoreState + pair PairFunc + token string + dir string + extra ExtraFunc + mu sync.Mutex + addr string +} + +// New 构造控制服务(extra 可空) +func New(state CoreState, pair PairFunc, token, dir string, extra ExtraFunc) *Server { + return &Server{state: state, pair: pair, token: token, dir: dir, extra: extra} +} + +// Listen 绑定随机端口并服务,同时写 data/local.json +func (s *Server) Listen() error { + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return err + } + port := ln.Addr().(*net.TCPAddr).Port + s.mu.Lock() + s.addr = ln.Addr().String() + s.mu.Unlock() + raw, _ := json.Marshal(Info{Port: port, Token: s.token}) + _ = os.WriteFile(filepath.Join(s.dir, "local.json"), raw, 0o600) + log.Printf("[local] control server %s (local.json written)", s.addr) + + mux := http.NewServeMux() + mux.HandleFunc("/status", s.handleStatus) + mux.HandleFunc("/challenges", s.handleChallenges) + mux.HandleFunc("/challenges/", s.handleSolve) + mux.HandleFunc("/pair", s.handlePair) + return http.Serve(ln, mux) +} + +// Addr 实际监听地址(Listen 后可用) +func (s *Server) Addr() string { + s.mu.Lock() + defer s.mu.Unlock() + return s.addr +} + +func (s *Server) auth(r *http.Request) bool { + // 仅接受请求头;token 不进 URL(避免泄露进日志/历史)。常数时间比较防时序侧信道。 + got := r.Header.Get("X-Agent-Token") + if len(got) != len(s.token) { + return false + } + return subtle.ConstantTimeCompare([]byte(got), []byte(s.token)) == 1 +} + +func (s *Server) handleStatus(w http.ResponseWriter, r *http.Request) { + if !s.auth(r) { + http.Error(w, "unauthorized", http.StatusUnauthorized) + return + } + m := map[string]interface{}{ + "online": s.state.Online(), + "session": s.state.SessionID(), + } + if s.extra != nil { + for k, v := range s.extra() { + m[k] = v + } + } + _ = json.NewEncoder(w).Encode(m) +} + +func (s *Server) handleChallenges(w http.ResponseWriter, r *http.Request) { + if !s.auth(r) { + http.Error(w, "unauthorized", http.StatusUnauthorized) + return + } + _ = json.NewEncoder(w).Encode(map[string]interface{}{"list": s.state.Challenges()}) +} + +func (s *Server) handleSolve(w http.ResponseWriter, r *http.Request) { + if !s.auth(r) { + http.Error(w, "unauthorized", http.StatusUnauthorized) + return + } + id := strings.TrimPrefix(r.URL.Path, "/challenges/") + var req struct { + Value string `json:"value"` + } + _ = json.NewDecoder(r.Body).Decode(&req) + if err := s.state.SolveChallenge(id, req.Value); err != nil { + _ = json.NewEncoder(w).Encode(map[string]interface{}{"code": 1, "message": err.Error()}) + return + } + _ = json.NewEncoder(w).Encode(map[string]interface{}{"code": 0}) +} + +func (s *Server) handlePair(w http.ResponseWriter, r *http.Request) { + if !s.auth(r) { + http.Error(w, "unauthorized", http.StatusUnauthorized) + return + } + var req struct { + Code string `json:"code"` + Name string `json:"name"` + } + _ = json.NewDecoder(r.Body).Decode(&req) + if req.Code == "" { + _ = json.NewEncoder(w).Encode(map[string]interface{}{"code": 1, "message": "配对码必填"}) + return + } + if err := s.pair(req.Code, req.Name); err != nil { + _ = json.NewEncoder(w).Encode(map[string]interface{}{"code": 1, "message": err.Error()}) + return + } + _ = json.NewEncoder(w).Encode(map[string]interface{}{"code": 0}) +} + +// RandToken 生成随机 token +func RandToken() string { + b := make([]byte, 16) + _, _ = rand.Read(b) + return hex.EncodeToString(b) +} diff --git a/client/core/internal/localserver/localserver_test.go b/client/core/internal/localserver/localserver_test.go new file mode 100644 index 0000000..81d6229 --- /dev/null +++ b/client/core/internal/localserver/localserver_test.go @@ -0,0 +1,136 @@ +package localserver + +import ( + "bytes" + "encoding/json" + "errors" + "net/http" + "testing" + "time" + + "everypublish/shared/proto" +) + +type fakeState struct { + online bool + chs []proto.Challenge + err error +} + +func (f *fakeState) Online() bool { return f.online } +func (f *fakeState) SessionID() string { return "s1" } +func (f *fakeState) Challenges() []proto.Challenge { return f.chs } +func (f *fakeState) SolveChallenge(id, value string) error { return f.err } + +func startTestServer(t *testing.T, state CoreState, pair PairFunc) (*Server, string) { + t.Helper() + token := RandToken() + s := New(state, pair, token, t.TempDir(), nil) + go func() { _ = s.Listen() }() + for i := 0; i < 50; i++ { + if s.Addr() != "" { + break + } + time.Sleep(10 * time.Millisecond) + } + if s.Addr() == "" { + t.Fatal("server did not start") + } + return s, token +} + +func get(t *testing.T, base, path, token string) (int, map[string]interface{}) { + t.Helper() + req, _ := http.NewRequest("GET", "http://"+base+path, nil) + if token != "" { + req.Header.Set("X-Agent-Token", token) + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatalf("get: %v", err) + } + defer resp.Body.Close() + var m map[string]interface{} + _ = json.NewDecoder(resp.Body).Decode(&m) + return resp.StatusCode, m +} + +func TestStatusAuth(t *testing.T) { + s, token := startTestServer(t, &fakeState{online: true}, nil) + code, _ := get(t, s.Addr(), "/status", "") + if code != 401 { + t.Fatalf("no token should be 401, got %d", code) + } + code, m := get(t, s.Addr(), "/status", token) + if code != 200 || m["online"] != true { + t.Fatalf("status wrong: %d %v", code, m) + } +} + +func TestPairCallback(t *testing.T) { + var gotCode, gotName string + s, token := startTestServer(t, &fakeState{}, func(code, name string) error { + gotCode, gotName = code, name + return nil + }) + // 未带 token → 401 + req, _ := http.NewRequest("POST", "http://"+s.Addr()+"/pair", bytes.NewBufferString(`{"code":"AB12CD","name":"PC"}`)) + resp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatalf("pair: %v", err) + } + if resp.StatusCode != 401 { + t.Fatalf("pair without token should 401, got %d", resp.StatusCode) + } + resp.Body.Close() + // 带 token → 回调被调用 + req2, _ := http.NewRequest("POST", "http://"+s.Addr()+"/pair", bytes.NewBufferString(`{"code":"AB12CD","name":"PC"}`)) + req2.Header.Set("X-Agent-Token", token) + resp2, err := http.DefaultClient.Do(req2) + if err != nil { + t.Fatalf("pair: %v", err) + } + resp2.Body.Close() + if gotCode != "AB12CD" || gotName != "PC" { + t.Fatalf("callback not invoked: %q %q", gotCode, gotName) + } +} + +func TestPairErrorPropagates(t *testing.T) { + s, token := startTestServer(t, &fakeState{}, func(code, name string) error { + return errors.New("配对码无效") + }) + req, _ := http.NewRequest("POST", "http://"+s.Addr()+"/pair", bytes.NewBufferString(`{"code":"X"}`)) + req.Header.Set("X-Agent-Token", token) + resp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatalf("pair: %v", err) + } + defer resp.Body.Close() + var m map[string]interface{} + _ = json.NewDecoder(resp.Body).Decode(&m) + if m["code"] != float64(1) { + t.Fatalf("expect error code 1, got %v", m) + } +} + +func TestSolve(t *testing.T) { + s, token := startTestServer(t, &fakeState{err: nil}, nil) + code, m := get(t, s.Addr(), "/challenges", token) + if code != 200 { + t.Fatalf("challenges: %d", code) + } + _ = m + req, _ := http.NewRequest("POST", "http://"+s.Addr()+"/challenges/9", bytes.NewBufferString(`{"value":"123456"}`)) + req.Header.Set("X-Agent-Token", token) + resp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatalf("solve: %v", err) + } + defer resp.Body.Close() + var m2 map[string]interface{} + _ = json.NewDecoder(resp.Body).Decode(&m2) + if m2["code"] != float64(0) { + t.Fatalf("solve should succeed, got %v", m2) + } +} diff --git a/client/core/internal/pairing/pairing.go b/client/core/internal/pairing/pairing.go new file mode 100644 index 0000000..4f08b74 --- /dev/null +++ b/client/core/internal/pairing/pairing.go @@ -0,0 +1,83 @@ +package pairing + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "strconv" + "time" +) + +func httpJSON(method, url, token string, body interface{}) (int, []byte, error) { + var buf bytes.Buffer + if body != nil { + raw, _ := json.Marshal(body) + buf.Write(raw) + } + req, err := http.NewRequest(method, url, &buf) + if err != nil { + return 0, nil, err + } + req.Header.Set("Content-Type", "application/json") + if token != "" { + req.Header.Set("Authorization", "Bearer "+token) + } + client := &http.Client{Timeout: 10 * time.Second} + resp, err := client.Do(req) + if err != nil { + return 0, nil, err + } + defer resp.Body.Close() + raw, _ := io.ReadAll(resp.Body) + return resp.StatusCode, raw, nil +} + +func field(raw []byte, key string) string { + var m map[string]interface{} + _ = json.Unmarshal(raw, &m) + if d, ok := m["data"].(map[string]interface{}); ok { + if v, ok := d[key]; ok { + switch t := v.(type) { + case string: + return t + case float64: + return strconv.FormatInt(int64(t), 10) + } + } + } + return "" +} + +type ginH map[string]interface{} + +// AutoPair 开发便利:登录 → 取配对码 → 配对(无 UI 场景闭环用) +func AutoPair(server, email, password, deviceName, pubKeyB64 string) (uint64, error) { + code, raw, err := httpJSON("POST", server+"/api/v1/auth/login", "", ginH{"email": email, "password": password}) + if err != nil || code != 200 { + return 0, fmt.Errorf("login failed: %s", string(raw)) + } + token := field(raw, "accessToken") + code, raw, err = httpJSON("POST", server+"/api/v1/agent/pair-code", token, nil) + if err != nil || code != 200 { + return 0, fmt.Errorf("pair-code failed: %s", string(raw)) + } + pairCode := field(raw, "code") + return PairWithCode(server, pairCode, deviceName, "core", "0.2.0", pubKeyB64) +} + +// PairWithCode 凭配对码注册设备,返回服务器分配的设备 ID +func PairWithCode(server, pairCode, deviceName, osName, version, pubKeyB64 string) (uint64, error) { + code, raw, err := httpJSON("POST", server+"/api/v1/agent/pair", "", ginH{ + "code": pairCode, "deviceName": deviceName, "os": osName, "version": version, "publicKey": pubKeyB64, + }) + if err != nil || code != 200 { + return 0, fmt.Errorf("pair failed: %s", string(raw)) + } + id, _ := strconv.ParseUint(field(raw, "deviceId"), 10, 64) + if id == 0 { + return 0, fmt.Errorf("pair response missing deviceId") + } + return id, nil +} diff --git a/client/core/internal/vault/vault.go b/client/core/internal/vault/vault.go new file mode 100644 index 0000000..65ec119 --- /dev/null +++ b/client/core/internal/vault/vault.go @@ -0,0 +1,139 @@ +package vault + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/base64" + "encoding/json" + "errors" + "os" + "path/filepath" +) + +// Vault 凭据保险库:AES-256-GCM 加密的 JSON 文件(主密钥单独 0600 文件)。 +// 二期:Windows 上主密钥改用 DPAPI 保护,文件格式不变。 +type Vault struct { + dir string + key []byte +} + +type vaultFile struct { + Version int `json:"version"` + Entries map[string]string `json:"entries"` +} + +// Open 打开/创建保险库(主密钥 data/master.key,32 字节) +func Open(dir string) (*Vault, error) { + if err := os.MkdirAll(dir, 0o700); err != nil { + return nil, err + } + keyPath := filepath.Join(dir, "master.key") + var key []byte + if raw, err := os.ReadFile(keyPath); err == nil { + key = make([]byte, base64.RawStdEncoding.DecodedLen(len(raw))) + if _, err = base64.RawStdEncoding.Decode(key, raw); err != nil || len(key) != 32 { + return nil, errors.New("master key corrupted") + } + } else { + key = make([]byte, 32) + if _, err = rand.Read(key); err != nil { + return nil, err + } + if err = os.WriteFile(keyPath, []byte(base64.RawStdEncoding.EncodeToString(key)), 0o600); err != nil { + return nil, err + } + } + return &Vault{dir: dir, key: key}, nil +} + +func (v *Vault) path() string { return filepath.Join(v.dir, "vault.enc") } + +// load 读取并解密全部条目 +func (v *Vault) load() (map[string]string, error) { + raw, err := os.ReadFile(v.path()) + if err != nil { + if os.IsNotExist(err) { + return map[string]string{}, nil + } + return nil, err + } + var f vaultFile + if err = json.Unmarshal(raw, &f); err != nil { + return nil, err + } + if f.Entries == nil { + f.Entries = map[string]string{} + } + return f.Entries, nil +} + +// Save 加密保存单条凭据(cookie/token 等,值为 JSON 字符串) +func (v *Vault) Save(accountID string, plaintext []byte) error { + entries, err := v.load() + if err != nil { + return err + } + block, err := aes.NewCipher(v.key) + if err != nil { + return err + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return err + } + nonce := make([]byte, gcm.NonceSize()) + if _, err = rand.Read(nonce); err != nil { + return err + } + sealed := gcm.Seal(nonce, nonce, plaintext, nil) + entries[accountID] = base64.StdEncoding.EncodeToString(sealed) + return v.persist(entries) +} + +// Load 解密读取单条凭据 +func (v *Vault) Load(accountID string) ([]byte, error) { + entries, err := v.load() + if err != nil { + return nil, err + } + sealedB64, ok := entries[accountID] + if !ok { + return nil, errors.New("entry not found") + } + sealed, err := base64.StdEncoding.DecodeString(sealedB64) + if err != nil { + return nil, err + } + block, err := aes.NewCipher(v.key) + if err != nil { + return nil, err + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + if len(sealed) < gcm.NonceSize() { + return nil, errors.New("entry corrupted") + } + nonce, ciphertext := sealed[:gcm.NonceSize()], sealed[gcm.NonceSize():] + return gcm.Open(nil, nonce, ciphertext, nil) +} + +// Delete 删除单条凭据 +func (v *Vault) Delete(accountID string) error { + entries, err := v.load() + if err != nil { + return err + } + delete(entries, accountID) + return v.persist(entries) +} + +func (v *Vault) persist(entries map[string]string) error { + raw, err := json.MarshalIndent(vaultFile{Version: 1, Entries: entries}, "", " ") + if err != nil { + return err + } + return os.WriteFile(v.path(), raw, 0o600) +} diff --git a/client/core/internal/vault/vault_test.go b/client/core/internal/vault/vault_test.go new file mode 100644 index 0000000..df40644 --- /dev/null +++ b/client/core/internal/vault/vault_test.go @@ -0,0 +1,83 @@ +package vault + +import ( + "os" + "path/filepath" + "testing" +) + +func TestSaveLoadRoundTrip(t *testing.T) { + dir := t.TempDir() + v, err := Open(dir) + if err != nil { + t.Fatalf("open: %v", err) + } + secret := []byte(`{"cookies":"session=abc","token":"xyz"}`) + if err = v.Save("acc-1", secret); err != nil { + t.Fatalf("save: %v", err) + } + // 文件不应包含明文 + raw, _ := os.ReadFile(filepath.Join(dir, "vault.enc")) + if stringContains(string(raw), "session=abc") { + t.Fatal("vault file must not contain plaintext") + } + got, err := v.Load("acc-1") + if err != nil { + t.Fatalf("load: %v", err) + } + if string(got) != string(secret) { + t.Fatalf("roundtrip mismatch: %s vs %s", got, secret) + } +} + +func TestReopenPersists(t *testing.T) { + dir := t.TempDir() + v1, _ := Open(dir) + _ = v1.Save("acc-2", []byte("data-2")) + v2, err := Open(dir) + if err != nil { + t.Fatalf("reopen: %v", err) + } + got, err := v2.Load("acc-2") + if err != nil || string(got) != "data-2" { + t.Fatalf("reopen mismatch: %s %v", got, err) + } +} + +func TestTamperFails(t *testing.T) { + dir := t.TempDir() + v, _ := Open(dir) + _ = v.Save("acc-3", []byte("data-3")) + path := filepath.Join(dir, "vault.enc") + raw, _ := os.ReadFile(path) + raw[len(raw)/2] ^= 0xff + _ = os.WriteFile(path, raw, 0o600) + if _, err := v.Load("acc-3"); err == nil { + t.Fatal("tampered ciphertext should fail auth") + } +} + +func TestDelete(t *testing.T) { + dir := t.TempDir() + v, _ := Open(dir) + _ = v.Save("acc-4", []byte("d4")) + if err := v.Delete("acc-4"); err != nil { + t.Fatalf("delete: %v", err) + } + if _, err := v.Load("acc-4"); err == nil { + t.Fatal("deleted entry should not load") + } +} + +func stringContains(s, sub string) bool { + return len(s) >= len(sub) && indexOf(s, sub) >= 0 +} + +func indexOf(s, sub string) int { + for i := 0; i+len(sub) <= len(s); i++ { + if s[i:i+len(sub)] == sub { + return i + } + } + return -1 +} diff --git a/client/core/internal/wsclient/client.go b/client/core/internal/wsclient/client.go new file mode 100644 index 0000000..f1ff60b --- /dev/null +++ b/client/core/internal/wsclient/client.go @@ -0,0 +1,334 @@ +package wsclient + +import ( + "context" + "crypto/rand" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "log" + "strconv" + "strings" + "sync" + "time" + + "github.com/coder/websocket" + + "everypublish/agentcore/internal/device" + "everypublish/agentcore/internal/exec" + "everypublish/shared/proto" +) + +// Client WSS 客户端:指数退避重连 + 任务幂等重放 + 挑战自动处理 +type Client struct { + ServerURL string + Identity *device.Identity + Registry *exec.Registry + ExecTimeout time.Duration + + mu sync.Mutex + writeMu sync.Mutex + conn *websocket.Conn + backoff time.Duration + executed map[string]exec.Result + + chMu sync.Mutex + challenges map[string]proto.Challenge // 活跃挑战(WinUI 壳展示) + online bool + sessionID string +} + +// New 构造客户端 +func New(serverURL string, id *device.Identity, registry *exec.Registry) *Client { + return &Client{ + ServerURL: serverURL, + Identity: id, + Registry: registry, + ExecTimeout: 5 * time.Minute, + executed: make(map[string]exec.Result), + challenges: make(map[string]proto.Challenge), + } +} + +// Run 主循环:断线按 1s/5s/15s/60s 指数退避重连,握手成功重置 +func (c *Client) Run(ctx context.Context) error { + for { + err := c.session(ctx) + if ctx.Err() != nil { + return nil + } + c.stepBackoff() + log.Printf("[ws] connection lost (%v), reconnect in %s", err, c.backoff) + select { + case <-ctx.Done(): + return nil + case <-time.After(c.backoff): + } + } +} + +func (c *Client) stepBackoff() { + switch c.backoff { + case 0: + c.backoff = time.Second + case time.Second: + c.backoff = 5 * time.Second + case 5 * time.Second: + c.backoff = 15 * time.Second + default: + c.backoff = 60 * time.Second + } +} + +// session 单次连接会话:握手 → 心跳 → 消息循环 +func (c *Client) session(ctx context.Context) error { + wsURL := "ws" + strings.TrimPrefix(c.ServerURL, "http") + "/ws/agent" + conn, _, err := websocket.Dial(ctx, wsURL, nil) + if err != nil { + return err + } + defer conn.Close(websocket.StatusNormalClosure, "bye") + defer func() { + c.mu.Lock() + c.online = false + c.mu.Unlock() + }() + c.mu.Lock() + c.conn = conn + c.mu.Unlock() + + // hello 握手(Ed25519 签名) + nonce := randHex(16) + ts := time.Now().UnixMilli() + sig := c.Identity.Sign([]byte(nonce + "|" + strconv.FormatInt(ts, 10))) + hello := proto.NewEnvelope(randHex(8), proto.TypeHello, proto.DeviceHello{ + DeviceID: strconv.FormatUint(c.Identity.DeviceID, 10), + Nonce: nonce, + TS: ts, + Sig: sig, + Version: "0.2.0", + }) + if err = c.send(conn, hello); err != nil { + return err + } + rctx, cancel := context.WithTimeout(ctx, 10*time.Second) + _, raw, err := conn.Read(rctx) + cancel() + if err != nil { + return err + } + var ackEnv proto.Envelope + _ = json.Unmarshal(raw, &ackEnv) + if ackEnv.Type != proto.TypeHelloAck { + return fmt.Errorf("expect hello.ack, got %s", ackEnv.Type) + } + var ack proto.HelloAck + _ = json.Unmarshal(ackEnv.Payload, &ack) + c.mu.Lock() + c.backoff = 0 // 握手成功重置退避 + c.online = true + c.sessionID = ack.SessionID + c.mu.Unlock() + log.Printf("[ws] online, session %s", ack.SessionID) + + // 心跳(15s,服务器 90s 超时检测) + hbCtx, hbCancel := context.WithCancel(ctx) + defer hbCancel() + go c.heartbeatLoop(hbCtx) + + // 消息循环 + for { + typ, raw, err := conn.Read(ctx) + if err != nil { + return err + } + if typ != websocket.MessageText { + continue + } + var env proto.Envelope + if err = json.Unmarshal(raw, &env); err != nil { + continue + } + c.handle(conn, env) + } +} + +// handle 分发服务端消息 +func (c *Client) handle(conn *websocket.Conn, env proto.Envelope) { + switch env.Type { + case proto.TypeTaskPush: + var push proto.TaskPush + if err := json.Unmarshal(env.Payload, &push); err != nil { + return + } + c.handleTaskPush(conn, env, push) + case proto.TypeChallenge: + var ch proto.Challenge + if err := json.Unmarshal(env.Payload, &ch); err != nil { + return + } + c.handleChallenge(conn, env, ch) + case proto.TypeHeartbeatAck: + // 忽略 + default: + log.Printf("[ws] ignore msg %s", env.Type) + } +} + +// handleTaskPush 任务下发:幂等(已执行过直接重放结果)+ 执行 + 回传 +func (c *Client) handleTaskPush(conn *websocket.Conn, env proto.Envelope, push proto.TaskPush) { + c.mu.Lock() + if res, ok := c.executed[push.TaskID]; ok { + c.mu.Unlock() + log.Printf("[ws] task %s already executed, replay result", push.TaskID) + _ = c.send(conn, proto.NewEnvelope(env.ID, proto.TypeTaskAck, proto.TaskAck{TaskID: push.TaskID, Accept: true})) + _ = c.send(conn, proto.NewEnvelope(randHex(8), proto.TypeTaskResult, proto.TaskResult{ + TaskID: res.TaskID, Status: res.Status, PublishedURL: res.PublishedURL, + Receipts: res.Receipts, Error: res.Error, FinishedAt: time.Now().UnixMilli(), + })) + return + } + c.mu.Unlock() + + log.Printf("[ws] TASK push %s platform=%s title=%s", push.TaskID, push.Platform, push.Title) + _ = c.send(conn, proto.NewEnvelope(env.ID, proto.TypeTaskAck, proto.TaskAck{TaskID: push.TaskID, Accept: true})) + + task := exec.Task{ + TaskID: push.TaskID, Platform: push.Platform, AccountID: push.AccountID, AccountName: push.AccountName, + Title: push.Title, Content: push.Content, Tags: push.Tags, MaterialURLs: push.MaterialURLs, + ScheduleAt: push.ScheduleAt, Priority: push.Priority, + } + var res exec.Result + e := c.Registry.Get(push.Platform) + if e == nil { + res = exec.Result{TaskID: push.TaskID, Status: "failed", Error: "平台执行器未接入: " + push.Platform} + } else { + eCtx, cancel := context.WithTimeout(context.Background(), c.ExecTimeout) + res = e.Execute(eCtx, task) + cancel() + } + c.mu.Lock() + c.executed[push.TaskID] = res + if len(c.executed) > 1024 { + c.executed = make(map[string]exec.Result) + } + c.mu.Unlock() + _ = c.send(conn, proto.NewEnvelope(randHex(8), proto.TypeTaskResult, proto.TaskResult{ + TaskID: res.TaskID, Status: res.Status, PublishedURL: res.PublishedURL, + Receipts: res.Receipts, Error: res.Error, FinishedAt: time.Now().UnixMilli(), + })) + log.Printf("[ws] TASK %s done status=%s", push.TaskID, res.Status) +} + +// handleChallenge 挑战:ack + 有自动解决器则异步解决(60s 超时),否则留人工 +func (c *Client) handleChallenge(conn *websocket.Conn, env proto.Envelope, ch proto.Challenge) { + log.Printf("[ws] CHALLENGE %s kind=%s prompt=%s", ch.ChallengeID, ch.Kind, ch.Prompt) + c.chMu.Lock() + c.challenges[ch.ChallengeID] = ch + c.chMu.Unlock() + _ = c.send(conn, proto.NewEnvelope(env.ID, proto.TypeChallengeAck, proto.ChallengeAck{ChallengeID: ch.ChallengeID, Action: "accept"})) + solver := c.Registry.Solve(ch.Platform) + if solver == nil { + log.Printf("[ws] challenge %s 无自动解决器,等待人工处理", ch.ChallengeID) + return + } + // 平台生成二维码时更新挑战记录(壳经 /challenges 展示) + onQR := func(qrToken, qrURL string) { + c.chMu.Lock() + if cur, ok := c.challenges[ch.ChallengeID]; ok { + cur.QRToken = qrToken + cur.QRURL = qrURL + c.challenges[ch.ChallengeID] = cur + } + c.chMu.Unlock() + } + go func() { + sCtx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + if solved, value := solver.SolveChallenge(sCtx, ch, onQR); solved { + _ = c.send(conn, proto.NewEnvelope(randHex(8), proto.TypeChallengeSolve, proto.ChallengeSolve{ChallengeID: ch.ChallengeID, Value: value})) + c.chMu.Lock() + delete(c.challenges, ch.ChallengeID) + c.chMu.Unlock() + log.Printf("[ws] CHALLENGE %s solved", ch.ChallengeID) + } else { + log.Printf("[ws] CHALLENGE %s 未解决,等待人工", ch.ChallengeID) + } + }() +} + +// heartbeatLoop 心跳(15s) +func (c *Client) heartbeatLoop(ctx context.Context) { + ticker := time.NewTicker(15 * time.Second) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + c.mu.Lock() + conn := c.conn + c.mu.Unlock() + if conn == nil { + continue + } + _ = c.send(conn, proto.NewEnvelope(randHex(8), proto.TypeHeartbeat, proto.Heartbeat{DeviceID: strconv.FormatUint(c.Identity.DeviceID, 10), TS: time.Now().UnixMilli()})) + } + } +} + +// send 统一写出口(写锁串行化,避免并发写) +func (c *Client) send(conn *websocket.Conn, msg *proto.Envelope) error { + raw, err := json.Marshal(msg) + if err != nil { + return err + } + wctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + c.writeMu.Lock() + defer c.writeMu.Unlock() + return conn.Write(wctx, websocket.MessageText, raw) +} + +// Challenges 当前活跃挑战快照(WinUI 壳轮询展示) +func (c *Client) Challenges() []proto.Challenge { + c.chMu.Lock() + defer c.chMu.Unlock() + list := make([]proto.Challenge, 0, len(c.challenges)) + for _, ch := range c.challenges { + list = append(list, ch) + } + return list +} + +// SolveChallenge 人工完成挑战(验证码/确认),经 WSS 回传服务器 +func (c *Client) SolveChallenge(challengeID, value string) error { + c.mu.Lock() + conn := c.conn + c.mu.Unlock() + if conn == nil { + return errors.New("offline") + } + return c.send(conn, proto.NewEnvelope(randHex(8), proto.TypeChallengeSolve, proto.ChallengeSolve{ChallengeID: challengeID, Value: value})) +} + +// Online 当前是否在线 +func (c *Client) Online() bool { + c.mu.Lock() + defer c.mu.Unlock() + return c.online +} + +// SessionID 当前会话 ID +func (c *Client) SessionID() string { + c.mu.Lock() + defer c.mu.Unlock() + return c.sessionID +} + +func randHex(n int) string { + b := make([]byte, n) + _, _ = rand.Read(b) + return hex.EncodeToString(b) +} diff --git a/client/core/internal/wsclient/client_test.go b/client/core/internal/wsclient/client_test.go new file mode 100644 index 0000000..932beb7 --- /dev/null +++ b/client/core/internal/wsclient/client_test.go @@ -0,0 +1,207 @@ +package wsclient + +import ( + "context" + "crypto/ed25519" + "encoding/base64" + "encoding/json" + "net/http" + "net/http/httptest" + "strconv" + "sync/atomic" + "testing" + "time" + + "github.com/coder/websocket" + + "everypublish/agentcore/internal/device" + "everypublish/agentcore/internal/exec" + "everypublish/shared/proto" +) + +// countingExec 计数执行器(验证幂等:只执行一次) +type countingExec struct{ n int32 } + +func (c *countingExec) Platform() string { return "" } +func (c *countingExec) Execute(ctx context.Context, task exec.Task) exec.Result { + atomic.AddInt32(&c.n, 1) + return exec.Result{TaskID: task.TaskID, Status: "success", PublishedURL: "https://ex.com/" + task.TaskID} +} +func (c *countingExec) SolveChallenge(ctx context.Context, ch proto.Challenge) (bool, string) { + return true, "tok" +} + +// verifyHello 校验客户端 hello 签名 +func verifyHello(t *testing.T, id *device.Identity, raw []byte) { + t.Helper() + var env proto.Envelope + _ = json.Unmarshal(raw, &env) + var hello proto.DeviceHello + _ = json.Unmarshal(env.Payload, &hello) + pub, _ := base64.StdEncoding.DecodeString(id.PublicKey) + sig, _ := base64.StdEncoding.DecodeString(hello.Sig) + if !ed25519.Verify(ed25519.PublicKey(pub), []byte(hello.Nonce+"|"+strconv.FormatInt(hello.TS, 10)), sig) { + t.Errorf("hello signature invalid") + } +} + +// sendAck 回 hello.ack +func sendAck(r *http.Request, conn *websocket.Conn) { + ack := proto.NewEnvelope("ack1", proto.TypeHelloAck, proto.HelloAck{ServerNonce: "n", SessionID: "s1", ServerTS: 1}) + rawAck, _ := json.Marshal(ack) + _ = conn.Write(r.Context(), websocket.MessageText, rawAck) +} + +// startStub 启动 stub:验签 → ack → 推任务1 → 收结果 → 再推任务1(重放)→ 收重放结果 +func startStub(t *testing.T, id *device.Identity) (*httptest.Server, chan proto.TaskResult, chan struct{}) { + results := make(chan proto.TaskResult, 8) + connected := make(chan struct{}, 8) + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + conn, err := websocket.Accept(w, r, nil) + if err != nil { + return + } + defer conn.Close(websocket.StatusNormalClosure, "") + ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second) + _, raw, err := conn.Read(ctx) + cancel() + if err != nil { + return + } + verifyHello(t, id, raw) + sendAck(r, conn) + connected <- struct{}{} + + push := proto.NewEnvelope("p1", proto.TypeTaskPush, proto.TaskPush{TaskID: "1", Platform: "douyin", Title: "t1"}) + rawPush, _ := json.Marshal(push) + _ = conn.Write(r.Context(), websocket.MessageText, rawPush) + + gotFirst := false + for { + _, raw, err = conn.Read(r.Context()) + if err != nil { + return + } + var env2 proto.Envelope + if json.Unmarshal(raw, &env2) != nil { + continue + } + if env2.Type == proto.TypeTaskResult { + var res proto.TaskResult + _ = json.Unmarshal(env2.Payload, &res) + results <- res + if !gotFirst { + gotFirst = true + push2 := proto.NewEnvelope("p2", proto.TypeTaskPush, proto.TaskPush{TaskID: "1", Platform: "douyin", Title: "t1"}) + rawPush2, _ := json.Marshal(push2) + _ = conn.Write(r.Context(), websocket.MessageText, rawPush2) + } + } + } + })) + return srv, results, connected +} + +func TestClientTaskRoundTripAndIdempotent(t *testing.T) { + id, _ := device.LoadOrCreate(t.TempDir(), "dev") + id.DeviceID = 1 + registry := exec.NewRegistry() + ce := &countingExec{} + registry.SetFallback(ce) + + srv, results, connected := startStub(t, id) + defer srv.Close() + + c := New(srv.URL, id, registry) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go func() { _ = c.Run(ctx) }() + + select { + case <-connected: + case <-time.After(5 * time.Second): + t.Fatal("handshake timeout") + } + + var r1 proto.TaskResult + select { + case r1 = <-results: + case <-time.After(5 * time.Second): + t.Fatal("first result timeout") + } + if r1.Status != "success" { + t.Fatalf("first result should be success, got %s", r1.Status) + } + + var r2 proto.TaskResult + select { + case r2 = <-results: + case <-time.After(5 * time.Second): + t.Fatal("replay result timeout") + } + if r2.Status != "success" || r2.PublishedURL != r1.PublishedURL { + t.Fatalf("replay should return cached result, got %+v", r2) + } + + if n := atomic.LoadInt32(&ce.n); n != 1 { + t.Fatalf("executor should run exactly once, got %d", n) + } +} + +// TestClientReconnect 服务器断开后客户端应按退避自动重连 +func TestClientReconnect(t *testing.T) { + id, _ := device.LoadOrCreate(t.TempDir(), "dev") + id.DeviceID = 1 + registry := exec.NewRegistry() + registry.SetFallback(&countingExec{}) + + var connCount int32 + connected := make(chan struct{}, 8) + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + conn, err := websocket.Accept(w, r, nil) + if err != nil { + return + } + ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second) + _, raw, err := conn.Read(ctx) + cancel() + if err != nil { + return + } + verifyHello(t, id, raw) + sendAck(r, conn) + connected <- struct{}{} + n := atomic.AddInt32(&connCount, 1) + if n == 1 { + // 第一次连接:ack 后立刻断开,模拟服务器故障 + _ = conn.Close(websocket.StatusGoingAway, "restart") + return + } + // 后续连接:正常读循环 + for { + if _, _, err = conn.Read(r.Context()); err != nil { + return + } + } + })) + defer srv.Close() + + c := New(srv.URL, id, registry) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go func() { _ = c.Run(ctx) }() + + select { + case <-connected: + case <-time.After(5 * time.Second): + t.Fatal("first connect timeout") + } + select { + case <-connected: + case <-time.After(8 * time.Second): + t.Fatal("reconnect timeout") + } + if atomic.LoadInt32(&connCount) < 2 { + t.Fatal("expected second connection") + } +} diff --git a/client/core/main.go b/client/core/main.go new file mode 100644 index 0000000..a8e768e --- /dev/null +++ b/client/core/main.go @@ -0,0 +1,162 @@ +// agent-core 客户端核心:设备身份 + 凭据保险库 + WSS 长连接 + 平台执行器。 +// 常驻模型:WinUI 壳 spawn 本进程 → 壳读 data/local.json(随机端口+token)→ +// 经 localhost HTTP 控制(状态/配对/挑战)。CLI 亦可独立运行(-user/-pass 自动配对)。 +package main + +import ( + "context" + "errors" + "flag" + "log" + "os" + "path/filepath" + "os/signal" + "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("设备已配对") + } + 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") +} diff --git a/client/desktop/gen-ico.js b/client/desktop/gen-ico.js new file mode 100644 index 0000000..c8f1429 --- /dev/null +++ b/client/desktop/gen-ico.js @@ -0,0 +1,13 @@ +const fs = require('fs'); +const p = require('path'); +const png = fs.readFileSync(p.join(__dirname, 'icons', 'app.png')); +const hdr = Buffer.alloc(6); +hdr.writeUInt16LE(1, 2); +hdr.writeUInt16LE(1, 4); +const ent = Buffer.alloc(16); +ent.writeUInt16LE(1, 4); +ent.writeUInt16LE(32, 6); +ent.writeUInt32LE(png.length, 8); +ent.writeUInt32LE(22, 12); +fs.writeFileSync(p.join(__dirname, 'icons', 'app.ico'), Buffer.concat([hdr, ent, png])); +console.log('app.ico bytes:', 22 + png.length); diff --git a/client/desktop/gen-icon.js b/client/desktop/gen-icon.js new file mode 100644 index 0000000..dc0653c --- /dev/null +++ b/client/desktop/gen-icon.js @@ -0,0 +1,50 @@ +// 生成 icons/app.png(256x256 纯蓝方块图标) +const zlib = require("zlib"); +const fs = require("fs"); +const path = require("path"); + +const W = 256, H = 256; +const row = Buffer.alloc(1 + W * 4); +row[0] = 0; +for (let x = 0; x < W; x++) { + row[1 + x * 4] = 0; + row[2 + x * 4] = 110; + row[3 + x * 4] = 255; + row[4 + x * 4] = 255; +} +const raw = Buffer.concat(Array(H).fill(row)); + +const crcTable = []; +for (let n = 0; n < 256; n++) { + let c = n; + for (let k = 0; k < 8; k++) c = (c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1); + crcTable[n] = c >>> 0; +} +function crc32(buf) { + let c = 0xffffffff; + for (const b of buf) c = crcTable[(c ^ b) & 0xff] ^ (c >>> 8); + return (c ^ 0xffffffff) >>> 0; +} +function chunk(type, data) { + const len = Buffer.alloc(4); + len.writeUInt32BE(data.length); + const t = Buffer.from(type, "ascii"); + const crc = Buffer.alloc(4); + crc.writeUInt32BE(crc32(Buffer.concat([t, data]))); + return Buffer.concat([len, t, data, crc]); +} + +const ihdr = Buffer.alloc(13); +ihdr.writeUInt32BE(W, 0); +ihdr.writeUInt32BE(H, 4); +ihdr[8] = 8; // bit depth +ihdr[9] = 6; // color type RGBA +const png = Buffer.concat([ + Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), + chunk("IHDR", ihdr), + chunk("IDAT", zlib.deflateSync(raw)), + chunk("IEND", Buffer.alloc(0)), +]); +fs.mkdirSync(path.join(__dirname, "icons"), { recursive: true }); +fs.writeFileSync(path.join(__dirname, "icons", "app.png"), png); +console.log("icon generated:", png.length, "bytes"); diff --git a/client/desktop/icons/app.ico b/client/desktop/icons/app.ico new file mode 100644 index 0000000..8304a54 Binary files /dev/null and b/client/desktop/icons/app.ico differ diff --git a/client/desktop/icons/app.png b/client/desktop/icons/app.png new file mode 100644 index 0000000..37c6e43 Binary files /dev/null and b/client/desktop/icons/app.png differ diff --git a/client/desktop/main.js b/client/desktop/main.js new file mode 100644 index 0000000..2da966e --- /dev/null +++ b/client/desktop/main.js @@ -0,0 +1,177 @@ +const { app, BrowserWindow, Tray, Menu, ipcMain, shell, nativeImage } = require('electron'); +const { spawn } = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const http = require('http'); + +const isWin = process.platform === 'win32'; +const isSmoke = process.argv.includes('--smoke'); +const minimized = process.argv.includes('--minimized'); + +let win = null; +let tray = null; +let coreProc = null; +let corePort = 0; +let coreToken = ''; +let localJsonTimer = null; +let quitting = false; + +const dataDir = path.join(app.getPath('userData'), 'data'); +const coreBin = isWin ? 'agent-core.exe' : 'agent-core'; +const coreExe = app.isPackaged + ? path.join(process.resourcesPath, coreBin) + : path.join(__dirname, '..', 'dist', coreBin); + +function startCore() { + if (!fs.existsSync(coreExe)) { + console.error('[core] binary not found: ' + coreExe); + win?.webContents.send('core-error', '核心程序缺失:' + coreExe); + return; + } + fs.mkdirSync(dataDir, { recursive: true }); + try { coreProc?.kill(); } catch (e) { /* ignore */ } + coreProc = spawn(coreExe, ['-dir', dataDir], { windowsHide: true }); + coreProc.on('exit', (code) => { + coreProc = null; + win?.webContents.send('core-exit', code); + }); + pollLocalJson(); +} + +function pollLocalJson() { + if (localJsonTimer) clearInterval(localJsonTimer); + localJsonTimer = setInterval(() => { + try { + const info = JSON.parse(fs.readFileSync(path.join(dataDir, 'local.json'), 'utf8')); + if (info.port && info.token) { + corePort = info.port; + coreToken = info.token; + clearInterval(localJsonTimer); + win?.webContents.send('core-ready'); + } + } catch (e) { /* 核心尚未就绪 */ } + }, 800); +} + +function coreRequest(method, p, body) { + return new Promise((resolve, reject) => { + if (!corePort || !coreToken) { reject(new Error('核心未就绪')); return; } + const data = body ? JSON.stringify(body) : null; + const req = http.request({ + host: '127.0.0.1', + port: corePort, + path: p, + method: method, + headers: { + 'X-Agent-Token': coreToken, + 'Content-Type': 'application/json', + 'Content-Length': data ? Buffer.byteLength(data) : 0, + }, + }, (res) => { + let raw = ''; + res.on('data', (c) => { raw += c; }); + res.on('end', () => { + try { resolve(JSON.parse(raw)); } catch (e) { resolve({ raw }); } + }); + }); + req.on('error', reject); + req.setTimeout(8000, () => { req.destroy(new Error('核心请求超时')); }); + if (data) req.write(data); + req.end(); + }); +} + +// ---- IPC(渲染进程 → 核心/系统) ---- +ipcMain.handle('core:status', () => coreRequest('GET', '/status')); +ipcMain.handle('core:pair', (e, code, name) => coreRequest('POST', '/pair', { code, name })); +ipcMain.handle('core:challenges', () => coreRequest('GET', '/challenges')); +ipcMain.handle('core:solve', (e, id, value) => coreRequest('POST', '/challenges/' + encodeURIComponent(id), { value })); +ipcMain.handle('core:get-config', () => readConfig()); +ipcMain.handle('core:save-config', (e, cfg) => { writeConfig(cfg); startCore(); return true; }); +ipcMain.handle('core:restart', () => { startCore(); return true; }); +ipcMain.handle('app:open-data-dir', () => shell.openPath(dataDir)); +ipcMain.handle('app:open-web', (e, url) => shell.openExternal(url)); +ipcMain.handle('app:set-autostart', (e, on) => { + app.setLoginItemSettings({ openAtLogin: !!on }); + return app.getLoginItemSettings().openAtLogin; +}); +ipcMain.handle('app:get-autostart', () => app.getLoginItemSettings().openAtLogin); +ipcMain.handle('app:quit', () => { quitting = true; app.quit(); }); + +function readConfig() { + try { return JSON.parse(fs.readFileSync(path.join(dataDir, 'config.json'), 'utf8')); } + catch (e) { return { server: '', fakeAll: false }; } +} +function writeConfig(cfg) { + fs.mkdirSync(dataDir, { recursive: true }); + fs.writeFileSync(path.join(dataDir, 'config.json'), JSON.stringify(cfg, null, 2)); +} + +function createTray() { + let img = nativeImage.createFromPath(path.join(__dirname, 'icons', 'app.png')); + if (img.isEmpty()) img = nativeImage.createEmpty(); + tray = new Tray(img); + tray.setToolTip('EveryPublish 客户端'); + tray.setContextMenu(Menu.buildFromTemplate([ + { label: '打开主窗口', click: showWin }, + { type: 'separator' }, + { label: '退出', click: () => { quitting = true; app.quit(); } }, + ])); + tray.on('click', showWin); +} + +function showWin() { + if (win) { win.show(); win.focus(); } +} + +function createWindow() { + win = new BrowserWindow({ + width: 980, height: 660, + minWidth: 780, minHeight: 540, + title: 'EveryPublish 客户端', + autoHideMenuBar: true, + webPreferences: { + preload: path.join(__dirname, 'preload.js'), + contextIsolation: true, + nodeIntegration: false, + }, + }); + win.loadFile(path.join(__dirname, 'renderer', 'index.html')); + win.on('close', (e) => { + if (!quitting) { e.preventDefault(); win.hide(); } + }); +} + +app.whenReady().then(() => { + app.setAppUserModelId('com.everypublish.client'); + if (isSmoke) { + // 冒烟模式:启动 core → 等就绪 → 查状态 → 退出(无窗口) + runSmoke(); + return; + } + createTray(); + createWindow(); + if (minimized) win?.hide(); + startCore(); +}); + +async function runSmoke() { + startCore(); + const deadline = Date.now() + 30000; + while (!coreToken && Date.now() < deadline) { + await new Promise((r) => setTimeout(r, 500)); + } + if (!coreToken) { console.log('SMOKE_RESULT ' + JSON.stringify({ error: 'core 未就绪' })); app.exit(1); return; } + try { + const status = await coreRequest('GET', '/status'); + console.log('SMOKE_RESULT ' + JSON.stringify(status)); + app.exit(0); + } catch (e) { + console.log('SMOKE_RESULT ' + JSON.stringify({ error: String(e) })); + app.exit(1); + } +} + +app.on('before-quit', () => { quitting = true; try { coreProc?.kill(); } catch (e) { /* ignore */ } }); +app.on('window-all-closed', () => { /* 驻留托盘不退出 */ }); +app.on('activate', showWin); diff --git a/client/desktop/package-lock.json b/client/desktop/package-lock.json new file mode 100644 index 0000000..98560b8 --- /dev/null +++ b/client/desktop/package-lock.json @@ -0,0 +1,5285 @@ +{ + "name": "everypublish-client", + "version": "0.2.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "everypublish-client", + "version": "0.2.0", + "devDependencies": { + "electron": "^31.7.7", + "electron-builder": "^25.1.8" + } + }, + "node_modules/@develar/schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npmmirror.com/@develar/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@electron/asar": { + "version": "3.4.1", + "resolved": "https://registry.npmmirror.com/@electron/asar/-/asar-3.4.1.tgz", + "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@electron/asar/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@electron/asar/node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@electron/asar/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@electron/get": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/@electron/get/-/get-2.0.3.tgz", + "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/@electron/notarize": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/@electron/notarize/-/notarize-2.5.0.tgz", + "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/notarize/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/notarize/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/osx-sign": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/@electron/osx-sign/-/osx-sign-1.3.1.tgz", + "integrity": "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/osx-sign/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmmirror.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/@electron/osx-sign/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/osx-sign/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/rebuild": { + "version": "3.6.1", + "resolved": "https://registry.npmmirror.com/@electron/rebuild/-/rebuild-3.6.1.tgz", + "integrity": "sha512-f6596ZHpEq/YskUd8emYvOUne89ij8mQgjYFA5ru25QwbrRO+t1SImofdDv7kKOuWCmVOuU5tvfkbgGxIl3E/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@malept/cross-spawn-promise": "^2.0.0", + "chalk": "^4.0.0", + "debug": "^4.1.1", + "detect-libc": "^2.0.1", + "fs-extra": "^10.0.0", + "got": "^11.7.0", + "node-abi": "^3.45.0", + "node-api-version": "^0.2.0", + "node-gyp": "^9.0.0", + "ora": "^5.1.0", + "read-binary-file-arch": "^1.0.6", + "semver": "^7.3.5", + "tar": "^6.0.5", + "yargs": "^17.0.1" + }, + "bin": { + "electron-rebuild": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/@electron/rebuild/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/rebuild/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/rebuild/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/rebuild/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/universal": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/@electron/universal/-/universal-2.0.1.tgz", + "integrity": "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "^3.2.7", + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.3.1", + "dir-compare": "^4.2.0", + "fs-extra": "^11.1.1", + "minimatch": "^9.0.3", + "plist": "^3.1.0" + }, + "engines": { + "node": ">=16.4" + } + }, + "node_modules/@electron/universal/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@electron/universal/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@electron/universal/node_modules/fs-extra": { + "version": "11.4.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.4.0.tgz", + "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/universal/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/universal/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@electron/universal/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@malept/cross-spawn-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", + "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/@malept/flatpak-bundler": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", + "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "tmp-promise": "^3.0.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/fs/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmmirror.com/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmmirror.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.1.tgz", + "integrity": "sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmmirror.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmmirror.com/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmmirror.com/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmmirror.com/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/plist": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/@types/plist/-/plist-3.0.5.tgz", + "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*", + "xmlbuilder": ">=11.0.1" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/verror": { + "version": "1.10.11", + "resolved": "https://registry.npmmirror.com/@types/verror/-/verror-1.10.11.tgz", + "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmmirror.com/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.11", + "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.9.11.tgz", + "integrity": "sha512-tW8bcK3hsG0/uqSnNz6TK4BkcuZSezoU7DlnYssILmZDktPnSHHuDJJFM0AJv+13gz2r0iGdrj6qqKeUnxXEDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/7zip-bin": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/7zip-bin/-/7zip-bin-5.2.0.tgz", + "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmmirror.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/app-builder-bin": { + "version": "5.0.0-alpha.10", + "resolved": "https://registry.npmmirror.com/app-builder-bin/-/app-builder-bin-5.0.0-alpha.10.tgz", + "integrity": "sha512-Ev4jj3D7Bo+O0GPD2NMvJl+PGiBAfS7pUGawntBNpCbxtpncfUixqFj9z9Jme7V7s3LBGqsWZZP54fxBX3JKJw==", + "dev": true, + "license": "MIT" + }, + "node_modules/app-builder-lib": { + "version": "25.1.8", + "resolved": "https://registry.npmmirror.com/app-builder-lib/-/app-builder-lib-25.1.8.tgz", + "integrity": "sha512-pCqe7dfsQFBABC1jeKZXQWhGcCPF3rPCXDdfqVKjIeWBcXzyC1iOWZdfFhGl+S9MyE/k//DFmC6FzuGAUudNDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@develar/schema-utils": "~2.6.5", + "@electron/notarize": "2.5.0", + "@electron/osx-sign": "1.3.1", + "@electron/rebuild": "3.6.1", + "@electron/universal": "2.0.1", + "@malept/flatpak-bundler": "^0.4.0", + "@types/fs-extra": "9.0.13", + "async-exit-hook": "^2.0.1", + "bluebird-lst": "^1.0.9", + "builder-util": "25.1.7", + "builder-util-runtime": "9.2.10", + "chromium-pickle-js": "^0.2.0", + "config-file-ts": "0.2.8-rc1", + "debug": "^4.3.4", + "dotenv": "^16.4.5", + "dotenv-expand": "^11.0.6", + "ejs": "^3.1.8", + "electron-publish": "25.1.7", + "form-data": "^4.0.0", + "fs-extra": "^10.1.0", + "hosted-git-info": "^4.1.0", + "is-ci": "^3.0.0", + "isbinaryfile": "^5.0.0", + "js-yaml": "^4.1.0", + "json5": "^2.2.3", + "lazy-val": "^1.0.5", + "minimatch": "^10.0.0", + "resedit": "^1.7.0", + "sanitize-filename": "^1.6.3", + "semver": "^7.3.8", + "tar": "^6.1.12", + "temp-file": "^3.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "dmg-builder": "25.1.8", + "electron-builder-squirrel-windows": "25.1.8" + } + }, + "node_modules/app-builder-lib/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/app-builder-lib/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/app-builder-lib/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/app-builder-lib/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aproba": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "dev": true, + "license": "ISC" + }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmmirror.com/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/bluebird-lst": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/bluebird-lst/-/bluebird-lst-1.0.9.tgz", + "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "^3.5.5" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/builder-util": { + "version": "25.1.7", + "resolved": "https://registry.npmmirror.com/builder-util/-/builder-util-25.1.7.tgz", + "integrity": "sha512-7jPjzBwEGRbwNcep0gGNpLXG9P94VA3CPAZQCzxkFXiV2GMQKlziMbY//rXPI7WKfhsvGgFXjTcXdBEwgXw9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.6", + "7zip-bin": "~5.2.0", + "app-builder-bin": "5.0.0-alpha.10", + "bluebird-lst": "^1.0.9", + "builder-util-runtime": "9.2.10", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "debug": "^4.3.4", + "fs-extra": "^10.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "is-ci": "^3.0.0", + "js-yaml": "^4.1.0", + "source-map-support": "^0.5.19", + "stat-mode": "^1.0.0", + "temp-file": "^3.4.0" + } + }, + "node_modules/builder-util-runtime": { + "version": "9.2.10", + "resolved": "https://registry.npmmirror.com/builder-util-runtime/-/builder-util-runtime-9.2.10.tgz", + "integrity": "sha512-6p/gfG1RJSQeIbz8TK5aPNkoztgY1q5TgmGFMAXcY8itsGW6Y2ld1ALsZ5UJn8rog7hKF3zHx5iQbNQ8uLcRlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/builder-util/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/builder-util/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/builder-util/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/cacache": { + "version": "16.1.3", + "resolved": "https://registry.npmmirror.com/cacache/-/cacache-16.1.3.tgz", + "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/cacache/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmmirror.com/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmmirror.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmmirror.com/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmmirror.com/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/config-file-ts": { + "version": "0.2.8-rc1", + "resolved": "https://registry.npmmirror.com/config-file-ts/-/config-file-ts-0.2.8-rc1.tgz", + "integrity": "sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^10.3.12", + "typescript": "^5.4.3" + } + }, + "node_modules/config-file-ts/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/config-file-ts/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/config-file-ts/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmmirror.com/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/crc": { + "version": "3.8.0", + "resolved": "https://registry.npmmirror.com/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer": "^5.1.0" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/dir-compare": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/dir-compare/-/dir-compare-4.2.0.tgz", + "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "^3.0.5", + "p-limit": "^3.1.0 " + } + }, + "node_modules/dir-compare/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dir-compare/node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/dir-compare/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dmg-builder": { + "version": "25.1.8", + "resolved": "https://registry.npmmirror.com/dmg-builder/-/dmg-builder-25.1.8.tgz", + "integrity": "sha512-NoXo6Liy2heSklTI5OIZbCgXC1RzrDQsZkeEwXhdOro3FT1VBOvbubvscdPnjVuQ4AMwwv61oaH96AbiYg9EnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "25.1.8", + "builder-util": "25.1.7", + "builder-util-runtime": "9.2.10", + "fs-extra": "^10.1.0", + "iconv-lite": "^0.6.2", + "js-yaml": "^4.1.0" + }, + "optionalDependencies": { + "dmg-license": "^1.0.11" + } + }, + "node_modules/dmg-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dmg-builder/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/dmg-builder/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/dmg-license": { + "version": "1.0.11", + "resolved": "https://registry.npmmirror.com/dmg-license/-/dmg-license-1.0.11.tgz", + "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "@types/plist": "^3.0.1", + "@types/verror": "^1.10.3", + "ajv": "^6.10.0", + "crc": "^3.8.0", + "iconv-corefoundation": "^1.1.7", + "plist": "^3.0.4", + "smart-buffer": "^4.0.2", + "verror": "^1.10.0" + }, + "bin": { + "dmg-license": "bin/dmg-license.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "11.0.7", + "resolved": "https://registry.npmmirror.com/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmmirror.com/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron": { + "version": "31.7.7", + "resolved": "https://registry.npmmirror.com/electron/-/electron-31.7.7.tgz", + "integrity": "sha512-HZtZg8EHsDGnswFt0QeV8If8B+et63uD6RJ7I4/xhcXqmTIbI08GoubX/wm+HdY0DwcuPe1/xsgqpmYvjdjRoA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@electron/get": "^2.0.0", + "@types/node": "^20.9.0", + "extract-zip": "^2.0.1" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 12.20.55" + } + }, + "node_modules/electron-builder": { + "version": "25.1.8", + "resolved": "https://registry.npmmirror.com/electron-builder/-/electron-builder-25.1.8.tgz", + "integrity": "sha512-poRgAtUHHOnlzZnc9PK4nzG53xh74wj2Jy7jkTrqZ0MWPoHGh1M2+C//hGeYdA+4K8w4yiVCNYoLXF7ySj2Wig==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "25.1.8", + "builder-util": "25.1.7", + "builder-util-runtime": "9.2.10", + "chalk": "^4.1.2", + "dmg-builder": "25.1.8", + "fs-extra": "^10.1.0", + "is-ci": "^3.0.0", + "lazy-val": "^1.0.5", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "electron-builder": "cli.js", + "install-app-deps": "install-app-deps.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/electron-builder-squirrel-windows": { + "version": "25.1.8", + "resolved": "https://registry.npmmirror.com/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-25.1.8.tgz", + "integrity": "sha512-2ntkJ+9+0GFP6nAISiMabKt6eqBB0kX1QqHNWFWAXgi0VULKGisM46luRFpIBiU3u/TDmhZMM8tzvo2Abn3ayg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "app-builder-lib": "25.1.8", + "archiver": "^5.3.1", + "builder-util": "25.1.7", + "fs-extra": "^10.1.0" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-builder/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-builder/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-publish": { + "version": "25.1.7", + "resolved": "https://registry.npmmirror.com/electron-publish/-/electron-publish-25.1.7.tgz", + "integrity": "sha512-+jbTkR9m39eDBMP4gfbqglDd6UvBC7RLh5Y0MhFSsc6UkGHj9Vj9TWobxevHYMMqmoujL11ZLjfPpMX+Pt6YEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^9.0.11", + "builder-util": "25.1.7", + "builder-util-runtime": "9.2.10", + "chalk": "^4.1.2", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" + } + }, + "node_modules/electron-publish/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-publish/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-publish/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmmirror.com/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "optional": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-agent/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmmirror.com/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-corefoundation": { + "version": "1.1.7", + "resolved": "https://registry.npmmirror.com/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", + "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "cli-truncate": "^2.1.0", + "node-addon-api": "^1.6.3" + }, + "engines": { + "node": "^8.11.2 || >=10" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true, + "license": "ISC" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.5.0", + "resolved": "https://registry.npmmirror.com/ip-address/-/ip-address-10.5.0.tgz", + "integrity": "sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/isbinaryfile": { + "version": "5.0.7", + "resolved": "https://registry.npmmirror.com/isbinaryfile/-/isbinaryfile-5.0.7.tgz", + "integrity": "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmmirror.com/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/js-yaml": { + "version": "4.3.1", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmmirror.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmmirror.com/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmmirror.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/minipass-flush/-/minipass-flush-1.0.7.tgz", + "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.94.0", + "resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-3.94.0.tgz", + "integrity": "sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-api-version": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/node-api-version/-/node-api-version-0.2.1.tgz", + "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + } + }, + "node_modules/node-api-version/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp": { + "version": "9.4.1", + "resolved": "https://registry.npmmirror.com/node-gyp/-/node-gyp-9.4.1.tgz", + "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.13 || ^14.13 || >=16" + } + }, + "node_modules/node-gyp/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nopt": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmmirror.com/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/pe-library": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/pe-library/-/pe-library-0.4.1.tgz", + "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/plist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/plist/-/plist-3.1.1.tgz", + "integrity": "sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.9.10", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-binary-file-arch": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", + "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "bin": { + "read-binary-file-arch": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resedit": { + "version": "1.7.2", + "resolved": "https://registry.npmmirror.com/resedit/-/resedit-1.7.2.tgz", + "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pe-library": "^0.4.1" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmmirror.com/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sanitize-filename": { + "version": "1.6.4", + "resolved": "https://registry.npmmirror.com/sanitize-filename/-/sanitize-filename-1.6.4.tgz", + "integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==", + "dev": true, + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmmirror.com/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.9", + "resolved": "https://registry.npmmirror.com/socks/-/socks-2.8.9.tgz", + "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmmirror.com/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/temp-file": { + "version": "3.4.0", + "resolved": "https://registry.npmmirror.com/temp-file/-/temp-file-3.4.0.tgz", + "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-exit-hook": "^2.0.1", + "fs-extra": "^10.0.0" + } + }, + "node_modules/temp-file/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/temp-file/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/temp-file/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmmirror.com/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tmp": "^0.2.0" + } + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unique-filename": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/unique-slug": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmmirror.com/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmmirror.com/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmmirror.com/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + } + } +} diff --git a/client/desktop/package.json b/client/desktop/package.json new file mode 100644 index 0000000..18d01cb --- /dev/null +++ b/client/desktop/package.json @@ -0,0 +1,35 @@ +{ + "name": "everypublish-client", + "version": "0.2.0", + "description": "EveryPublish Windows 客户端(Electron 壳 + Go agent-core)", + "main": "main.js", + "scripts": { + "start": "electron .", + "gen-icon": "node gen-icon.js", + "dist:win": "electron-builder --win nsis --x64" + }, + "devDependencies": { + "electron": "^31.7.7", + "electron-builder": "^25.1.8" + }, + "build": { + "appId": "com.everypublish.client", + "productName": "EveryPublish", + "directories": { "output": "release" }, + "files": ["main.js", "preload.js", "renderer/**/*", "icons/**/*"], + "extraResources": [ + { "from": "../dist/agent-core.exe", "to": "agent-core.exe" } + ], + "win": { + "target": [{ "target": "nsis", "arch": ["x64"] }], + "icon": "icons/app.ico", + "signAndEditExecutable": false + }, + "nsis": { + "oneClick": false, + "allowToChangeInstallationDirectory": true, + "createDesktopShortcut": true, + "createStartMenuShortcut": true + } + } +} diff --git a/client/desktop/preload.js b/client/desktop/preload.js new file mode 100644 index 0000000..60f91aa --- /dev/null +++ b/client/desktop/preload.js @@ -0,0 +1,19 @@ +const { contextBridge, ipcRenderer } = require('electron'); + +contextBridge.exposeInMainWorld('api', { + status: () => ipcRenderer.invoke('core:status'), + pair: (code, name) => ipcRenderer.invoke('core:pair', code, name), + challenges: () => ipcRenderer.invoke('core:challenges'), + solve: (id, value) => ipcRenderer.invoke('core:solve', id, value), + getConfig: () => ipcRenderer.invoke('core:get-config'), + saveConfig: (cfg) => ipcRenderer.invoke('core:save-config', cfg), + restart: () => ipcRenderer.invoke('core:restart'), + openDataDir: () => ipcRenderer.invoke('app:open-data-dir'), + openWeb: (url) => ipcRenderer.invoke('app:open-web', url), + setAutoStart: (on) => ipcRenderer.invoke('app:set-autostart', on), + getAutoStart: () => ipcRenderer.invoke('app:get-autostart'), + quit: () => ipcRenderer.invoke('app:quit'), + onCoreReady: (cb) => ipcRenderer.on('core-ready', cb), + onCoreError: (cb) => ipcRenderer.on('core-error', cb), + onCoreExit: (cb) => ipcRenderer.on('core-exit', cb), +}); diff --git a/client/desktop/renderer/app.js b/client/desktop/renderer/app.js new file mode 100644 index 0000000..8ac4320 --- /dev/null +++ b/client/desktop/renderer/app.js @@ -0,0 +1,179 @@ +// EveryPublish 客户端渲染进程逻辑 +const $ = (id) => document.getElementById(id); + +// ---- 页签切换 ---- +document.querySelectorAll('.tab').forEach((tab) => { + tab.addEventListener('click', () => { + document.querySelectorAll('.tab').forEach((t) => t.classList.remove('active')); + document.querySelectorAll('.page').forEach((p) => p.classList.remove('active')); + tab.classList.add('active'); + $('page-' + tab.dataset.tab).classList.add('active'); + }); +}); + +// ---- 状态页 ---- +let currentServer = ''; +async function refreshStatus() { + try { + const s = await window.api.status(); + if (s && s.server) currentServer = s.server; + $('st-server').textContent = (s && s.server) || '-'; + $('st-device').textContent = s && s.deviceId > 0 ? '#' + s.deviceId + '(' + (s.name || '') + ')' : '未配对'; + $('st-session').textContent = (s && s.session) || '-'; + const dot = $('st-dot'); + if (!s) { $('st-online').textContent = '核心未就绪'; dot.className = 'dot gray'; } + else if (s.online) { $('st-online').textContent = '在线'; dot.className = 'dot green'; } + else { $('st-online').textContent = s.deviceId > 0 ? '离线(自动重连中)' : '未配对'; dot.className = 'dot orange'; } + } catch (e) { + $('st-online').textContent = '核心未就绪'; + $('st-dot').className = 'dot gray'; + } +} +setInterval(refreshStatus, 3000); +refreshStatus(); + +$('btn-open-web').addEventListener('click', () => { + const url = currentServer || 'http://127.0.0.1:8090'; + window.api.openWeb(url); +}); +$('btn-open-data').addEventListener('click', () => window.api.openDataDir()); + +// ---- 配对页 ---- +$('btn-pair').addEventListener('click', async () => { + const code = $('pair-code').value.trim(); + const name = $('pair-name').value.trim(); + const res = $('pair-result'); + if (!code) { res.textContent = '请输入配对码'; res.className = 'msg err'; return; } + $('btn-pair').disabled = true; + res.textContent = '配对中...'; + res.className = 'msg'; + try { + const r = await window.api.pair(code, name); + if (r && r.code === 0) { + res.textContent = '配对成功,正在连接服务器(可到「状态」页查看)'; + res.className = 'msg ok'; + } else { + res.textContent = (r && r.message) || '配对失败'; + res.className = 'msg err'; + } + } catch (e) { + res.textContent = '配对失败:' + e; + res.className = 'msg err'; + } + $('btn-pair').disabled = false; +}); + +// ---- 挑战页 ---- +let selectedChallenge = null; +let pollTimer = null; +const KIND_LABEL = { qr: '扫码', sms: '短信验证码', confirm: 'APP 确认', captcha: '图形验证码', pending: '等待设备' }; + +async function refreshChallenges() { + try { + const r = await window.api.challenges(); + const list = (r && r.list) || []; + const badge = $('ch-badge'); + if (list.length > 0) { badge.textContent = list.length; badge.classList.remove('hidden'); } + else badge.classList.add('hidden'); + const box = $('ch-list'); + box.innerHTML = ''; + list.forEach((ch) => { + const div = document.createElement('div'); + div.className = 'item' + (selectedChallenge && selectedChallenge.challengeId === ch.challengeId ? ' selected' : ''); + const inner = [ + '', + ch.platform || '?', + ' · ', + KIND_LABEL[ch.kind] || ch.kind, + '', + ch.prompt || '', + '', + ].join(''); + div.innerHTML = inner; + div.addEventListener('click', () => selectChallenge(ch)); + box.appendChild(div); + }); + if (!list.length) { + box.innerHTML = '

暂无待处理挑战

'; + } + } catch (e) { /* 忽略 */ } +} + +function selectChallenge(ch) { + selectedChallenge = ch; + refreshChallenges(); + const detail = $('ch-detail'); + detail.classList.remove('hidden'); + const qr = ch.qrUrl || ch.qrToken || ''; + const img = $('ch-qr'); + if (qr && (qr.startsWith('http') || qr.startsWith('file://'))) { + img.src = qr; + img.classList.remove('hidden'); + } else { + img.classList.add('hidden'); + } + $('ch-value').value = ''; + $('ch-result').textContent = ''; +} + +$('btn-solve').addEventListener('click', async () => { + if (!selectedChallenge) return; + const value = $('ch-value').value.trim() || 'confirmed'; + const res = $('ch-result'); + try { + const r = await window.api.solve(selectedChallenge.challengeId, value); + if (r && r.code === 0) { + res.textContent = '已提交,等待服务器确认'; + res.className = 'msg ok'; + selectedChallenge = null; + $('ch-detail').classList.add('hidden'); + refreshChallenges(); + } else { + res.textContent = (r && r.message) || '提交失败'; + res.className = 'msg err'; + } + } catch (e) { + res.textContent = '提交失败:' + e; + res.className = 'msg err'; + } +}); + +function startPolling() { + if (pollTimer) return; + pollTimer = setInterval(refreshChallenges, 2000); + refreshChallenges(); +} +startPolling(); + +// ---- 设置页 ---- +async function loadSettings() { + try { + const cfg = await window.api.getConfig(); + $('set-server').value = (cfg && cfg.server) || ''; + $('set-fake').checked = !!(cfg && cfg.fakeAll); + $('set-autostart').checked = await window.api.getAutoStart(); + } catch (e) { /* 忽略 */ } +} +loadSettings(); + +$('btn-save').addEventListener('click', async () => { + const server = $('set-server').value.trim(); + const res = $('set-result'); + if (!server) { res.textContent = '请填写服务器地址(内网部署填 http://服务器IP:8090)'; res.className = 'msg err'; return; } + const cfg = { server: server, fakeAll: $('set-fake').checked }; + await window.api.saveConfig(cfg); + await window.api.setAutoStart($('set-autostart').checked); + currentServer = server; + res.textContent = '已保存,核心进程已重启'; + res.className = 'msg ok'; +}); + +$('btn-quit').addEventListener('click', () => window.api.quit()); + +// 核心就绪提示 +if (window.api.onCoreReady) { + window.api.onCoreReady(() => { + $('st-online').textContent = '核心已就绪'; + refreshStatus(); + }); +} diff --git a/client/desktop/renderer/index.html b/client/desktop/renderer/index.html new file mode 100644 index 0000000..16e7eeb --- /dev/null +++ b/client/desktop/renderer/index.html @@ -0,0 +1,93 @@ + + + + + EveryPublish 客户端 + + + +
+
EveryPublish 发布客户端
+ +
+ +
+ +
+
+

运行状态

+
连接状态检测中...
+
服务器-
+
设备-
+
会话-
+
+
+ + +
+
+ + +
+
+

设备配对

+
    +
  1. 打开网页控制台(需与服务器内网互通)
  2. +
  3. 登录后进入「设置 → Agent 设备」生成配对码
  4. +
  5. 在下方输入配对码并点击「配对」
  6. +
+ + + + + +

+
+
+ + +
+
+

二次验证挑战

+

平台要求扫码/验证码时在此处理;超时可在网页端一键重发。

+
+ +
+
+ + +
+
+

设置

+ + + + +
+ + +
+

+
+
+
+ + + diff --git a/client/desktop/renderer/style.css b/client/desktop/renderer/style.css new file mode 100644 index 0000000..47032e9 --- /dev/null +++ b/client/desktop/renderer/style.css @@ -0,0 +1,40 @@ +* { margin: 0; padding: 0; box-sizing: border-box; } +body { font-family: "Microsoft YaHei", "PingFang SC", sans-serif; background: #f5f6f8; color: #1f2329; height: 100vh; display: flex; flex-direction: column; } +.topbar { background: #fff; border-bottom: 1px solid #e5e7eb; padding: 0 24px; display: flex; align-items: center; height: 56px; gap: 32px; } +.brand { font-size: 18px; font-weight: 700; color: #0a63f0; } +.brand .sub { font-size: 13px; font-weight: 400; color: #86909c; margin-left: 6px; } +.tabs { display: flex; gap: 8px; } +.tab { border: none; background: none; padding: 8px 16px; font-size: 14px; cursor: pointer; border-radius: 6px; color: #4e5969; } +.tab:hover { background: #f2f4f7; } +.tab.active { background: #e8f1ff; color: #0a63f0; font-weight: 600; } +.badge { display: inline-block; margin-left: 4px; background: #f53f3f; color: #fff; border-radius: 8px; font-size: 11px; padding: 0 6px; } +.hidden { display: none !important; } +#main { flex: 1; overflow-y: auto; padding: 24px; } +.page { display: none; max-width: 680px; margin: 0 auto; } +.page.active { display: block; } +.card { background: #fff; border: 1px solid #e5e7eb; border-radius: 8px; padding: 20px; margin-bottom: 16px; } +.card h2 { font-size: 16px; margin-bottom: 14px; } +.kv { display: flex; align-items: center; gap: 8px; padding: 8px 0; border-bottom: 1px dashed #f0f1f3; font-size: 14px; } +.kv span { color: #86909c; width: 80px; } +.dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; margin-left: 6px; } +.dot.gray { background: #c9cdd4; } .dot.green { background: #00b42a; } .dot.orange { background: #ff7d00; } .dot.red { background: #f53f3f; } +label { display: block; font-size: 13px; color: #4e5969; margin: 12px 0 6px; } +input[type="text"], input:not([type]) { width: 100%; padding: 9px 12px; border: 1px solid #d9dde3; border-radius: 6px; font-size: 14px; outline: none; } +input:focus { border-color: #0a63f0; } +.btn { padding: 9px 20px; border: 1px solid #d9dde3; border-radius: 6px; background: #fff; cursor: pointer; font-size: 14px; } +.btn:hover { background: #f7f8fa; } +.btn.primary { background: #0a63f0; border-color: #0a63f0; color: #fff; } +.btn.primary:hover { background: #0b57d0; } +.btn.danger { color: #f53f3f; border-color: #f53f3f; } +.actions { display: flex; gap: 10px; margin-top: 16px; } +.msg { margin-top: 10px; font-size: 13px; color: #4e5969; min-height: 18px; } +.msg.ok { color: #00b42a; } .msg.err { color: #f53f3f; } +.hint { font-size: 13px; color: #86909c; margin-bottom: 10px; } +.steps { margin: 0 0 14px 18px; font-size: 13px; color: #4e5969; line-height: 1.9; } +.list .item { display: flex; justify-content: space-between; align-items: center; padding: 10px 12px; border: 1px solid #e5e7eb; border-radius: 6px; margin-bottom: 8px; cursor: pointer; font-size: 13px; } +.list .item:hover { border-color: #0a63f0; } +.list .item.selected { border-color: #0a63f0; background: #f0f7ff; } +.list .item .p { color: #86909c; } +.detail { margin-top: 14px; } +.detail img { max-width: 220px; max-height: 220px; border: 1px solid #e5e7eb; border-radius: 6px; display: block; margin-bottom: 10px; } +.switch-row { display: flex; align-items: center; gap: 8px; font-size: 14px; margin: 10px 0; cursor: pointer; } diff --git a/client/dist/agent-core b/client/dist/agent-core new file mode 100755 index 0000000..43ad682 Binary files /dev/null and b/client/dist/agent-core differ diff --git a/client/ui/App.xaml b/client/ui/App.xaml new file mode 100644 index 0000000..001b02f --- /dev/null +++ b/client/ui/App.xaml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/client/ui/App.xaml.cs b/client/ui/App.xaml.cs new file mode 100644 index 0000000..888b16f --- /dev/null +++ b/client/ui/App.xaml.cs @@ -0,0 +1,23 @@ +using Microsoft.UI.Xaml; + +namespace EveryPublish.Client; + +public partial class App : Application +{ + private Window? _window; + + public App() + { + InitializeComponent(); + } + + protected override void OnLaunched(LaunchActivatedEventArgs args) + { + var startMinimized = args.Arguments.Contains("--minimized"); + _window = new MainWindow(); + if (!startMinimized) + { + _window.Activate(); + } + } +} diff --git a/client/ui/EveryPublish.Client.csproj b/client/ui/EveryPublish.Client.csproj new file mode 100644 index 0000000..d72792d --- /dev/null +++ b/client/ui/EveryPublish.Client.csproj @@ -0,0 +1,25 @@ + + + WinExe + net8.0-windows10.0.19041.0 + 10.0.17763.0 + 10.0.17763.0 + EveryPublish.Client + EveryPublish.Client + app.manifest + x64 + win-x64 + true + true + None + true + enable + enable + 0.2.0 + + + + + + + diff --git a/client/ui/MainWindow.xaml b/client/ui/MainWindow.xaml new file mode 100644 index 0000000..7327bd4 --- /dev/null +++ b/client/ui/MainWindow.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + diff --git a/client/ui/MainWindow.xaml.cs b/client/ui/MainWindow.xaml.cs new file mode 100644 index 0000000..a23c0a3 --- /dev/null +++ b/client/ui/MainWindow.xaml.cs @@ -0,0 +1,119 @@ +using System; +using System.IO; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media.Imaging; +using EveryPublish.Client.Pages; +using EveryPublish.Client.Services; +using H.NotifyIcon; + +namespace EveryPublish.Client; + +public sealed partial class MainWindow : Window +{ + private readonly string _dataDir = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "EveryPublish"); + private readonly CoreClient _core; + private readonly Watchdog _watchdog; + private readonly ClientContext _context; + private TaskbarIcon? _tray; + private bool _exiting; + + /// 供设置页「退出客户端」调用 + public static Action? ExitAction; + + public MainWindow() + { + InitializeComponent(); + Directory.CreateDirectory(_dataDir); + _core = new CoreClient(_dataDir); + _watchdog = new Watchdog(_core, _dataDir, OnCoreFatal); + _context = new ClientContext(_core, _dataDir, _watchdog); + ExitAction = ExitApp; + AppWindow.Closing += OnClosing; + InitTray(); + NavView.SelectedItem = NavView.MenuItems[0]; + Navigate("status"); + _watchdog.Start(); + } + + private void OnCoreFatal() + { + DispatcherQueue.TryEnqueue(async () => + { + var dialog = new ContentDialog + { + Title = "核心进程异常", + Content = "agent-core 连续重启失败,请检查安装目录或重新安装。", + CloseButtonText = "知道了", + XamlRoot = Content.XamlRoot, + }; + await dialog.ShowAsync(); + }); + } + + private void InitTray() + { + try + { + _tray = new TaskbarIcon { ToolTipText = "EveryPublish 客户端" }; + var iconPath = Path.Combine(AppContext.BaseDirectory, "Assets", "app.ico"); + if (File.Exists(iconPath)) + { + _tray.IconSource = new BitmapImage(new Uri(iconPath)); + } + // H.NotifyIcon 2.x:左/右键命令打开主窗口(退出入口在设置页) + _tray.LeftClickCommand = new SimpleCommand(ShowWindow); + _tray.RightClickCommand = new SimpleCommand(ShowWindow); + } + catch + { + // 托盘不可用不影响主功能 + } + } + + private void ShowWindow() + { + AppWindow.Show(); + Activate(); + } + + private void OnClosing(AppWindow sender, AppWindowClosingEventArgs args) + { + if (!_exiting) + { + args.Cancel = true; + AppWindow.Hide(); // 隐藏到托盘(WinUI 3 无 Window.Hide,用 AppWindow.Hide) + } + } + + private void ExitApp() + { + _exiting = true; + _tray?.Dispose(); + _watchdog.Dispose(); + _core.Dispose(); + Close(); + } + + private void NavView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args) + { + if (args.SelectedItem is NavigationViewItem item && item.Tag is string tag) + { + Navigate(tag); + } + } + + private void Navigate(string tag) + { + Type page = tag switch + { + "pair" => typeof(PairPage), + "challenges" => typeof(ChallengesPage), + "settings" => typeof(SettingsPage), + _ => typeof(StatusPage), + }; + ContentFrame.Navigate(page, _context); + } +} diff --git a/client/ui/Models/Dtos.cs b/client/ui/Models/Dtos.cs new file mode 100644 index 0000000..9341279 --- /dev/null +++ b/client/ui/Models/Dtos.cs @@ -0,0 +1,57 @@ +using System.Text.Json.Serialization; + +namespace EveryPublish.Client.Models; + +/// agent-core 写入的 local.json(随机端口 + token) +public class LocalInfo +{ + [JsonPropertyName("port")] + public int Port { get; set; } + + [JsonPropertyName("token")] + public string Token { get; set; } = ""; +} + +/// 核心状态(/status 返回) +public class CoreStatus +{ + [JsonPropertyName("online")] + public bool Online { get; set; } + + [JsonPropertyName("session")] + public string Session { get; set; } = ""; + + [JsonPropertyName("server")] + public string Server { get; set; } = ""; + + [JsonPropertyName("deviceId")] + public long DeviceId { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } = ""; +} + +/// 挑战(/challenges 返回,与 shared/proto.Challenge 一致) +public class ChallengeItem +{ + [JsonPropertyName("challengeId")] + public string ChallengeId { get; set; } = ""; + + [JsonPropertyName("accountId")] + public string AccountId { get; set; } = ""; + + [JsonPropertyName("platform")] + public string Platform { get; set; } = ""; + + [JsonPropertyName("kind")] + public string Kind { get; set; } = ""; + + [JsonPropertyName("qrToken")] + public string QrToken { get; set; } = ""; + + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = ""; + + [JsonPropertyName("expiresAt")] + public long ExpiresAt { get; set; } +} diff --git a/client/ui/Packaging/build-installer.ps1 b/client/ui/Packaging/build-installer.ps1 new file mode 100644 index 0000000..f7f1208 --- /dev/null +++ b/client/ui/Packaging/build-installer.ps1 @@ -0,0 +1,47 @@ +# EveryPublish 客户端打包脚本(Windows 上执行) +# 产出 dist/EveryPublish-Client-<版本>-win-x64.zip:WinUI 壳 + agent-core.exe 同目录。 +param( + [string]$Version = "0.2.0" +) +$ErrorActionPreference = "Stop" +$root = Split-Path -Parent $PSScriptRoot # client/ui +$dist = Join-Path $root "..\dist" +New-Item -ItemType Directory -Force -Path $dist | Out-Null + +Write-Host "[1/5] 生成图标 app.ico ..." +Add-Type -AssemblyName System.Drawing +$assets = Join-Path $root "Assets" +New-Item -ItemType Directory -Force -Path $assets | Out-Null +$bmp = New-Object System.Drawing.Bitmap 32, 32 +$g = [System.Drawing.Graphics]::FromImage($bmp) +$g.Clear([System.Drawing.Color]::FromArgb(0, 110, 255)) +$font = New-Object System.Drawing.Font("Arial", 16, [System.Drawing.FontStyle]::Bold) +$g.DrawString("E", $font, [System.Drawing.Brushes]::White, 7, 5) +$g.Dispose() +$icon = [System.Drawing.Icon]::FromHandle($bmp.GetHicon()) +$fs = [System.IO.File]::Create((Join-Path $assets "app.ico")) +$icon.Save($fs) +$fs.Close() + +Write-Host "[2/5] 交叉编译 agent-core.exe (Go) ..." +Push-Location (Join-Path $root "..\core") +$env:GOOS = "windows" +$env:GOARCH = "amd64" +go build -o (Join-Path $dist "agent-core.exe") . +Pop-Location + +Write-Host "[3/5] dotnet publish WinUI 壳 ..." +$publishDir = Join-Path $dist "publish" +Push-Location $root +dotnet publish EveryPublish.Client.csproj -c Release -r win-x64 -p:Platform=x64 -o $publishDir +Pop-Location + +Write-Host "[4/5] 组装产物 ..." +Copy-Item (Join-Path $dist "agent-core.exe") $publishDir -Force +Copy-Item -Recurse -Force $assets (Join-Path $publishDir "Assets") + +Write-Host "[5/5] 压缩 ..." +$zip = Join-Path $dist "EveryPublish-Client-$Version-win-x64.zip" +if (Test-Path $zip) { Remove-Item $zip -Force } +Compress-Archive -Path (Join-Path $publishDir "*") -DestinationPath $zip -Force +Write-Host "OK: $zip" -ForegroundColor Green diff --git a/client/ui/Packaging/precheck.ps1 b/client/ui/Packaging/precheck.ps1 new file mode 100644 index 0000000..959b76a --- /dev/null +++ b/client/ui/Packaging/precheck.ps1 @@ -0,0 +1,31 @@ +# EveryPublish Windows 构建前环境自检(PowerShell) +Write-Host "=== EveryPublish Windows 构建自检 ===" -ForegroundColor Cyan + +function Check($name, $cmd) { + try { + $out = Invoke-Expression $cmd 2>&1 | Out-String + $first = ($out.Trim() -split "`n")[0] + Write-Host "[OK] $name : $first" -ForegroundColor Green + return $true + } catch { + Write-Host "[FAIL] $name 未安装或不可用" -ForegroundColor Red + return $false + } +} + +$ok = $true +$ok = (Check "dotnet SDK" "dotnet --version") -and $ok +$ok = (Check "Go 工具链" "go version") -and $ok + +# 版本校验:.NET 需 8.x,Go 需 1.23+ +$dotnetVer = (dotnet --version 2>$null) +if ($dotnetVer -and -not $dotnetVer.StartsWith("8.")) { + Write-Host "[WARN] dotnet 版本 $dotnetVer,建议 .NET 8 SDK" -ForegroundColor Yellow +} + +if (-not $ok) { + Write-Host "请先安装依赖(见 client/ui/README.md)后重试" -ForegroundColor Red + exit 1 +} + +Write-Host "环境就绪:可运行 Packaging\build-installer.ps1 打包" -ForegroundColor Green diff --git a/client/ui/Pages/ChallengesPage.xaml b/client/ui/Pages/ChallengesPage.xaml new file mode 100644 index 0000000..6de862c --- /dev/null +++ b/client/ui/Pages/ChallengesPage.xaml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +