magic-agent 1.0.1 → 1.0.3

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 (52) hide show
  1. package/CLIENT-CONTRACT.md +298 -0
  2. package/README.md +35 -0
  3. package/client-ui/assets/OFL-Caveat.txt +93 -0
  4. package/client-ui/assets/caveat.woff2 +0 -0
  5. package/client-ui/index.html +246 -0
  6. package/client-ui/pc/app.css +5110 -0
  7. package/client-ui/pc/assets/damiao-mascot.png +0 -0
  8. package/client-ui/pc/assets/icon.png +0 -0
  9. package/client-ui/pc/index.html +154 -0
  10. package/client-ui/pc/js/00-base.js +52 -0
  11. package/client-ui/pc/js/03-chat-message.js +880 -0
  12. package/client-ui/pc/js/04-task-list.js +430 -0
  13. package/client-ui/pc/js/05-task-list-render.js +812 -0
  14. package/client-ui/pc/js/06-ask-answer.js +204 -0
  15. package/client-ui/pc/js/07-chat-stream-render.js +493 -0
  16. package/client-ui/pc/js/08-ask-event-map.js +171 -0
  17. package/client-ui/pc/js/09-stream-apply.js +367 -0
  18. package/client-ui/pc/js/10-markdown.js +430 -0
  19. package/client-ui/pc/js/18-askbox.js +262 -0
  20. package/client-ui/pc/js/19-attachments.js +679 -0
  21. package/client-ui/pc/js/29-msg-variants.js +462 -0
  22. package/client-ui/pc/js/90-glue.js +212 -0
  23. package/client-ui/pc/js/91-workbench-stubs.js +134 -0
  24. package/client-ui/pc/js/92-composer.js +406 -0
  25. package/client-ui/pc/js/93-rail.js +362 -0
  26. package/client-ui/pc/vendor/highlight.min.js +1262 -0
  27. package/client-ui/pc/vendor/marked.min.js +6 -0
  28. package/client-ui/pc/vendor/purify.min.js +3 -0
  29. package/client-ui/shared/conversation.css +256 -0
  30. package/client-ui/shared/conversation.js +1225 -0
  31. package/client-ui/shared/transport-http.js +123 -0
  32. package/client-ui-mobile/assets/OFL-Caveat.txt +93 -0
  33. package/client-ui-mobile/assets/caveat.woff2 +0 -0
  34. package/client-ui-mobile/index.html +173 -0
  35. package/internal/agent/custom_models.go +151 -5
  36. package/internal/agent/custom_models_test.go +130 -7
  37. package/internal/agent/env.go +10 -2
  38. package/internal/agent/env_test.go +37 -0
  39. package/internal/agent/repair_models_test.go +194 -0
  40. package/internal/cli/ask.go +22 -18
  41. package/internal/cli/login.go +61 -4
  42. package/internal/cli/login_test.go +99 -0
  43. package/internal/cli/repair.go +60 -0
  44. package/internal/cli/repair_test.go +51 -0
  45. package/internal/cli/root.go +21 -0
  46. package/internal/cli/session.go +8 -1
  47. package/npm/dist/darwin-arm64/magic-agent +0 -0
  48. package/npm/dist/darwin-x64/magic-agent +0 -0
  49. package/npm/dist/linux-arm64/magic-agent +0 -0
  50. package/npm/dist/linux-x64/magic-agent +0 -0
  51. package/npm/dist/win32-x64/magic-agent.exe +0 -0
  52. package/package.json +4 -1
@@ -0,0 +1,60 @@
1
+ package cli
2
+
3
+ // repair.go - `--repair-models <engine>`:还原被反向同步写坏的桌面端 models.json。
4
+ //
5
+ // 为什么要有它:2026-09-30 的 Windows 事故里,magic-agent 把桌面端(WorkBuddy)那份
6
+ // **数组格式**的 models.json 覆盖成了 `{availableModels, models}` 对象格式,桌面端
7
+ // 解析不了 → 模型清单整个为空。同步那一侧已经加了「陌生形状一律不动」的硬约束,
8
+ // 但**已经坏掉的现场**需要一条能自己走回来的路 —— 否则用户只能手抄 apiKey。
9
+ //
10
+ // 还原逻辑在 agent.RepairDesktopModels(见 custom_models.go 的事故还原一节):
11
+ // 只认明确的坏形状才动手,动手前先留 .broken.bak。
12
+
13
+ import (
14
+ "encoding/json"
15
+ "fmt"
16
+ "strings"
17
+
18
+ "github.com/darren/magic-agent/internal/agent"
19
+ "github.com/spf13/cobra"
20
+ )
21
+
22
+ // runRepairModels 处理 `--repair-models <engine>`。
23
+ func runRepairModels(cmd *cobra.Command, opts *askOptions) error {
24
+ name := strings.TrimSpace(opts.repairModels)
25
+ if name == "" {
26
+ return &usageError{fmt.Errorf(
27
+ "--repair-models 需要引擎名:magic-agent --repair-models <engine>(可用:%s)",
28
+ strings.Join(agent.DesktopEngines(), " / "))}
29
+ }
30
+
31
+ // 引擎名先在这里校验:这是**参数错**(exit 2),而不是运行期失败(exit 1)。
32
+ // agent 层还会再兜一次,但那属于「不该发生」的安全网。
33
+ if agent.DesktopAppHomeOf(name) == "" {
34
+ return &usageError{fmt.Errorf("引擎 %q 没有对应的桌面端数据目录,没有可修的 models.json(可用:%s)",
35
+ name, strings.Join(agent.DesktopEngines(), " / "))}
36
+ }
37
+
38
+ repaired, note, err := agent.RepairDesktopModels(name)
39
+ if err != nil {
40
+ return err
41
+ }
42
+
43
+ // 机器可读的一行结果走 stdout(与 --sessions / --stop 同一族);人看的说明走 stderr。
44
+ if eerr := json.NewEncoder(cmd.OutOrStdout()).Encode(map[string]any{
45
+ "engine": name,
46
+ "repaired": repaired,
47
+ "note": note,
48
+ }); eerr != nil {
49
+ return eerr
50
+ }
51
+ out := cmd.ErrOrStderr()
52
+ fmt.Fprintf(out, "magic-agent: %s\n", note)
53
+ if !repaired {
54
+ // 没改动不是失败,但必须说清「没动」,不静默(本项目铁律)。
55
+ fmt.Fprintf(out, "magic-agent: 未改动任何文件。形状对不上就不动,避免二次破坏。\n")
56
+ return nil
57
+ }
58
+ fmt.Fprintf(out, "magic-agent: 已还原。请重开桌面端确认模型清单;官方模型由桌面端自己拉取,不在这个文件里。\n")
59
+ return nil
60
+ }
@@ -0,0 +1,51 @@
1
+ package cli
2
+
3
+ // repair_test.go - `--repair-models` 的参数校验。
4
+ //
5
+ // 落地逻辑(形状识别 / 还原 / 备份 / 幂等)在 internal/agent/repair_models_test.go,
6
+ // 这里只锁 CLI 层的契约:引擎名缺失或不支持桌面端时是**参数错**(exit 2),
7
+ // 而不是被当成 prompt 发给引擎。
8
+
9
+ import (
10
+ "strings"
11
+ "testing"
12
+ )
13
+
14
+ func TestRepairModelsWithoutEngineNameIsUsageError(t *testing.T) {
15
+ _, _, err := runAskCmd(t, "", "--repair-models")
16
+ if err == nil {
17
+ t.Fatal("--repair-models 缺引擎名应报错")
18
+ }
19
+ if !strings.Contains(err.Error(), "需要引擎名") {
20
+ t.Errorf("应是中文用法提示,实际 %q", err.Error())
21
+ }
22
+ if _, ok := err.(*usageError); !ok {
23
+ t.Errorf("应是 usageError(exit 2),实际 %T", err)
24
+ }
25
+ }
26
+
27
+ // 显式给空串同样走校验(与 --login 同一取舍:用「传过没有」而不是「值非空」判断)。
28
+ func TestRepairModelsEmptyEngineIsUsageError(t *testing.T) {
29
+ _, _, err := runAskCmd(t, "", "--repair-models", "")
30
+ if err == nil {
31
+ t.Fatal("空引擎名应报错")
32
+ }
33
+ if _, ok := err.(*usageError); !ok {
34
+ t.Errorf("应是 usageError(exit 2),实际 %T", err)
35
+ }
36
+ }
37
+
38
+ // 没有桌面端数据目录的引擎:参数错,并点名可用值。
39
+ func TestRepairModelsUnsupportedEngine(t *testing.T) {
40
+ _, _, err := runAskCmd(t, "", "--repair-models", "claude")
41
+ if err == nil {
42
+ t.Fatal("claude 没有桌面端 models.json,应报错")
43
+ }
44
+ msg := err.Error()
45
+ if !strings.Contains(msg, "没有对应的桌面端数据目录") {
46
+ t.Errorf("错误应说明该引擎没有桌面端目录,实际 %q", msg)
47
+ }
48
+ if _, ok := err.(*usageError); !ok {
49
+ t.Errorf("应是 usageError(exit 2),实际 %T", err)
50
+ }
51
+ }
@@ -129,6 +129,7 @@ func NewRootCommand() *cobra.Command {
129
129
  magic-agent --engines # 列出引擎、可用性与各引擎支持的模型(JSON 数组)
130
130
  magic-agent --engines --no-models # 只列引擎与可用性(跳过模型探测,不启动 CLI)
131
131
  magic-agent --engines --json # 等价(--json 为兼容保留)
132
+ magic-agent --repair-models codebuddy # 还原被覆盖写坏的桌面端 models.json(事故收尾,先留 .broken.bak)
132
133
 
133
134
  四档权限模型(--permission,仅 claude / codebuddy / codebuddy-ai 生效):
134
135
  把「哪些动作自动放行 + 放行不了时由谁裁决」收敛成四档,映射到 Claude Code 的参数:
@@ -399,6 +400,9 @@ codebuddy-gateway 引擎(CodeBuddy Code HTTP 网关,走 webhook + SSE;凭
399
400
  case flagChanged(cmd, "login"):
400
401
  // 用「传过没有」而不是「值非空」判断:`--login ""` 也要走参数校验(提示要引擎名)
401
402
  return runLogin(cmd, opts)
403
+ case flagChanged(cmd, "repair-models"):
404
+ // 同上:`--repair-models ""` 要走参数校验,而不是掉进「提问」分支报 empty prompt。
405
+ return runRepairModels(cmd, opts)
402
406
  case opts.listSessions:
403
407
  return runSessions(cmd, opts)
404
408
  case flagChanged(cmd, "session-log"):
@@ -426,6 +430,23 @@ codebuddy-gateway 引擎(CodeBuddy Code HTTP 网关,走 webhook + SSE;凭
426
430
  },
427
431
  }
428
432
  bindAskFlags(root, opts)
433
+ /* `--login` / `--repair-models` 忘带引擎名时,cobra 在解析阶段就报错,**走不到**
434
+ runLogin / runRepairModels 的校验分支,默认文案是英文的
435
+ "flag needs an argument: ..."(且退出码 1)。这里翻成与传空串一致的中文
436
+ usage(exit 2),免得用户只看到一句看不懂的报错。 */
437
+ root.SetFlagErrorFunc(func(_ *cobra.Command, err error) error {
438
+ if err == nil || !strings.Contains(err.Error(), "flag needs an argument") {
439
+ return err
440
+ }
441
+ switch {
442
+ case strings.Contains(err.Error(), "login"):
443
+ return &usageError{fmt.Errorf("--login 需要引擎名:magic-agent --login <engine>(可用:%s)", loginCapableEngines())}
444
+ case strings.Contains(err.Error(), "repair-models"):
445
+ return &usageError{fmt.Errorf("--repair-models 需要引擎名:magic-agent --repair-models <engine>(可用:%s)",
446
+ strings.Join(agent.DesktopEngines(), " / "))}
447
+ }
448
+ return err
449
+ })
429
450
  // 无子命令设计:禁用 cobra 自动注入的 completion 命令,
430
451
  // 保证 "magic-agent completion" 也只是被当作 prompt 而非隐藏子命令。
431
452
  root.CompletionOptions.DisableDefaultCmd = true
@@ -72,11 +72,18 @@ func stopActiveSession() {
72
72
  // 为什么必须做:引擎 CLI 在**独立进程组**里(Setpgid),调用方对 magic-agent
73
73
  // 发 SIGTERM / kill(-magic-agent-pid) 时,引擎子进程不在那一组里 → 会变成孤儿继续跑
74
74
  // (观物台「停止」按钮踩的就是这个)。这里在退出前补一刀。
75
+ //
76
+ // 覆盖三处孤儿来源:
77
+ // - 当前活动会话(stopActiveSession);
78
+ // - 交互式登录子进程(stopLoginChild)—— 它必须待在前台组里读 tty,因而
79
+ // 比普通引擎更「自己管不了」,只能在这里记一笔带走(见 login.go);
80
+ // - 关终端(SIGHUP):与 SIGTERM 同路。
75
81
  func installSignalStop() {
76
82
  ch := make(chan os.Signal, 1)
77
- signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
83
+ signal.Notify(ch, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
78
84
  go func() {
79
85
  <-ch
86
+ stopLoginChild()
80
87
  stopActiveSession()
81
88
  os.Exit(exitInterrupted)
82
89
  }()
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magic-agent",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "专业的 agent CLI 代理工具 - 统一 claude / codebuddy / trae / llm / codex / openclaw 引擎",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -22,6 +22,9 @@
22
22
  "internal/",
23
23
  "go.mod",
24
24
  "go.sum",
25
+ "client-ui/",
26
+ "client-ui-mobile/",
27
+ "CLIENT-CONTRACT.md",
25
28
  "README.md",
26
29
  "LICENSE"
27
30
  ],