-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
155 lines (142 loc) · 3.52 KB
/
Copy pathmain.go
File metadata and controls
155 lines (142 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package main
import (
"encoding/json"
"errors"
"fmt"
"os"
"os/exec"
"os/signal"
"syscall"
)
func main() {
args := os.Args[1:]
cmd := ""
if len(args) > 0 {
cmd = args[0]
}
switch cmd {
case "setup":
runSetup()
case "off":
runOff()
case "config":
runConfigCmd(args[1:])
case "-h", "--help", "help":
usage()
default:
runLaunch(args)
}
}
func fatalf(format string, args ...any) {
fmt.Fprintf(os.Stderr, format+"\n", args...)
os.Exit(1)
}
func usage() {
fmt.Print(`qwencode-proxy — customize what flows between qwen-code and its model.
qwencode-proxy [qwen args] launch qwen behind the proxy (first run: setup)
qwencode-proxy setup (re)run setup: back up settings.json, point qwen at the proxy
qwencode-proxy off restore original settings.json baseUrl from backup
qwencode-proxy config [-e] show config (or open it in $EDITOR)
`)
}
func runOff() {
if err := restoreSettings(); err != nil {
fatalf("off: %v", err)
}
fmt.Println("✓ restored original settings.json — qwen now talks to the upstream directly")
}
func runConfigCmd(args []string) {
if len(args) > 0 && (args[0] == "-e" || args[0] == "--edit") {
ed := os.Getenv("EDITOR")
if ed == "" {
ed = "vi"
}
c := exec.Command(ed, configPath())
c.Stdin, c.Stdout, c.Stderr = os.Stdin, os.Stdout, os.Stderr
_ = c.Run()
return
}
cfg, err := loadConfig()
if err != nil {
fatalf("no config at %s — run: qwencode-proxy setup", configPath())
}
fmt.Printf("config: %s\n", configPath())
fmt.Printf("upstream: %s (real endpoint traffic is forwarded to)\n", cfg.Upstream)
fmt.Printf("port: %d (local proxy; settings.json points here)\n", cfg.Port)
fmt.Println("rules:")
for _, r := range cfg.Rules {
state := "on"
if !ruleEnabled(r) {
state = "off"
}
fmt.Printf(" - %-13s %s\n", r.Type, state)
}
b, _ := json.MarshalIndent(cfg, "", " ")
fmt.Printf("\n%s\n", b)
}
func runLaunch(qwenArgs []string) {
cfg, err := loadConfig()
if err != nil {
if os.IsNotExist(err) {
runSetup()
cfg, err = loadConfig()
}
if err != nil {
// config corrupt: recover enough to run transparently (fail-open)
cfg = recoverConfig()
}
}
if cfg.Port == 0 || cfg.Upstream == "" {
fatalf("config unusable and unrecoverable — run: qwencode-proxy setup")
}
stop, err := ensureProxy(cfg)
if err != nil {
fatalf("proxy: %v", err)
}
defer stop()
// self-heal: wire settings only after the proxy is confirmed listening
if err := ensureWired(cfg); err != nil {
stop()
fatalf("wiring: %v", err)
}
c := exec.Command("qwen", qwenArgs...)
c.Stdin, c.Stdout, c.Stderr = os.Stdin, os.Stdout, os.Stderr
if err := c.Start(); err != nil {
fatalf("launch qwen: %v", err)
}
// relay terminal signals to the child
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
go func() {
for s := range sig {
if c.Process != nil {
_ = c.Process.Signal(s)
}
}
}()
err = c.Wait()
signal.Stop(sig)
stop()
os.Exit(exitCode(err))
}
// recoverConfig: minimal transparent config from settings+backup when config is corrupt.
func recoverConfig() Config {
fmt.Fprintln(os.Stderr, "warning: config unreadable — running as a transparent proxy (no rules)")
c := Config{Port: portFromSettings()}
if b, err := os.ReadFile(backupPath()); err == nil {
if u, e := upstreamFromRaw(b); e == nil {
c.Upstream = u
}
}
return c
}
func exitCode(err error) int {
if err == nil {
return 0
}
var ee *exec.ExitError
if errors.As(err, &ee) {
return ee.ExitCode()
}
return 1
}