返回提交历史
Modified
g4f-go/main.go
+64
-99
XFEstudio/gpt4free
Update main.go
3b626c82
代码差异
1 个文件
+64
-99
@@ -4,20 +4,15 @@ import (
4
4
"context"
5
5
"fmt"
6
6
"os"
7
"os/exec"
8
7
"os/signal"
8
"syscall"
9
9
"path/filepath"
10
10
"strings"
11
"syscall"
12
11
"time"
13
12
)
14
13
15
// Constants usually defined at build time or in a separate file
16
const (
17
Version = "1.0.0"
18
PythonVer = "3.10"
19
)
20
14
// main handles the `g4f-go <command>` interface. Anything unknown is forwarded
15
// to the embedded g4f CLI (so `g4f-go client "hello"` just works).
21
16
func printHelp() {
22
17
fmt.Printf(`g4f-go %s - gpt4free with embedded Python %s
23
18
@@ -36,148 +31,118 @@ Environment:
36
31
}
37
32
38
33
func main() {
39
// We use a helper function so we can use "return" for exit codes
40
os.Exit(run())
34
os.Exit(runMain())
41
35
}
42
36
43
func run() int {
37
func runMain() int {
44
38
binDir := installDir()
45
// os.Args[0] is the program name, we want the actual arguments
46
args := normalizeArgs(os.Args[1:])
47
39
args := normalizeArgs(os.Args)
48
40
if len(args) == 0 {
49
41
printHelp()
50
42
return 0
51
43
}
52
44
53
// 1. Handle non-python commands first
54
switch args[0] {
55
case "help", "--help", "-h":
56
printHelp()
57
return 0
58
case "--version", "-v":
59
fmt.Printf("g4f-go %s (embedded CPython %s)\n", Version, PythonVer)
60
return 0
61
}
62
63
// 2. Prepare Python Runtime
64
45
py, err := ensureRuntime()
65
46
if err != nil {
66
fmt.Fprintln(os.Stderr, "g4f-go runtime error:", err)
67
return 1
68
}
69
70
if os.Getenv("G4F_PYTHON_ONLY") == "1" {
71
fmt.Println(py)
72
return 0
47
fmt.Fprintln(os.Stderr, "g4f-go:", err)
48
os.Exit(1)
73
49
}
74
50
75
51
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
76
52
defer stop()
77
53
78
// 3. Handle management commands
79
54
switch args[0] {
55
case "help", "--help", "-h":
56
printHelp()
57
return 0
58
case "--version", "-v":
59
fmt.Printf("g4f-go %s (embedded CPython %s)\n", Version, PythonVer)
60
return 0
80
61
case "status":
81
return handleStatus(ctx, binDir, py)
62
exe := pythonExecutable(binDir)
63
stamp := filepath.Join(binDir, ".g4f-runtime", ".installed")
64
fmt.Printf("binary dir: %s\n", binDir)
65
fmt.Printf("python: %s\n", exe)
66
if _, err := os.Stat(exe); err == nil {
67
fmt.Println("runtime: extracted")
68
} else {
69
fmt.Println("runtime: not extracted (will extract on first run)")
70
}
71
if _, err := os.Stat(stamp); err == nil {
72
fmt.Println("g4f: installed")
73
} else {
74
fmt.Println("g4f: not installed (will install on first run)")
75
}
76
code, err := runPython(ctx, py, []string{"--version"})
77
if err != nil {
78
fmt.Fprintln(os.Stderr, "g4f-go:", err)
79
return 1
80
}
81
return code
82
82
case "install", "uninstall":
83
83
if len(args) < 2 {
84
fmt.Fprintln(os.Stderr, "usage: g4f-go install <package>")
84
fmt.Fprintln(os.Stderr, "usage: g4f-go install g4f")
85
85
return 2
86
86
}
87
code, err := runPython(ctx, py, append([]string{"-m", "pip"}, args...))
87
code, err := runPython(ctx, py, []string{
88
"-m", "pip", args...
89
})
88
90
if err != nil {
89
91
fmt.Fprintln(os.Stderr, "g4f-go:", err)
92
return 1
90
93
}
91
94
return code
92
95
case "bootstrap":
96
// Refresh the embedded package installation (e.g. after updating g4f-go).
93
97
code, err := runPython(ctx, py, []string{"-m", "pip", "install", "--no-input", "g4f[slim]"}, pipEnv(binDir)...)
94
98
if err != nil {
95
99
fmt.Fprintln(os.Stderr, "g4f-go:", err)
100
return 1
96
101
}
97
102
return code
98
103
}
99
104
100
// 4. Auto-install G4F if missing before running commands
105
if os.Getenv("G4F_PYTHON_ONLY") == "1" {
106
fmt.Println(py)
107
return 0
108
}
109
101
110
exe := pythonExecutable(binDir)
102
if err := installG4F(binDir, exe, time.Now()); err != nil {
111
start := time.Now()
112
if err := installG4F(binDir, exe, start); err != nil {
103
113
fmt.Fprintln(os.Stderr, "g4f-go:", err)
104
114
return 1
105
115
}
106
116
107
// 5. Default: forward everything to the g4f module
117
// Default: forward everything to the g4f module.
108
118
code, err := runPython(ctx, py, append([]string{"-m", "g4f"}, args...))
109
119
if err != nil {
110
fmt.Fprintln(os.Stderr, "g4f-go execution error:", err)
120
fmt.Fprintln(os.Stderr, "g4f-go:", err)
111
121
return 1
112
122
}
113
123
return code
114
124
}
115
125
116
// --- Helper Functions (Stubs/Implementations) ---
117
126
// normalizeArgs cleans leading `g4f-go` repeats (typos like `g4f-go g4f-go ...`).
118
127
func normalizeArgs(args []string) []string {
119
if len(args) > 0 && args[0] == "g4f-go" {
120
return normalizeArgs(args[1:])
128
for len(args) > 0 && args[0] == "g4f-go" {
129
args = args[1:]
121
130
}
131
// A leading binary-name echo (e.g. argv[0]) is stripped by callers.
122
132
return args
123
133
}
124
134
125
func installDir() string {
126
configDir, _ := os.UserConfigDir()
127
return filepath.Join(configDir, "g4f-go")
128
}
129
130
func pythonExecutable(binDir string) string {
131
// Adjust for Windows if necessary
132
return filepath.Join(binDir, "python")
133
}
134
135
func ensureRuntime() (string, error) {
136
dir := installDir()
137
pyPath := pythonExecutable(dir)
138
// Logic to extract embedded python would go here
139
if _, err := os.Stat(dir); os.IsNotExist(err) {
140
os.MkdirAll(dir, 0755)
135
// hasSubcommand reports whether args start with a known g4f-go subcommand.
136
func hasSubcommand(args []string) bool {
137
if len(args) == 0 {
138
return true
141
139
}
142
return pyPath, nil
143
}
144
145
func runPython(ctx context.Context, pyPath string, args []string, env ...string) (int, error) {
146
cmd := exec.CommandContext(ctx, pyPath, args...)
147
cmd.Stdout = os.Stdout
148
cmd.Stderr = os.Stderr
149
cmd.Stdin = os.Stdin
150
cmd.Env = append(os.Environ(), env...)
151
152
err := cmd.Run()
153
if err != nil {
154
if exitError, ok := err.(*exec.ExitError); ok {
155
return exitError.ExitCode(), nil
156
}
157
return 1, err
140
switch args[0] {
141
case "help", "--help", "-h", "--version", "-v", "status", "install", "bootstrap":
142
return true
158
143
}
159
return 0, nil
160
}
161
162
func installG4F(binDir, pyExe string, start time.Time) error {
163
stamp := filepath.Join(binDir, ".g4f_installed")
164
if _, err := os.Stat(stamp); err == nil {
165
return nil // Already installed
144
if strings.HasPrefix(args[0], "-") {
145
return true
166
146
}
167
// Logic to install g4f via pip
168
os.WriteFile(stamp, []byte(start.String()), 0644)
169
return nil
170
}
171
172
func pipEnv(binDir string) []string {
173
return []string{fmt.Sprintf("PYTHONPATH=%s", binDir)}
174
}
175
176
func handleStatus(ctx context.Context, binDir, py string) int {
177
exe := pythonExecutable(binDir)
178
fmt.Printf("binary dir: %s\n", binDir)
179
fmt.Printf("python: %s\n", exe)
180
181
code, _ := runPython(ctx, py, []string{"--version"})
182
return code
147
return false
183
148
}