mirror of
https://github.com/lukaszraczylo/mcp-readitall.git
synced 2026-10-12 00:45:33 +00:00
fix(deps): migrate to chromedp 0.19 and drop the cdproto replace
Update chromedp to v0.19.1 and cdproto to v0.157.4, port the browser, reader and login code to the generic Action API, group both modules in Renovate, and move the Docker build to Go 1.27.
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
# Build stage
|
# Build stage
|
||||||
FROM golang:1.26-alpine AS build
|
FROM golang:1.27-alpine AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
|
|||||||
@@ -1,24 +1,18 @@
|
|||||||
module github.com/lukaszraczylo/mcp-readitall
|
module github.com/lukaszraczylo/mcp-readitall
|
||||||
|
|
||||||
go 1.26.4
|
go 1.27
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/JohannesKaufmann/html-to-markdown/v2 v2.5.2
|
github.com/JohannesKaufmann/html-to-markdown/v2 v2.5.2
|
||||||
github.com/chromedp/cdproto v0.157.2
|
github.com/chromedp/cdproto v0.157.4
|
||||||
github.com/chromedp/chromedp v0.16.0
|
github.com/chromedp/chromedp v0.19.1
|
||||||
github.com/modelcontextprotocol/go-sdk v1.8.0
|
github.com/modelcontextprotocol/go-sdk v1.8.0
|
||||||
github.com/stretchr/testify v1.12.1
|
github.com/stretchr/testify v1.12.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/JohannesKaufmann/dom v0.3.1 // indirect
|
github.com/JohannesKaufmann/dom v0.3.1 // indirect
|
||||||
github.com/chromedp/sysutil v1.1.0 // indirect
|
|
||||||
github.com/go-json-experiment/json v0.0.0-20260820222146-c27c302e5fc3 // indirect
|
|
||||||
github.com/gobwas/httphead v0.1.0 // indirect
|
|
||||||
github.com/gobwas/pool v0.2.1 // indirect
|
|
||||||
github.com/gobwas/ws v1.4.0 // indirect
|
|
||||||
github.com/google/jsonschema-go v0.4.3 // indirect
|
github.com/google/jsonschema-go v0.4.3 // indirect
|
||||||
github.com/orisano/pixelmatch v0.0.0-20230914042517-fa304d1dc785 // indirect
|
|
||||||
github.com/segmentio/asm v1.2.1 // indirect
|
github.com/segmentio/asm v1.2.1 // indirect
|
||||||
github.com/segmentio/encoding v0.5.4 // indirect
|
github.com/segmentio/encoding v0.5.4 // indirect
|
||||||
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||||
@@ -29,6 +23,3 @@ require (
|
|||||||
golang.org/x/sys v0.48.0 // indirect
|
golang.org/x/sys v0.48.0 // indirect
|
||||||
golang.org/x/time v0.16.0 // indirect
|
golang.org/x/time v0.16.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
// chromedp v0.16 builds against this cdproto; the v0.157 tag changed the input API.
|
|
||||||
replace github.com/chromedp/cdproto => github.com/chromedp/cdproto v0.0.0-20260714215040-dc233986426f
|
|
||||||
|
|||||||
@@ -2,32 +2,18 @@ github.com/JohannesKaufmann/dom v0.3.1 h1:J16l9JAHWgkFPR3VIPbQ1gvS0cWab6laK1q7PF
|
|||||||
github.com/JohannesKaufmann/dom v0.3.1/go.mod h1:BZPkf8ZeYrBgABjwJn9iiKt8aiCtkxpHkevms+Yp2DE=
|
github.com/JohannesKaufmann/dom v0.3.1/go.mod h1:BZPkf8ZeYrBgABjwJn9iiKt8aiCtkxpHkevms+Yp2DE=
|
||||||
github.com/JohannesKaufmann/html-to-markdown/v2 v2.5.2 h1:XFJZFWESIWlUEHHjzBuv8RvrtCWnSGlimEX17ysSDb8=
|
github.com/JohannesKaufmann/html-to-markdown/v2 v2.5.2 h1:XFJZFWESIWlUEHHjzBuv8RvrtCWnSGlimEX17ysSDb8=
|
||||||
github.com/JohannesKaufmann/html-to-markdown/v2 v2.5.2/go.mod h1:BHWO8lJzttJLqwuV8Rb1B3OG2OSzLbssZDI1FRg2eAA=
|
github.com/JohannesKaufmann/html-to-markdown/v2 v2.5.2/go.mod h1:BHWO8lJzttJLqwuV8Rb1B3OG2OSzLbssZDI1FRg2eAA=
|
||||||
github.com/chromedp/cdproto v0.0.0-20260714215040-dc233986426f h1:0Z1zcSLEmnj2c2CmJYBqewtS6pxhB39bNWUSEUAWjgk=
|
github.com/chromedp/cdproto v0.157.4 h1:UleIu3RHfaQmhE0ri9GpCbHrGpe0HgV55HVhzhPWglI=
|
||||||
github.com/chromedp/cdproto v0.0.0-20260714215040-dc233986426f/go.mod h1:RwFsSODCtFExll+GhHM6R92SARHR3Z3oipaxLHj46C0=
|
github.com/chromedp/cdproto v0.157.4/go.mod h1:scNPhJ/A4T8BVkyR5k2Uocvoju3iBRZS4cg3uMlxuMk=
|
||||||
github.com/chromedp/chromedp v0.16.0 h1:rOO4deOm4CbZgBCa8mD9g2rDyIoNs0BkgvNrlbp5ouk=
|
github.com/chromedp/chromedp v0.19.1 h1:OAf0ukr4nC5LIwaYEWIv7JYXlpZIZt6ZSDsYXCdjGlc=
|
||||||
github.com/chromedp/chromedp v0.16.0/go.mod h1:rbuGKFT1vMcFcFqKfPIO1GpX/N+2s8onm2qMxZLbU5U=
|
github.com/chromedp/chromedp v0.19.1/go.mod h1:4Rs9XtapAMgXxg0s1PpixyQHf37kdnSdu2ov5G4FsZ0=
|
||||||
github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM=
|
|
||||||
github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8=
|
|
||||||
github.com/go-json-experiment/json v0.0.0-20260820222146-c27c302e5fc3 h1:UADEEmDKgfXbtnGJZ97beY5XLo9ZechG1nlU4KnRrkE=
|
|
||||||
github.com/go-json-experiment/json v0.0.0-20260820222146-c27c302e5fc3/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg=
|
|
||||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
|
||||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
|
||||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
|
||||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
|
||||||
github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
|
|
||||||
github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
|
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/jsonschema-go v0.4.3 h1:/DBOLZTfDow7pe2GmaJNhltueGTtDKICi8V8p+DQPd0=
|
github.com/google/jsonschema-go v0.4.3 h1:/DBOLZTfDow7pe2GmaJNhltueGTtDKICi8V8p+DQPd0=
|
||||||
github.com/google/jsonschema-go v0.4.3/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
|
github.com/google/jsonschema-go v0.4.3/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
|
||||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
|
|
||||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
|
||||||
github.com/modelcontextprotocol/go-sdk v1.8.0 h1:KIvahhYqwtbeniWVPs3TcXEA7b8jEtwfBpOTAI+Urx4=
|
github.com/modelcontextprotocol/go-sdk v1.8.0 h1:KIvahhYqwtbeniWVPs3TcXEA7b8jEtwfBpOTAI+Urx4=
|
||||||
github.com/modelcontextprotocol/go-sdk v1.8.0/go.mod h1:dL7u98E/zjJTGzEq+j30jQ8K2k1mb6LeAH4inEcSGts=
|
github.com/modelcontextprotocol/go-sdk v1.8.0/go.mod h1:dL7u98E/zjJTGzEq+j30jQ8K2k1mb6LeAH4inEcSGts=
|
||||||
github.com/orisano/pixelmatch v0.0.0-20230914042517-fa304d1dc785 h1:J1//5K/6QF10cZ59zLcVNFGmBfiSrH8Cho/lNrViK9s=
|
|
||||||
github.com/orisano/pixelmatch v0.0.0-20230914042517-fa304d1dc785/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/sebdah/goldie/v2 v2.8.0 h1:dZb9wR8q5++oplmEiJT+U/5KyotVD+HNGCAc5gNr8rc=
|
github.com/sebdah/goldie/v2 v2.8.0 h1:dZb9wR8q5++oplmEiJT+U/5KyotVD+HNGCAc5gNr8rc=
|
||||||
@@ -52,7 +38,6 @@ golang.org/x/oauth2 v0.37.0 h1:JUlcxA8oAtauLfiH8FX2/FkAWHAdi0QtGCGc+hofE98=
|
|||||||
golang.org/x/oauth2 v0.37.0/go.mod h1:IxwZNxUULJmpBFf9K/9NTMSIfZZuvuTy1gGxhigP/58=
|
golang.org/x/oauth2 v0.37.0/go.mod h1:IxwZNxUULJmpBFf9K/9NTMSIfZZuvuTy1gGxhigP/58=
|
||||||
golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
|
golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
|
||||||
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||||
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||||
golang.org/x/time v0.16.0 h1:vMb6ptszcQMkcwiRTAuNNU50gom6++Q/6gY2hDM6VDE=
|
golang.org/x/time v0.16.0 h1:vMb6ptszcQMkcwiRTAuNNU50gom6++Q/6gY2hDM6VDE=
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func (b *Browser) ensureAllocator() (context.Context, error) {
|
|||||||
b.allocCtx, b.allocCancel = chromedp.NewExecAllocator(context.Background(), opts...)
|
b.allocCtx, b.allocCancel = chromedp.NewExecAllocator(context.Background(), opts...)
|
||||||
|
|
||||||
browserCtx, cancel := chromedp.NewContext(b.allocCtx)
|
browserCtx, cancel := chromedp.NewContext(b.allocCtx)
|
||||||
if err := chromedp.Run(browserCtx, chromedp.Navigate("about:blank")); err != nil {
|
if err := chromedp.Do(browserCtx, chromedp.Navigate("about:blank")); err != nil {
|
||||||
b.startErr = fmt.Errorf("warm up browser: %w", err)
|
b.startErr = fmt.Errorf("warm up browser: %w", err)
|
||||||
cancel()
|
cancel()
|
||||||
return
|
return
|
||||||
@@ -144,10 +144,8 @@ func (b *Browser) NewContext(parentCtx context.Context, statePath string) (conte
|
|||||||
// the first step of a fresh login (otherwise stale session cookies from
|
// the first step of a fresh login (otherwise stale session cookies from
|
||||||
// a previous attempt can mask the new login's success).
|
// a previous attempt can mask the new login's success).
|
||||||
func ClearAllCookies(ctx context.Context) error {
|
func ClearAllCookies(ctx context.Context) error {
|
||||||
return chromedp.Run(ctx, chromedp.ActionFunc(func(ctx context.Context) error {
|
_, err := chromedp.Call(ctx, network.ClearBrowserCookies, cdp.Empty{})
|
||||||
target := chromedp.FromContext(ctx).Target
|
return err
|
||||||
return network.ClearBrowserCookies().Do(cdp.WithExecutor(ctx, target))
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHeadedContext spawns a *separate* Chromium process with a visible
|
// NewHeadedContext spawns a *separate* Chromium process with a visible
|
||||||
|
|||||||
+18
-25
@@ -7,7 +7,6 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/chromedp/cdproto/cdp"
|
"github.com/chromedp/cdproto/cdp"
|
||||||
"github.com/chromedp/cdproto/network"
|
"github.com/chromedp/cdproto/network"
|
||||||
@@ -76,10 +75,8 @@ func ApplyStorageStateCookies(ctx context.Context, statePath string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return chromedp.Run(ctx, chromedp.ActionFunc(func(ctx context.Context) error {
|
_, err = chromedp.Call(ctx, network.SetCookies, network.SetCookiesParams{Cookies: cookies})
|
||||||
target := chromedp.FromContext(ctx).Target
|
return err
|
||||||
return network.SetCookies(cookies).Do(cdp.WithExecutor(ctx, target))
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyLocalStorageOnCurrentOrigin sets every localStorage entry that
|
// ApplyLocalStorageOnCurrentOrigin sets every localStorage entry that
|
||||||
@@ -99,8 +96,8 @@ func ApplyLocalStorageOnCurrentOrigin(ctx context.Context, statePath string) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentURL string
|
currentURL, err := chromedp.Run(ctx, chromedp.Location())
|
||||||
if err := chromedp.Run(ctx, chromedp.Location(¤tURL)); err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("read current origin: %w", err)
|
return fmt.Errorf("read current origin: %w", err)
|
||||||
}
|
}
|
||||||
currentOrigin := normalizeOrigin(currentURL)
|
currentOrigin := normalizeOrigin(currentURL)
|
||||||
@@ -123,7 +120,8 @@ func ApplyLocalStorageOnCurrentOrigin(ctx context.Context, statePath string) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return chromedp.Run(ctx, chromedp.Evaluate(buildLocalStorageScript(entries), nil))
|
_, err = chromedp.Run(ctx, chromedp.Evaluate[chromedp.Void](buildLocalStorageScript(entries)))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStorageState reads every cookie set on the current target and
|
// CaptureStorageState reads every cookie set on the current target and
|
||||||
@@ -138,8 +136,8 @@ func CaptureStorageState(ctx context.Context, statePath string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2) Current origin + localStorage
|
// 2) Current origin + localStorage
|
||||||
var currentURL string
|
currentURL, err := chromedp.Run(ctx, chromedp.Location())
|
||||||
if err := chromedp.Run(ctx, chromedp.Location(¤tURL)); err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("read location: %w", err)
|
return fmt.Errorf("read location: %w", err)
|
||||||
}
|
}
|
||||||
origin := normalizeOrigin(currentURL)
|
origin := normalizeOrigin(currentURL)
|
||||||
@@ -182,14 +180,11 @@ func CaptureStorageState(ctx context.Context, statePath string) error {
|
|||||||
// captureCDPCookies asks the current target for its full cookie list via
|
// captureCDPCookies asks the current target for its full cookie list via
|
||||||
// the network domain.
|
// the network domain.
|
||||||
func captureCDPCookies(ctx context.Context) ([]*network.Cookie, error) {
|
func captureCDPCookies(ctx context.Context) ([]*network.Cookie, error) {
|
||||||
var out []*network.Cookie
|
res, err := chromedp.Call(ctx, network.GetCookies, network.GetCookiesParams{})
|
||||||
err := chromedp.Run(ctx, chromedp.ActionFunc(func(ctx context.Context) error {
|
if err != nil {
|
||||||
target := chromedp.FromContext(ctx).Target
|
return nil, err
|
||||||
var err error
|
}
|
||||||
out, err = network.GetCookies().Do(cdp.WithExecutor(ctx, target))
|
return res.Cookies, nil
|
||||||
return err
|
|
||||||
}))
|
|
||||||
return out, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// captureLocalStorage serialises the current page's localStorage to a
|
// captureLocalStorage serialises the current page's localStorage to a
|
||||||
@@ -200,8 +195,8 @@ func captureLocalStorage(ctx context.Context, origin string) (json.RawMessage, e
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
const script = `JSON.stringify(Object.fromEntries(Object.keys(localStorage).map(function(k){return [k, localStorage.getItem(k)];})))`
|
const script = `JSON.stringify(Object.fromEntries(Object.keys(localStorage).map(function(k){return [k, localStorage.getItem(k)];})))`
|
||||||
var lsJSON string
|
lsJSON, err := chromedp.Run(ctx, chromedp.Evaluate[string](script))
|
||||||
if err := chromedp.Run(ctx, chromedp.Evaluate(script, &lsJSON)); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if lsJSON == "" || lsJSON == "[]" || lsJSON == "{}" {
|
if lsJSON == "" || lsJSON == "[]" || lsJSON == "{}" {
|
||||||
@@ -257,9 +252,8 @@ type onDiskCookie struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// onDiskToCookieParam converts our on-disk shape into a CDP
|
// onDiskToCookieParam converts our on-disk shape into a CDP
|
||||||
// network.CookieParam. Session cookies (Expires <= 0) get a nil Expires
|
// network.CookieParam. Session cookies (Expires <= 0) keep a zero Expires;
|
||||||
// pointer; persistent cookies get a *cdp.TimeSinceEpoch built from the
|
// persistent cookies get a cdp.TimeSinceEpoch built from the Unix timestamp.
|
||||||
// Unix timestamp.
|
|
||||||
func onDiskToCookieParam(c onDiskCookie) *network.CookieParam {
|
func onDiskToCookieParam(c onDiskCookie) *network.CookieParam {
|
||||||
p := &network.CookieParam{
|
p := &network.CookieParam{
|
||||||
Name: c.Name,
|
Name: c.Name,
|
||||||
@@ -271,8 +265,7 @@ func onDiskToCookieParam(c onDiskCookie) *network.CookieParam {
|
|||||||
SameSite: parseSameSite(c.SameSite),
|
SameSite: parseSameSite(c.SameSite),
|
||||||
}
|
}
|
||||||
if c.Expires > 0 {
|
if c.Expires > 0 {
|
||||||
t := cdp.TimeSinceEpoch(time.Unix(int64(c.Expires), 0))
|
p.Expires = cdp.TimeSinceEpoch(float64(int64(c.Expires)))
|
||||||
p.Expires = &t
|
|
||||||
}
|
}
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/chromedp/cdproto/cdp"
|
|
||||||
"github.com/chromedp/chromedp"
|
"github.com/chromedp/chromedp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,8 +12,8 @@ import (
|
|||||||
// a separate chromedp.Run so a slow extraction does not re-do navigation.
|
// a separate chromedp.Run so a slow extraction does not re-do navigation.
|
||||||
func (r *Reader) extractHTML(ctx context.Context, selector string) (string, error) {
|
func (r *Reader) extractHTML(ctx context.Context, selector string) (string, error) {
|
||||||
if selector == "" {
|
if selector == "" {
|
||||||
var html string
|
html, err := chromedp.Run(ctx, chromedp.OuterHTML(chromedp.CSS("html")))
|
||||||
if err := chromedp.Run(ctx, chromedp.OuterHTML("html", &html, chromedp.ByQuery)); err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("read document: %w", err)
|
return "", fmt.Errorf("read document: %w", err)
|
||||||
}
|
}
|
||||||
return html, nil
|
return html, nil
|
||||||
@@ -22,16 +21,16 @@ func (r *Reader) extractHTML(ctx context.Context, selector string) (string, erro
|
|||||||
|
|
||||||
// Verify the selector matches at least one node; an empty result is
|
// Verify the selector matches at least one node; an empty result is
|
||||||
// almost always a caller mistake, not an empty page.
|
// almost always a caller mistake, not an empty page.
|
||||||
var nodes []*cdp.Node
|
nodes, err := chromedp.Run(ctx, chromedp.Nodes(chromedp.CSS(selector)))
|
||||||
if err := chromedp.Run(ctx, chromedp.Nodes(selector, &nodes, chromedp.ByQuery)); err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("locate %q: %w", selector, err)
|
return "", fmt.Errorf("locate %q: %w", selector, err)
|
||||||
}
|
}
|
||||||
if len(nodes) == 0 {
|
if len(nodes) == 0 {
|
||||||
return "", fmt.Errorf("selector %q matched no elements", selector)
|
return "", fmt.Errorf("selector %q matched no elements", selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
var html string
|
html, err := chromedp.Run(ctx, chromedp.OuterHTML(chromedp.CSS(selector)))
|
||||||
if err := chromedp.Run(ctx, chromedp.OuterHTML(selector, &html, chromedp.ByQuery)); err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("extract %q: %w", selector, err)
|
return "", fmt.Errorf("extract %q: %w", selector, err)
|
||||||
}
|
}
|
||||||
return html, nil
|
return html, nil
|
||||||
|
|||||||
@@ -94,12 +94,12 @@ func (r *Reader) Read(parentCtx context.Context, opts ReadOptions) (*ReadResult,
|
|||||||
// Build the action chain. Cookies were already applied by NewContext
|
// Build the action chain. Cookies were already applied by NewContext
|
||||||
// (if a session exists). localStorage is applied post-navigation
|
// (if a session exists). localStorage is applied post-navigation
|
||||||
// because it is origin-scoped.
|
// because it is origin-scoped.
|
||||||
actions := []chromedp.Action{
|
actions := []chromedp.Action[chromedp.Void]{
|
||||||
chromedp.Navigate(opts.URL),
|
chromedp.Navigate(opts.URL),
|
||||||
chromedp.WaitReady("body"),
|
chromedp.WaitReady("body"),
|
||||||
}
|
}
|
||||||
if statePath != "" {
|
if statePath != "" {
|
||||||
actions = append(actions, chromedp.ActionFunc(func(ctx context.Context) error {
|
actions = append(actions, chromedp.Func(func(ctx context.Context, _ *chromedp.Target) error {
|
||||||
return browser.ApplyLocalStorageOnCurrentOrigin(ctx, statePath)
|
return browser.ApplyLocalStorageOnCurrentOrigin(ctx, statePath)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -109,14 +109,17 @@ func (r *Reader) Read(parentCtx context.Context, opts ReadOptions) (*ReadResult,
|
|||||||
chromedp.Sleep(500*time.Millisecond),
|
chromedp.Sleep(500*time.Millisecond),
|
||||||
)
|
)
|
||||||
if opts.WaitSelector != "" {
|
if opts.WaitSelector != "" {
|
||||||
actions = append(actions, chromedp.WaitVisible(opts.WaitSelector, chromedp.ByQuery))
|
actions = append(actions, chromedp.WaitVisible(chromedp.CSS(opts.WaitSelector)))
|
||||||
}
|
}
|
||||||
actions = append(actions,
|
actions = append(actions, chromedp.Func(func(ctx context.Context, t *chromedp.Target) (err error) {
|
||||||
chromedp.Title(&title),
|
if title, err = chromedp.Title()(ctx, t); err != nil {
|
||||||
chromedp.Location(&finalURL),
|
return err
|
||||||
)
|
}
|
||||||
|
finalURL, err = chromedp.Location()(ctx, t)
|
||||||
|
return err
|
||||||
|
}))
|
||||||
|
|
||||||
if err := chromedp.Run(cdpCtx, actions...); err != nil {
|
if err := chromedp.Do(cdpCtx, actions...); err != nil {
|
||||||
return nil, fmt.Errorf("navigate: %w", err)
|
return nil, fmt.Errorf("navigate: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-14
@@ -8,7 +8,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/chromedp/cdproto/cdp"
|
|
||||||
"github.com/chromedp/chromedp"
|
"github.com/chromedp/chromedp"
|
||||||
"github.com/lukaszraczylo/mcp-readitall/internal/browser"
|
"github.com/lukaszraczylo/mcp-readitall/internal/browser"
|
||||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||||
@@ -78,11 +77,9 @@ func RegisterLogin(s *mcp.Server, pw *browser.Browser, sess *browser.SessionStor
|
|||||||
return nil, LoginOutput{}, fmt.Errorf("clear cookies: %w", err)
|
return nil, LoginOutput{}, fmt.Errorf("clear cookies: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var title string
|
if err := chromedp.Do(browserCtx,
|
||||||
if err := chromedp.Run(browserCtx,
|
|
||||||
chromedp.Navigate(in.URL),
|
chromedp.Navigate(in.URL),
|
||||||
chromedp.WaitReady("body"),
|
chromedp.WaitReady("body"),
|
||||||
chromedp.Title(&title),
|
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, LoginOutput{}, fmt.Errorf("navigate: %w", err)
|
return nil, LoginOutput{}, fmt.Errorf("navigate: %w", err)
|
||||||
}
|
}
|
||||||
@@ -146,8 +143,8 @@ func waitForLogin(ctx context.Context, pageCtx context.Context, in LoginInput, t
|
|||||||
|
|
||||||
check := func() (bool, error) {
|
check := func() (bool, error) {
|
||||||
if in.SuccessURL != "" {
|
if in.SuccessURL != "" {
|
||||||
var current string
|
current, err := chromedp.Run(pageCtx, chromedp.Location())
|
||||||
if err := chromedp.Run(pageCtx, chromedp.Location(¤t)); err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if strings.Contains(current, in.SuccessURL) {
|
if strings.Contains(current, in.SuccessURL) {
|
||||||
@@ -155,16 +152,15 @@ func waitForLogin(ctx context.Context, pageCtx context.Context, in LoginInput, t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if in.SuccessSelector != "" {
|
if in.SuccessSelector != "" {
|
||||||
var nodes []*cdp.Node
|
nodes, err := chromedp.Run(pageCtx, chromedp.Nodes(chromedp.CSS(in.SuccessSelector)))
|
||||||
if err := chromedp.Run(pageCtx, chromedp.Nodes(in.SuccessSelector, &nodes, chromedp.ByQuery)); err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if len(nodes) > 0 {
|
if len(nodes) > 0 {
|
||||||
var visible bool
|
visible, err := chromedp.Run(pageCtx, chromedp.Evaluate[bool](
|
||||||
if err := chromedp.Run(pageCtx, chromedp.Evaluate(
|
|
||||||
`(()=>{const e=document.querySelector(`+jsQuote(in.SuccessSelector)+`);return !!e && e.getBoundingClientRect().height>0;})()`,
|
`(()=>{const e=document.querySelector(`+jsQuote(in.SuccessSelector)+`);return !!e && e.getBoundingClientRect().height>0;})()`,
|
||||||
&visible,
|
))
|
||||||
)); err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if visible {
|
if visible {
|
||||||
@@ -205,8 +201,8 @@ func waitForLogin(ctx context.Context, pageCtx context.Context, in LoginInput, t
|
|||||||
// currentLocation returns the page's current URL or "" if the query
|
// currentLocation returns the page's current URL or "" if the query
|
||||||
// fails (e.g. the browser has already been torn down).
|
// fails (e.g. the browser has already been torn down).
|
||||||
func currentLocation(ctx context.Context) string {
|
func currentLocation(ctx context.Context) string {
|
||||||
var s string
|
s, err := chromedp.Run(ctx, chromedp.Location())
|
||||||
if err := chromedp.Run(ctx, chromedp.Location(&s)); err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"packageRules": [
|
||||||
|
{
|
||||||
|
"description": "chromedp pins an exact cdproto; bump them together",
|
||||||
|
"matchManagers": ["gomod"],
|
||||||
|
"matchPackageNames": ["github.com/chromedp/chromedp", "github.com/chromedp/cdproto"],
|
||||||
|
"groupName": "chromedp"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user