@yufengtadian/freedom-cli 1.12.17 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/lib/build.js +74 -9
  2. package/lib/cli.js +61 -3
  3. package/lib/init.js +9 -1
  4. package/lib/security.js +2 -2
  5. package/lib/utils.js +5 -2
  6. package/lib/verify.js +236 -0
  7. package/package.json +43 -39
  8. package/shell/darwin-arm64/freedom-shell +0 -0
  9. package/shell/linux-x64/freedom-shell +0 -0
  10. package/shell/win-x64/freedom-shell.exe +0 -0
  11. package/templates/go/go.mod +1 -7
  12. package/templates/go/go.sum +2 -2
  13. package/templates/go/main.go +22 -8
  14. package/templates/go/pkg/freedom/anti_debug_other.go +2 -3
  15. package/templates/go/pkg/freedom/anti_debug_windows.go +10 -12
  16. package/templates/go/pkg/freedom/assets/{index.html → default.html} +1 -1
  17. package/templates/go/pkg/freedom/assets/freedom.js +219 -21
  18. package/templates/go/pkg/freedom/assets_embed.go +11 -13
  19. package/templates/go/pkg/freedom/authenticode_other.go +11 -0
  20. package/templates/go/pkg/freedom/authenticode_windows.go +87 -0
  21. package/templates/go/pkg/freedom/backend_proc.go +295 -51
  22. package/templates/go/pkg/freedom/bridge.go +13 -2
  23. package/templates/go/pkg/freedom/capability.go +77 -0
  24. package/templates/go/pkg/freedom/center_other.go +3 -0
  25. package/templates/go/pkg/freedom/center_windows.go +64 -36
  26. package/templates/go/pkg/freedom/dispatch.go +60 -0
  27. package/templates/go/pkg/freedom/events_windows.go +319 -0
  28. package/templates/go/pkg/freedom/freedom.go +242 -92
  29. package/templates/go/pkg/freedom/msgwindow_windows.go +257 -0
  30. package/templates/go/pkg/freedom/osver_other.go +15 -0
  31. package/templates/go/pkg/freedom/osver_windows.go +121 -0
  32. package/templates/go/pkg/freedom/proc_hide_windows.go +2 -2
  33. package/templates/go/pkg/freedom/{configfile.go → resources.go} +56 -32
  34. package/templates/go/pkg/freedom/security.go +19 -12
  35. package/templates/go/pkg/freedom/singleinstance_other.go +10 -0
  36. package/templates/go/pkg/freedom/singleinstance_windows.go +145 -0
  37. package/templates/go/pkg/freedom/spawn_other.go +11 -0
  38. package/templates/go/pkg/freedom/spawn_windows.go +23 -0
  39. package/templates/go/pkg/freedom/store.go +458 -0
  40. package/templates/go/pkg/freedom/syscap_linux.go +278 -0
  41. package/templates/go/pkg/freedom/syscap_other.go +22 -0
  42. package/templates/go/pkg/freedom/syscap_windows.go +845 -0
  43. package/templates/go/pkg/freedom/sysint_common.go +70 -0
  44. package/templates/go/pkg/freedom/sysint_windows.go +426 -0
  45. package/templates/go/pkg/freedom/tray_common.go +12 -0
  46. package/templates/go/pkg/freedom/tray_linux.go +302 -0
  47. package/templates/go/pkg/freedom/tray_other.go +10 -0
  48. package/templates/go/pkg/freedom/tray_windows.go +536 -0
  49. package/templates/go/pkg/freedom/updater.go +372 -0
  50. package/templates/go/pkg/freedom/window_mgr.go +385 -0
  51. package/templates/go/pkg/freedom/window_other.go +32 -66
  52. package/templates/go/pkg/freedom/window_windows.go +406 -251
  53. package/templates/project-minimal/freedom.config.js +58 -0
  54. package/templates/project-minimal/index.html +41 -0
  55. package/templates/project-minimal/package.json +14 -0
  56. package/templates/project-minimal/src/main.js +19 -0
  57. package/templates/project-minimal/vite.config.js +18 -0
  58. package/templates/go/gen_config.go +0 -22
  59. package/templates/go/pkg/freedom/security_test.go +0 -153
  60. package/templates/go/webview_go/.github/workflows/ci.yaml +0 -62
  61. package/templates/go/webview_go/CHANGELOG.md +0 -15
  62. package/templates/go/webview_go/LICENSE +0 -22
  63. package/templates/go/webview_go/README.md +0 -50
  64. package/templates/go/webview_go/examples/basic/main.go +0 -12
  65. package/templates/go/webview_go/examples/bind/main.go +0 -38
  66. package/templates/go/webview_go/glue.c +0 -36
  67. package/templates/go/webview_go/go.mod +0 -3
  68. package/templates/go/webview_go/go.sum +0 -0
  69. package/templates/go/webview_go/libs/mswebview2/LICENSE +0 -27
  70. package/templates/go/webview_go/libs/mswebview2/include/WebView2.h +0 -23568
  71. package/templates/go/webview_go/libs/mswebview2/include/vendor.go +0 -2
  72. package/templates/go/webview_go/libs/mswebview2/vendor.go +0 -2
  73. package/templates/go/webview_go/libs/mswebview2/version.txt +0 -1
  74. package/templates/go/webview_go/libs/webview/LICENSE +0 -22
  75. package/templates/go/webview_go/libs/webview/include/vendor.go +0 -2
  76. package/templates/go/webview_go/libs/webview/include/webview.h +0 -3871
  77. package/templates/go/webview_go/libs/webview/vendor.go +0 -2
  78. package/templates/go/webview_go/libs/webview/version.txt +0 -1
  79. package/templates/go/webview_go/webview.cc +0 -1
  80. package/templates/go/webview_go/webview.go +0 -481
  81. package/templates/go/webview_go/webview_test.go +0 -50
@@ -0,0 +1,278 @@
1
+ //go:build linux
2
+
3
+ package freedom
4
+
5
+ import (
6
+ "encoding/json"
7
+ "fmt"
8
+ "os"
9
+ "os/exec"
10
+ "path/filepath"
11
+ "strings"
12
+ )
13
+
14
+ // sysCapCall Linux 分发器(M4):对标 Tauri/Wails 的 Linux 一等公民面。
15
+ // - 一级实现:clipboard.*(wl-clipboard 优先、回退 xclip)、shell.open(xdg-open)、
16
+ // notification.show(notify-send)、autostart.*(XDG autostart desktop 条目)、
17
+ // app.launchArgs;数据层 path/store/os/process/update 由 sysGeneric 平台无关承接。
18
+ // - 未实现(明确报错,不静默):taskbar.*(Windows 概念)、dialog.*(GTK chooser
19
+ // 待补)、shortcut.*(X11 全局热键待补)、protocol.*、window.monitors/效果。
20
+ //
21
+ // 契约与 Windows 侧一致:外部工具都是 argv 直传(不经 shell),拒绝路径零副作用。
22
+ func (a *App) sysCapCall(method string, paramsJSON string) (result interface{}, err error) {
23
+ defer func() {
24
+ if r := recover(); r != nil {
25
+ result = nil
26
+ err = fmt.Errorf("freedom: sys method %q panicked: %v", method, r)
27
+ }
28
+ }()
29
+
30
+ var args map[string]json.RawMessage
31
+ if len(paramsJSON) > 0 && paramsJSON != "null" {
32
+ if err := json.Unmarshal([]byte(paramsJSON), &args); err != nil {
33
+ return nil, fmt.Errorf("freedom: sys method %q: invalid args: %w", method, err)
34
+ }
35
+ }
36
+ argStr := func(k string) string {
37
+ if v, ok := args[k]; ok {
38
+ var s string
39
+ if json.Unmarshal(v, &s) == nil {
40
+ return s
41
+ }
42
+ }
43
+ return ""
44
+ }
45
+ argBool := func(k string) (bool, bool) {
46
+ if v, ok := args[k]; ok {
47
+ var b bool
48
+ if json.Unmarshal(v, &b) == nil {
49
+ return b, true
50
+ }
51
+ }
52
+ return false, false
53
+ }
54
+ // 平台无关方法(path/store/os/process/update)优先由 sysGeneric 处理。
55
+ if res, ok, err := a.sysGeneric(method, args); ok {
56
+ return res, err
57
+ }
58
+
59
+ switch method {
60
+ case "clipboard.read":
61
+ return clipboardRead()
62
+ case "clipboard.write":
63
+ return nil, clipboardWrite(argStr("text"))
64
+ case "shell.open":
65
+ return nil, linuxShellOpen(argStr("target"))
66
+ case "notification.show":
67
+ // 外部工具冷启动可能卡顿:异步派发,不占 GTK 主循环(回调运行在消息泵内)。
68
+ title, body := argStr("title"), argStr("body")
69
+ appName := a.cfg.AppID
70
+ if appName == "" {
71
+ appName = defaultAppID()
72
+ }
73
+ go func() {
74
+ if err := linuxNotify(appName, title, body); err != nil {
75
+ fmt.Fprintf(os.Stderr, "freedom: %v\n", err)
76
+ }
77
+ }()
78
+ return nil, nil
79
+ case "autostart.get":
80
+ name := argStr("name")
81
+ if name == "" {
82
+ name = defaultAppID()
83
+ }
84
+ return linuxAutostartGet(name)
85
+ case "autostart.set":
86
+ name := argStr("name")
87
+ if name == "" {
88
+ name = defaultAppID()
89
+ }
90
+ enabled, ok := argBool("enabled")
91
+ if !ok {
92
+ return nil, fmt.Errorf("freedom: autostart.set 缺少 enabled")
93
+ }
94
+ return nil, linuxAutostartSet(name, enabled, argStr("args"))
95
+ case "app.launchArgs":
96
+ return launchArgsJSON(), nil
97
+
98
+ default:
99
+ return nil, fmt.Errorf("freedom: sys method %q is not supported on linux", method)
100
+ }
101
+ }
102
+
103
+ // ---- 剪贴板(wl-clipboard / xclip 双通道,argv 直传不经 shell)----
104
+
105
+ // clipboardMode 依环境变量选择后端:WAYLAND_DISPLAY 在 → wl-*,否则 xclip。
106
+ func clipboardMode() (wayland bool) {
107
+ return os.Getenv("WAYLAND_DISPLAY") != ""
108
+ }
109
+
110
+ func runCapture(name string, args []string, stdin string) (string, error) {
111
+ cmd := exec.Command(name, args...)
112
+ if stdin != "" {
113
+ cmd.Stdin = strings.NewReader(stdin)
114
+ }
115
+ out, err := cmd.Output()
116
+ if err != nil {
117
+ return "", fmt.Errorf("freedom: %s %v: %w", name, args, err)
118
+ }
119
+ return string(out), nil
120
+ }
121
+
122
+ func clipboardRead() (string, error) {
123
+ if clipboardMode() {
124
+ if s, err := runCapture("wl-paste", []string{"-n"}, ""); err == nil {
125
+ return s, nil
126
+ }
127
+ // wl-clipboard 缺席或失败 → 回退 xclip(XWayland 下可用)
128
+ }
129
+ return runCapture("xclip", []string{"-selection", "clipboard", "-o"}, "")
130
+ }
131
+
132
+ func clipboardWrite(text string) error {
133
+ if clipboardMode() {
134
+ // wl-copy fork 后台常驻持有 clipboard selection;它继承 stdout/stderr,
135
+ // 用 Output() 会等常驻进程的管道 EOF 永久阻塞,故只 Run 不捕获输出。
136
+ wc := exec.Command("wl-copy")
137
+ wc.Stdin = strings.NewReader(text)
138
+ if err := wc.Run(); err == nil {
139
+ return nil
140
+ }
141
+ // 失败(如无 Wayland 会话权限)回退 xclip(XWayland 下可用)
142
+ }
143
+ // xclip 自行 fork 常驻为属主;EOF 到达即完成装载
144
+ cmd := exec.Command("xclip", "-selection", "clipboard", "-i")
145
+ cmd.Stdin = strings.NewReader(text)
146
+ return cmd.Run()
147
+ }
148
+
149
+ // ---- openExternal / 通知 ----
150
+
151
+ func linuxShellOpen(target string) error {
152
+ if target == "" {
153
+ return fmt.Errorf("freedom: shell.open 缺少 target")
154
+ }
155
+ if !shellTargetAllowed(target) {
156
+ return fmt.Errorf("freedom: 拒绝打开非白名单目标 %q(仅允许 http/https/mailto)", target)
157
+ }
158
+ // xdg-open 会阻塞到目标应用退出:Start 后即撒手,Wait 回收僵尸。
159
+ cmd := exec.Command("xdg-open", target)
160
+ if err := cmd.Start(); err != nil {
161
+ return fmt.Errorf("freedom: xdg-open %q: %w", target, err)
162
+ }
163
+ go cmd.Wait()
164
+ return nil
165
+ }
166
+
167
+ // notifyArgs 构造 notify-send argv;"--" 终止选项解析,防 title/body 以 - 开头被当参数。
168
+ func notifyArgs(appName, title, body string) []string {
169
+ args := []string{"--app-name", appName, "--", title}
170
+ if body != "" {
171
+ args = append(args, body)
172
+ }
173
+ return args
174
+ }
175
+
176
+ func linuxNotify(appName, title, body string) error {
177
+ cmd := exec.Command("notify-send", notifyArgs(appName, title, body)...)
178
+ if out, err := cmd.CombinedOutput(); err != nil {
179
+ return fmt.Errorf("freedom: 通知发送失败: %w (%s)", err, strings.TrimSpace(string(out)))
180
+ }
181
+ return nil
182
+ }
183
+
184
+ // ---- 开机自启(XDG autostart:~/.config/autostart/<name>.desktop)----
185
+
186
+ func autostartDir() (string, error) {
187
+ base := os.Getenv("XDG_CONFIG_HOME")
188
+ if base == "" {
189
+ home, err := os.UserHomeDir()
190
+ if err != nil {
191
+ return "", fmt.Errorf("freedom: 无法定位配置目录: %w", err)
192
+ }
193
+ base = filepath.Join(home, ".config")
194
+ }
195
+ return filepath.Join(base, "autostart"), nil
196
+ }
197
+
198
+ func desktopEntryPath(name string) (string, error) {
199
+ if name == "" {
200
+ return "", fmt.Errorf("freedom: 自启项名称为空或非法")
201
+ }
202
+ dir, err := autostartDir()
203
+ if err != nil {
204
+ return "", err
205
+ }
206
+ return filepath.Join(dir, sanitizeName(name)+".desktop"), nil
207
+ }
208
+
209
+ // desktopExecOwned 判断 .desktop 的 Exec 行是否指向本程序(防覆盖/误删他程序条目)。
210
+ func desktopExecOwned(content, exePath string) bool {
211
+ for _, line := range strings.Split(content, "\n") {
212
+ v := strings.TrimSpace(line)
213
+ if !strings.HasPrefix(v, "Exec=") {
214
+ continue
215
+ }
216
+ v = strings.ToLower(strings.TrimSpace(strings.TrimPrefix(v, "Exec=")))
217
+ p := strings.ToLower(exePath)
218
+ return strings.HasPrefix(v, `"`+p+`"`) || strings.HasPrefix(v, p)
219
+ }
220
+ return false
221
+ }
222
+
223
+ func linuxAutostartSet(name string, enabled bool, args string) error {
224
+ path, err := desktopEntryPath(name)
225
+ if err != nil {
226
+ return err
227
+ }
228
+ if !enabled {
229
+ old, readErr := os.ReadFile(path)
230
+ if readErr != nil {
231
+ if os.IsNotExist(readErr) {
232
+ return nil // 幂等:本就不存在
233
+ }
234
+ return readErr
235
+ }
236
+ exe, err := os.Executable()
237
+ if err != nil {
238
+ return fmt.Errorf("freedom: 无法定位自身路径: %w", err)
239
+ }
240
+ if !desktopExecOwned(string(old), exe) {
241
+ return fmt.Errorf("freedom: 自启项 %q 不属于本程序,拒绝删除", name)
242
+ }
243
+ return os.Remove(path)
244
+ }
245
+ exe, err := os.Executable()
246
+ if err != nil {
247
+ return fmt.Errorf("freedom: 无法定位自身路径: %w", err)
248
+ }
249
+ if old, readErr := os.ReadFile(path); readErr == nil && !desktopExecOwned(string(old), exe) {
250
+ return fmt.Errorf("freedom: 自启项 %q 已存在且不属于本程序,拒绝覆盖", name)
251
+ }
252
+ execLine := `"` + exe + `"`
253
+ if a := strings.TrimSpace(args); a != "" {
254
+ execLine += " " + a
255
+ }
256
+ // DisplayName 未透传:Name 用消毒后的条目名,足够桌面环境展示与撤回。
257
+ content := "[Desktop Entry]\nType=Application\nName=" + sanitizeName(name) +
258
+ "\nExec=" + execLine + "\nX-GNOME-Autostart-enabled=true\nTerminal=false\n"
259
+ if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
260
+ return err
261
+ }
262
+ return os.WriteFile(path, []byte(content), 0o644)
263
+ }
264
+
265
+ func linuxAutostartGet(name string) (bool, error) {
266
+ path, err := desktopEntryPath(name)
267
+ if err != nil {
268
+ return false, err
269
+ }
270
+ _, statErr := os.Stat(path)
271
+ if statErr == nil {
272
+ return true, nil
273
+ }
274
+ if os.IsNotExist(statErr) {
275
+ return false, nil
276
+ }
277
+ return false, statErr
278
+ }
@@ -0,0 +1,22 @@
1
+ //go:build !windows && !linux
2
+
3
+ package freedom
4
+
5
+ import (
6
+ "encoding/json"
7
+ "fmt"
8
+ )
9
+
10
+ // sysCapCall 非 Windows/Linux 平台(macOS 等):系统能力暂不支持,但平台无关的
11
+ // 数据层方法(path/store/os/process,见 store.go 的 sysGeneric)在三端一致可用。
12
+ // Linux 实装见 syscap_linux.go(M4)。
13
+ func (a *App) sysCapCall(method string, paramsJSON string) (interface{}, error) {
14
+ var args map[string]json.RawMessage
15
+ if len(paramsJSON) > 0 && paramsJSON != "null" {
16
+ _ = json.Unmarshal([]byte(paramsJSON), &args)
17
+ }
18
+ if res, ok, err := a.sysGeneric(method, args); ok {
19
+ return res, err
20
+ }
21
+ return nil, fmt.Errorf("freedom: sys method %q is not supported on this platform", method)
22
+ }