返回 DeepSeek-Reasonix
browser.go
根目录 / internal / remote / bootstrap / browser.go
1 package bootstrap
2
3 import (
4 "context"
5 "fmt"
6 "strings"
7 )
8
9 // Environment the launched serve reads to reach the desktop browser broker.
10 // Both travel in the process environment only: the token rotates with every
11 // SSH connection generation, so nothing on disk may outlive it.
12 const (
13 BrowserBrokerEnv = "REASONIX_BROWSER_BROKER"
14 BrowserTokenEnv = "REASONIX_BROWSER_TOKEN"
15 )
16
17 // ServeBrowserBrokerMarker is the `serve --help` flag name that advertises a
18 // binary able to use a desktop browser broker. Unlike the required capability
19 // markers it is optional: a serve without it launches untouched.
20 const ServeBrowserBrokerMarker = "browser-broker"
21
22 // BrowserBrokerOptions hands a launching serve the desktop browser broker:
23 // the reverse-forwarded loopback endpoint on the REMOTE host and the bearer
24 // token of the current connection generation.
25 type BrowserBrokerOptions struct {
26 BaseURL string
27 Token string
28 }
29
30 // BrowserBrokerSupportedCommand prints "yes" when bin's serve command
31 // advertises ServeBrowserBrokerMarker, "no" otherwise.
32 func BrowserBrokerSupportedCommand(bin string) string {
33 return fmt.Sprintf(
34 "if %s serve --help 2>&1 | grep -q -- %s; then echo yes; else echo no; fi",
35 shellQuote(bin), shellQuote(ServeBrowserBrokerMarker),
36 )
37 }
38
39 // browserEnvPrefix renders the environment assignments placed before the
40 // serve command; empty when no broker is configured.
41 func browserEnvPrefix(opts *BrowserBrokerOptions) string {
42 if opts == nil || strings.TrimSpace(opts.BaseURL) == "" || strings.TrimSpace(opts.Token) == "" {
43 return ""
44 }
45 return BrowserBrokerEnv + "=" + shellQuote(strings.TrimSpace(opts.BaseURL)) + " " +
46 BrowserTokenEnv + "=" + shellQuote(strings.TrimSpace(opts.Token)) + " "
47 }
48
49 // resolveBrowserBroker asks the desktop for broker options right before a
50 // fresh launch, and only when bin advertises the capability, so the reverse
51 // forward is never installed for a serve that cannot use it. A broker that
52 // cannot be prepared degrades to a launch without browser access.
53 func resolveBrowserBroker(ctx context.Context, conn Conn, bin string, opts Options) *BrowserBrokerOptions {
54 if opts.BrowserBroker == nil {
55 return nil
56 }
57 res, err := conn.Exec(ctx, BrowserBrokerSupportedCommand(bin))
58 if err != nil || strings.TrimSpace(string(res.Stdout)) != "yes" {
59 return nil
60 }
61 broker, err := opts.BrowserBroker(ctx)
62 if err != nil {
63 opts.progress("browser_broker", "unavailable: "+err.Error())
64 return nil
65 }
66 if browserEnvPrefix(broker) == "" {
67 return nil
68 }
69 return broker
70 }
71
71 lines GO