chore: update go-resty to v2.17.2 and clean up docs/plans
This commit is contained in:
parent
24e66b8f18
commit
8b5f3430a1
4 changed files with 3 additions and 307 deletions
|
|
@ -1,52 +0,0 @@
|
|||
# macOS 开机启动 (launchd) 设计方案
|
||||
|
||||
## 概述
|
||||
|
||||
添加 `picoclaw install` 命令,自动生成 macOS launchd 的 plist 配置文件并安装到 `~/Library/LaunchAgents/`,实现开机自动启动 `picoclaw gateway`。
|
||||
|
||||
## 详细设计
|
||||
|
||||
### 1. plist 文件内容
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>io.picoclaw.gateway</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/local/bin/picoclaw</string>
|
||||
<string>gateway</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
### 2. 命令设计
|
||||
|
||||
| 命令 | 描述 |
|
||||
|------|------|
|
||||
| `picoclaw install` | 安装 launchd plist(当前用户) |
|
||||
| `picoclaw install --uninstall` | 卸载 launchd plist |
|
||||
| `picoclaw install --status` | 查看安装状态 |
|
||||
|
||||
### 3. 实现位置
|
||||
|
||||
在 `cmd/picoclaw/main.go` 中添加 `install` case。
|
||||
|
||||
### 4. 可执行文件路径
|
||||
|
||||
- 优先使用 `which picoclaw` 查找已安装路径
|
||||
- 如果找不到,提示用户需要先 `go install` 或手动复制
|
||||
|
||||
## 实现步骤
|
||||
|
||||
1. 在 main.go 添加 install case
|
||||
2. 实现 installCmd() 函数
|
||||
3. 添加 uninstall 和 status 子命令
|
||||
|
|
@ -1,252 +0,0 @@
|
|||
# macOS 开机启动 (launchd) 实现计划
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** 在 picoclaw 项目中添加 `picoclaw install` 命令,实现 macOS 开机自动启动 `picoclaw gateway`
|
||||
|
||||
**Architecture:** 通过生成 launchd plist 配置文件到 `~/Library/LaunchAgents/` 目录,实现用户级开机自启动
|
||||
|
||||
**Tech Stack:** Go, macOS launchd
|
||||
|
||||
---
|
||||
|
||||
## 实现步骤
|
||||
|
||||
### Task 1: 添加 install case 到 main.go
|
||||
|
||||
**Files:**
|
||||
- Modify: `cmd/picoclaw/main.go:126-145`
|
||||
|
||||
**Step 1: 在 main.go 的 switch 语句中添加 install case**
|
||||
|
||||
在 `case "cron":` 之前添加:
|
||||
|
||||
```go
|
||||
case "install":
|
||||
installCmd()
|
||||
```
|
||||
|
||||
**Step 2: 运行测试验证**
|
||||
|
||||
Run: `go build ./cmd/picoclaw/`
|
||||
Expected: 编译成功(因为 installCmd 尚未定义,会有警告)
|
||||
|
||||
---
|
||||
|
||||
### Task 2: 实现 installCmd 函数
|
||||
|
||||
**Files:**
|
||||
- Modify: `cmd/picoclaw/main.go:199-216`
|
||||
|
||||
**Step 1: 添加 installCmd 函数和帮助信息**
|
||||
|
||||
在 `printHelp()` 函数后添加:
|
||||
|
||||
```go
|
||||
func installCmd() {
|
||||
args := os.Args[2:]
|
||||
for _, arg := range args {
|
||||
switch arg {
|
||||
case "--uninstall":
|
||||
uninstallInstall()
|
||||
return
|
||||
case "--status":
|
||||
statusInstall()
|
||||
return
|
||||
case "--help", "-h":
|
||||
installHelp()
|
||||
return
|
||||
}
|
||||
}
|
||||
doInstall()
|
||||
}
|
||||
|
||||
func installHelp() {
|
||||
fmt.Println("\nInstall commands:")
|
||||
fmt.Println(" picoclaw install Install launchd plist (current user)")
|
||||
fmt.Println(" picoclaw install --uninstall Uninstall launchd plist")
|
||||
fmt.Println(" picoclaw install --status Show install status")
|
||||
}
|
||||
|
||||
func doInstall() {
|
||||
// 查找 picoclaw 可执行文件路径
|
||||
execPath, err := findExecutable()
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 生成 plist 内容
|
||||
plistContent := generatePlist(execPath)
|
||||
|
||||
// 写入文件
|
||||
plistPath := filepath.Join(os.Getenv("HOME"), "Library", "LaunchAgents", "io.picoclaw.gateway.plist")
|
||||
if err := os.WriteFile(plistPath, []byte(plistContent), 0644); err != nil {
|
||||
fmt.Printf("Error writing plist: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Installed launchd plist to %s\n", plistPath)
|
||||
fmt.Println("Run 'picoclaw install --status' to check status")
|
||||
}
|
||||
|
||||
func findExecutable() (string, error) {
|
||||
// 使用 which 查找
|
||||
cmd := exec.Command("which", "picoclaw")
|
||||
out, err := cmd.Output()
|
||||
if err == nil {
|
||||
return strings.TrimSpace(string(out)), nil
|
||||
}
|
||||
|
||||
// 尝试常见路径
|
||||
commonPaths := []string{
|
||||
"/usr/local/bin/picoclaw",
|
||||
"/usr/bin/picoclaw",
|
||||
filepath.Join(os.Getenv("HOME"), "go", "bin", "picoclaw"),
|
||||
}
|
||||
|
||||
for _, p := range commonPaths {
|
||||
if _, err := os.Stat(p); err == nil {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("picoclaw not found in PATH. Please run 'go install' first or ensure picoclaw is in your PATH")
|
||||
}
|
||||
|
||||
func generatePlist(execPath string) string {
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>io.picoclaw.gateway</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>` + execPath + `</string>
|
||||
<string>gateway</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
`
|
||||
}
|
||||
|
||||
func uninstallInstall() {
|
||||
plistPath := filepath.Join(os.Getenv("HOME"), "Library", "LaunchAgents", "io.picoclaw.gateway.plist")
|
||||
if _, err := os.Stat(plistPath); os.IsNotExist(err) {
|
||||
fmt.Println("No launchd plist found.")
|
||||
return
|
||||
}
|
||||
|
||||
// 先 unload
|
||||
exec.Command("launchctl", "unload", plistPath).Run()
|
||||
|
||||
if err := os.Remove(plistPath); err != nil {
|
||||
fmt.Printf("Error removing plist: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Removed %s\n", plistPath)
|
||||
}
|
||||
|
||||
func statusInstall() {
|
||||
plistPath := filepath.Join(os.Getenv("HOME"), "Library", "LaunchAgents", "io.picoclaw.gateway.plist")
|
||||
|
||||
if _, err := os.Stat(plistPath); os.IsNotExist(err) {
|
||||
fmt.Println("Status: Not installed")
|
||||
fmt.Println("Run 'picoclaw install' to install")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Status: Installed")
|
||||
fmt.Printf("Path: %s\n", plistPath)
|
||||
|
||||
// 检查是否正在运行
|
||||
cmd := exec.Command("launchctl", "list", "io.picoclaw.gateway")
|
||||
out, err := cmd.Output()
|
||||
if err == nil {
|
||||
fmt.Println("\nRunning: Yes")
|
||||
fmt.Println(string(out))
|
||||
} else {
|
||||
fmt.Println("\nRunning: No")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: 添加 import**
|
||||
|
||||
确保已导入 `os/exec`:
|
||||
|
||||
```go
|
||||
import (
|
||||
// ... 其他 import
|
||||
"os/exec"
|
||||
)
|
||||
```
|
||||
|
||||
**Step 3: 运行测试验证**
|
||||
|
||||
Run: `go build ./cmd/picoclaw/`
|
||||
Expected: 编译成功
|
||||
|
||||
**Step 4: 手动测试**
|
||||
|
||||
```bash
|
||||
./picoclaw install --help
|
||||
./picoclaw install --status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: 更新帮助信息
|
||||
|
||||
**Files:**
|
||||
- Modify: `cmd/picoclaw/main.go:201-215`
|
||||
|
||||
**Step 1: 在 printHelp 中添加 install 命令**
|
||||
|
||||
在 Commands 列表中添加:
|
||||
|
||||
```go
|
||||
fmt.Println(" install Install/uninstall launchd service (macOS)")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: 测试完整流程
|
||||
|
||||
**Step 1: 构建并测试安装**
|
||||
|
||||
```bash
|
||||
go build -o picoclaw ./cmd/picoclaw/
|
||||
./picoclaw install
|
||||
./picoclaw install --status
|
||||
```
|
||||
|
||||
Expected: 成功创建 plist 文件
|
||||
|
||||
**Step 2: 测试卸载**
|
||||
|
||||
```bash
|
||||
./picoclaw install --uninstall
|
||||
./picoclaw install --status
|
||||
```
|
||||
|
||||
Expected: 成功删除 plist 文件
|
||||
|
||||
---
|
||||
|
||||
### Task 5: 提交代码
|
||||
|
||||
```bash
|
||||
git add cmd/picoclaw/main.go
|
||||
git commit -m "feat: add macOS launchd autostart support
|
||||
|
||||
- Add 'picoclaw install' command
|
||||
- Support install, uninstall, and status options
|
||||
- Auto-detect picoclaw executable path"
|
||||
```
|
||||
2
go.mod
2
go.mod
|
|
@ -26,7 +26,7 @@ require (
|
|||
github.com/bytedance/sonic/loader v0.5.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/github/copilot-sdk/go v0.1.23
|
||||
github.com/go-resty/resty/v2 v2.17.1 // indirect
|
||||
github.com/go-resty/resty/v2 v2.17.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/jsonschema-go v0.4.2 // indirect
|
||||
github.com/grbit/go-json v0.11.0 // indirect
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -36,8 +36,8 @@ github.com/github/copilot-sdk/go v0.1.23 h1:uExtO/inZQndCZMiSAA1hvXINiz9tqo/MZgQ
|
|||
github.com/github/copilot-sdk/go v0.1.23/go.mod h1:GdwwBfMbm9AABLEM3x5IZKw4ZfwCYxZ1BgyytmZenQ0=
|
||||
github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
|
||||
github.com/go-resty/resty/v2 v2.6.0/go.mod h1:PwvJS6hvaPkjtjNg9ph+VrSD92bi5Zq73w/BIH7cC3Q=
|
||||
github.com/go-resty/resty/v2 v2.17.1 h1:x3aMpHK1YM9e4va/TMDRlusDDoZiQ+ViDu/WpA6xTM4=
|
||||
github.com/go-resty/resty/v2 v2.17.1/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
|
||||
github.com/go-resty/resty/v2 v2.17.2 h1:FQW5oHYcIlkCNrMD2lloGScxcHJ0gkjshV3qcQAyHQk=
|
||||
github.com/go-resty/resty/v2 v2.17.2/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
||||
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue