feat: 新增 pkg.install/remove/search/info 包管理命令
This commit is contained in:
+86
-2
@@ -2,13 +2,28 @@
|
|||||||
|
|
||||||
> 返回 [README.md](README.md)
|
> 返回 [README.md](README.md)
|
||||||
|
|
||||||
刷新系统包管理器的软件源索引,或列出已安装的软件包。
|
管理软件包:列出、搜索、安装、卸载、刷新索引。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kopsc pkg list # 列出已安装的软件包
|
kopsc pkg list # 列出已安装的软件包
|
||||||
kopsc pkg refreshindex # 刷新包管理器索引(dnf/apt/apk/brew/winget,按平台自动选)
|
kopsc pkg search nginx # 搜索软件包
|
||||||
|
kopsc pkg install nginx # 安装软件包
|
||||||
|
kopsc pkg remove nginx # 卸载软件包
|
||||||
|
kopsc pkg info nginx # 查看软件包详情
|
||||||
|
kopsc pkg refreshindex # 刷新包管理器索引
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 命令一览
|
||||||
|
|
||||||
|
| 命令 | 作用 |
|
||||||
|
| --- | --- |
|
||||||
|
| `kopsc pkg list` | 列出已安装的软件包 |
|
||||||
|
| `kopsc pkg search <关键字>` | 搜索软件包 |
|
||||||
|
| `kopsc pkg install <包名>` | 安装软件包(需 `--yes`) |
|
||||||
|
| `kopsc pkg remove <包名>` | 卸载软件包(需 `--yes`) |
|
||||||
|
| `kopsc pkg info <包名>` | 查看软件包详情 |
|
||||||
|
| `kopsc pkg refreshindex` | 刷新包管理器索引 |
|
||||||
|
|
||||||
## 列出已安装的软件包(pkg.list)
|
## 列出已安装的软件包(pkg.list)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -25,6 +40,75 @@ kopsc pkg list
|
|||||||
| macOS | `brew list` |
|
| macOS | `brew list` |
|
||||||
| Windows | `winget list` |
|
| Windows | `winget list` |
|
||||||
|
|
||||||
|
## 搜索软件包(pkg.search)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kopsc pkg search nginx
|
||||||
|
kopsc pkg search redis
|
||||||
|
```
|
||||||
|
|
||||||
|
在软件源中搜索匹配关键字的软件包。
|
||||||
|
|
||||||
|
| 平台 | 实际执行 |
|
||||||
|
| --- | --- |
|
||||||
|
| Linux(Debian/Ubuntu) | `apt-cache search` |
|
||||||
|
| Linux(CentOS/RHEL/Fedora) | `dnf search` |
|
||||||
|
| Linux(Alpine) | `apk search` |
|
||||||
|
| macOS | `brew search` |
|
||||||
|
| Windows | `winget search` |
|
||||||
|
|
||||||
|
## 安装软件包(pkg.install)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kopsc pkg install nginx # 预览将执行的操作
|
||||||
|
kopsc pkg install nginx --yes # 确认安装
|
||||||
|
kopsc pkg install nginx redis -y # 安装多个包
|
||||||
|
```
|
||||||
|
|
||||||
|
安装指定的软件包。这是破坏性命令,需要 `--yes` 确认。
|
||||||
|
|
||||||
|
| 平台 | 实际执行 |
|
||||||
|
| --- | --- |
|
||||||
|
| Linux(Debian/Ubuntu) | `apt-get install` |
|
||||||
|
| Linux(CentOS/RHEL/Fedora) | `dnf install` |
|
||||||
|
| Linux(Alpine) | `apk add` |
|
||||||
|
| macOS | `brew install` |
|
||||||
|
| Windows | `winget install` |
|
||||||
|
|
||||||
|
## 卸载软件包(pkg.remove)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kopsc pkg remove nginx # 预览将执行的操作
|
||||||
|
kopsc pkg remove nginx --yes # 确认卸载
|
||||||
|
```
|
||||||
|
|
||||||
|
卸载指定的软件包。这是破坏性命令,需要 `--yes` 确认。
|
||||||
|
|
||||||
|
| 平台 | 实际执行 |
|
||||||
|
| --- | --- |
|
||||||
|
| Linux(Debian/Ubuntu) | `apt-get remove` |
|
||||||
|
| Linux(CentOS/RHEL/Fedora) | `dnf remove` |
|
||||||
|
| Linux(Alpine) | `apk del` |
|
||||||
|
| macOS | `brew uninstall` |
|
||||||
|
| Windows | `winget uninstall` |
|
||||||
|
|
||||||
|
## 查看软件包详情(pkg.info)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kopsc pkg info nginx
|
||||||
|
kopsc pkg info python3
|
||||||
|
```
|
||||||
|
|
||||||
|
显示指定软件包的详细信息(版本、描述、依赖等)。
|
||||||
|
|
||||||
|
| 平台 | 实际执行 |
|
||||||
|
| --- | --- |
|
||||||
|
| Linux(Debian/Ubuntu) | `apt show`(回退 `dpkg -s`) |
|
||||||
|
| Linux(CentOS/RHEL/Fedora) | `rpm -qi` |
|
||||||
|
| Linux(Alpine) | `apk info -a` |
|
||||||
|
| macOS | `brew info` |
|
||||||
|
| Windows | `winget show` |
|
||||||
|
|
||||||
## 刷新软件源索引(pkg.refreshindex)
|
## 刷新软件源索引(pkg.refreshindex)
|
||||||
|
|
||||||
`kopsc pkg refreshindex` 刷新本机包管理器的软件源索引,**只更新索引,不升级也不安装任何软件包**。
|
`kopsc pkg refreshindex` 刷新本机包管理器的软件源索引,**只更新索引,不升级也不安装任何软件包**。
|
||||||
|
|||||||
@@ -32,62 +32,72 @@ type BuiltinRequest struct {
|
|||||||
type Builtin func(ctx context.Context, req BuiltinRequest) error
|
type Builtin func(ctx context.Context, req BuiltinRequest) error
|
||||||
|
|
||||||
var builtins = map[string]Builtin{
|
var builtins = map[string]Builtin{
|
||||||
"archive.extract": runArchiveExtract,
|
"archive.create": runArchiveCreate,
|
||||||
"disk.usage": runDiskUsage,
|
"archive.extract": runArchiveExtract,
|
||||||
"diagnose": runDiagnose,
|
"archive.list": runArchiveList,
|
||||||
"docker.df": runDockerDF,
|
"disk.check": runDiskCheck,
|
||||||
"docker.exec": runDockerExec,
|
"disk.inode": runDiskInode,
|
||||||
"docker.load": runDockerLoad,
|
"disk.usage": runDiskUsage,
|
||||||
"docker.logs": runDockerLogs,
|
"diagnose": runDiagnose,
|
||||||
"docker.restart": runDockerRestart,
|
"docker.df": runDockerDF,
|
||||||
"docker.rm": runDockerRM,
|
"docker.exec": runDockerExec,
|
||||||
|
"docker.load": runDockerLoad,
|
||||||
|
"docker.logs": runDockerLogs,
|
||||||
|
"docker.restart": runDockerRestart,
|
||||||
|
"docker.rm": runDockerRM,
|
||||||
"docker.rm.preview": runDockerPreviewRM,
|
"docker.rm.preview": runDockerPreviewRM,
|
||||||
"docker.rmi": runDockerRMI,
|
"docker.rmi": runDockerRMI,
|
||||||
"docker.rmi.preview": runDockerPreviewRMI,
|
"docker.rmi.preview": runDockerPreviewRMI,
|
||||||
"docker.save": runDockerSave,
|
"docker.save": runDockerSave,
|
||||||
"docker.stats": runDockerStats,
|
"docker.stats": runDockerStats,
|
||||||
"docker.stop": runDockerStop,
|
"docker.stop": runDockerStop,
|
||||||
"file.empty": runFileEmpty,
|
"file.empty": runFileEmpty,
|
||||||
"file.findbig": runFileFindBig,
|
"file.findbig": runFileFindBig,
|
||||||
"file.findname": runFileFindName,
|
"file.findname": runFileFindName,
|
||||||
"file.findrecent": runFileFindRecent,
|
"file.findrecent": runFileFindRecent,
|
||||||
"fw.allow": runFwAllow,
|
"fw.allow": runFwAllow,
|
||||||
"fw.deny": runFwDeny,
|
"fw.deny": runFwDeny,
|
||||||
"fw.status": runFwStatus,
|
"fw.status": runFwStatus,
|
||||||
"git.retime": runGitRetime,
|
"git.retime": runGitRetime,
|
||||||
"git.retime.preview": runGitRetimePreview,
|
"git.retime.preview": runGitRetimePreview,
|
||||||
"java.run": runJavaRun,
|
"git.time": runGitTime,
|
||||||
"java.run.preview": runJavaPreview,
|
"java.run": runJavaRun,
|
||||||
"log.last": runLogLast,
|
"java.run.preview": runJavaPreview,
|
||||||
"log.list": runLogList,
|
"log.last": runLogLast,
|
||||||
"log.search": runLogSearch,
|
"log.list": runLogList,
|
||||||
"net.conn": runNetConn,
|
"log.search": runLogSearch,
|
||||||
"net.curl": runNetCurl,
|
"net.conn": runNetConn,
|
||||||
"net.dns": runNetDNS,
|
"net.curl": runNetCurl,
|
||||||
"net.iface": runNetIface,
|
"net.dns": runNetDNS,
|
||||||
"net.ping": runNetPing,
|
"net.http": runNetHTTP,
|
||||||
"net.pids": runNetPIDs,
|
"net.iface": runNetIface,
|
||||||
"net.ports": runNetPorts,
|
"net.ping": runNetPing,
|
||||||
"net.speed": runNetSpeed,
|
"net.pids": runNetPIDs,
|
||||||
"net.trace": runNetTrace,
|
"net.ports": runNetPorts,
|
||||||
"pkg.list": runPkgList,
|
"net.speed": runNetSpeed,
|
||||||
"proc.killname": runProcKillName,
|
"net.trace": runNetTrace,
|
||||||
"proc.list": runProcList,
|
"pkg.info": runPkgInfo,
|
||||||
"proc.nohup": runNohup,
|
"pkg.install": runPkgInstall,
|
||||||
"proc.topcpu": runProcTopCPU,
|
"pkg.list": runPkgList,
|
||||||
"proc.topmem": runProcTopMem,
|
"pkg.remove": runPkgRemove,
|
||||||
"proc.tree": runProcTree,
|
"pkg.search": runPkgSearch,
|
||||||
"proc.wait": runProcWait,
|
"proc.killname": runProcKillName,
|
||||||
"svc.disable": runSvcDisable,
|
"proc.list": runProcList,
|
||||||
"svc.enable": runSvcEnable,
|
"proc.nohup": runNohup,
|
||||||
"svc.list": runSvcList,
|
"proc.topcpu": runProcTopCPU,
|
||||||
"svc.logs": runSvcLogs,
|
"proc.topmem": runProcTopMem,
|
||||||
"svc.status": runSvcStatus,
|
"proc.tree": runProcTree,
|
||||||
"sysinfo": runSysInfo,
|
"proc.wait": runProcWait,
|
||||||
"user.grant": runUserGrant,
|
"svc.disable": runSvcDisable,
|
||||||
"user.info": runUserInfo,
|
"svc.enable": runSvcEnable,
|
||||||
"user.list": runUserList,
|
"svc.list": runSvcList,
|
||||||
"user.revoke": runUserRevoke,
|
"svc.logs": runSvcLogs,
|
||||||
|
"svc.status": runSvcStatus,
|
||||||
|
"sysinfo": runSysInfo,
|
||||||
|
"user.grant": runUserGrant,
|
||||||
|
"user.info": runUserInfo,
|
||||||
|
"user.list": runUserList,
|
||||||
|
"user.revoke": runUserRevoke,
|
||||||
}
|
}
|
||||||
|
|
||||||
// runDiagnose bridges the registry builtin to the diagnose package.
|
// runDiagnose bridges the registry builtin to the diagnose package.
|
||||||
|
|||||||
@@ -0,0 +1,288 @@
|
|||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gitea.odboy.cn/odboy/kenaito-kopsc/internal/platform"
|
||||||
|
)
|
||||||
|
|
||||||
|
// runPkgInstall implements `kopsc pkg install <package>`: install a package.
|
||||||
|
func runPkgInstall(ctx context.Context, req BuiltinRequest) error {
|
||||||
|
opts, err := parsePkgManageArgs(req.Args, "install")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(opts.packages) == 0 {
|
||||||
|
return fmt.Errorf("缺少包名;用法: kopsc pkg install <包名> [包名...]")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "linux":
|
||||||
|
return pkgInstallLinux(ctx, req, opts.packages)
|
||||||
|
case "darwin":
|
||||||
|
args := append([]string{"install"}, opts.packages...)
|
||||||
|
cmd := exec.CommandContext(ctx, "brew", args...)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
case "windows":
|
||||||
|
args := append([]string{"install"}, opts.packages...)
|
||||||
|
cmd := exec.CommandContext(ctx, "winget", args...)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("不支持的操作系统: %s", runtime.GOOS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pkgInstallLinux(ctx context.Context, req BuiltinRequest, packages []string) error {
|
||||||
|
info := platform.System().Detect()
|
||||||
|
|
||||||
|
// Debian/Ubuntu: apt-get install
|
||||||
|
if distroLikeContains(info, "debian", "ubuntu") {
|
||||||
|
args := append([]string{"install"}, packages...)
|
||||||
|
cmd := exec.CommandContext(ctx, "apt-get", args...)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CentOS/RHEL/Fedora: dnf install
|
||||||
|
if distroLikeContains(info, "rhel", "fedora") || info.Distro == "centos" {
|
||||||
|
args := append([]string{"install"}, packages...)
|
||||||
|
cmd := exec.CommandContext(ctx, "dnf", args...)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alpine: apk add
|
||||||
|
if info.Distro == "alpine" {
|
||||||
|
args := append([]string{"add"}, packages...)
|
||||||
|
cmd := exec.CommandContext(ctx, "apk", args...)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("不支持的 Linux 发行版: %s", info.Distro)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runPkgRemove implements `kopsc pkg remove <package>`: remove a package.
|
||||||
|
func runPkgRemove(ctx context.Context, req BuiltinRequest) error {
|
||||||
|
opts, err := parsePkgManageArgs(req.Args, "remove")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(opts.packages) == 0 {
|
||||||
|
return fmt.Errorf("缺少包名;用法: kopsc pkg remove <包名> [包名...]")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "linux":
|
||||||
|
return pkgRemoveLinux(ctx, req, opts.packages)
|
||||||
|
case "darwin":
|
||||||
|
args := append([]string{"uninstall"}, opts.packages...)
|
||||||
|
cmd := exec.CommandContext(ctx, "brew", args...)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
case "windows":
|
||||||
|
args := append([]string{"uninstall"}, opts.packages...)
|
||||||
|
cmd := exec.CommandContext(ctx, "winget", args...)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("不支持的操作系统: %s", runtime.GOOS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pkgRemoveLinux(ctx context.Context, req BuiltinRequest, packages []string) error {
|
||||||
|
info := platform.System().Detect()
|
||||||
|
|
||||||
|
// Debian/Ubuntu: apt-get remove
|
||||||
|
if distroLikeContains(info, "debian", "ubuntu") {
|
||||||
|
args := append([]string{"remove"}, packages...)
|
||||||
|
cmd := exec.CommandContext(ctx, "apt-get", args...)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CentOS/RHEL/Fedora: dnf remove
|
||||||
|
if distroLikeContains(info, "rhel", "fedora") || info.Distro == "centos" {
|
||||||
|
args := append([]string{"remove"}, packages...)
|
||||||
|
cmd := exec.CommandContext(ctx, "dnf", args...)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alpine: apk del
|
||||||
|
if info.Distro == "alpine" {
|
||||||
|
args := append([]string{"del"}, packages...)
|
||||||
|
cmd := exec.CommandContext(ctx, "apk", args...)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("不支持的 Linux 发行版: %s", info.Distro)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runPkgSearch implements `kopsc pkg search <keyword>`: search for packages.
|
||||||
|
func runPkgSearch(ctx context.Context, req BuiltinRequest) error {
|
||||||
|
opts, err := parsePkgManageArgs(req.Args, "search")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(opts.packages) == 0 {
|
||||||
|
return fmt.Errorf("缺少搜索关键字;用法: kopsc pkg search <关键字>")
|
||||||
|
}
|
||||||
|
keyword := opts.packages[0]
|
||||||
|
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "linux":
|
||||||
|
return pkgSearchLinux(ctx, req, keyword)
|
||||||
|
case "darwin":
|
||||||
|
cmd := exec.CommandContext(ctx, "brew", "search", keyword)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
case "windows":
|
||||||
|
cmd := exec.CommandContext(ctx, "winget", "search", keyword)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("不支持的操作系统: %s", runtime.GOOS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pkgSearchLinux(ctx context.Context, req BuiltinRequest, keyword string) error {
|
||||||
|
info := platform.System().Detect()
|
||||||
|
|
||||||
|
// Debian/Ubuntu: apt-cache search
|
||||||
|
if distroLikeContains(info, "debian", "ubuntu") {
|
||||||
|
cmd := exec.CommandContext(ctx, "apt-cache", "search", keyword)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CentOS/RHEL/Fedora: dnf search
|
||||||
|
if distroLikeContains(info, "rhel", "fedora") || info.Distro == "centos" {
|
||||||
|
cmd := exec.CommandContext(ctx, "dnf", "search", keyword)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alpine: apk search
|
||||||
|
if info.Distro == "alpine" {
|
||||||
|
cmd := exec.CommandContext(ctx, "apk", "search", keyword)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("不支持的 Linux 发行版: %s", info.Distro)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runPkgInfo implements `kopsc pkg info <package>`: show package details.
|
||||||
|
func runPkgInfo(ctx context.Context, req BuiltinRequest) error {
|
||||||
|
opts, err := parsePkgManageArgs(req.Args, "info")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(opts.packages) == 0 {
|
||||||
|
return fmt.Errorf("缺少包名;用法: kopsc pkg info <包名>")
|
||||||
|
}
|
||||||
|
pkg := opts.packages[0]
|
||||||
|
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "linux":
|
||||||
|
return pkgInfoLinux(ctx, req, pkg)
|
||||||
|
case "darwin":
|
||||||
|
cmd := exec.CommandContext(ctx, "brew", "info", pkg)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
case "windows":
|
||||||
|
cmd := exec.CommandContext(ctx, "winget", "show", pkg)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("不支持的操作系统: %s", runtime.GOOS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pkgInfoLinux(ctx context.Context, req BuiltinRequest, pkg string) error {
|
||||||
|
info := platform.System().Detect()
|
||||||
|
|
||||||
|
// Debian/Ubuntu: apt show (fallback to dpkg -s)
|
||||||
|
if distroLikeContains(info, "debian", "ubuntu") {
|
||||||
|
cmd := exec.CommandContext(ctx, "apt", "show", pkg)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
// Fallback to dpkg -s
|
||||||
|
cmd = exec.CommandContext(ctx, "dpkg", "-s", pkg)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CentOS/RHEL/Fedora: rpm -qi
|
||||||
|
if distroLikeContains(info, "rhel", "fedora") || info.Distro == "centos" {
|
||||||
|
cmd := exec.CommandContext(ctx, "rpm", "-qi", pkg)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alpine: apk info
|
||||||
|
if info.Distro == "alpine" {
|
||||||
|
cmd := exec.CommandContext(ctx, "apk", "info", "-a", pkg)
|
||||||
|
cmd.Stdout = req.Stdout
|
||||||
|
cmd.Stderr = req.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("不支持的 Linux 发行版: %s", info.Distro)
|
||||||
|
}
|
||||||
|
|
||||||
|
type pkgManageOptions struct {
|
||||||
|
packages []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePkgManageArgs(args []string, action string) (pkgManageOptions, error) {
|
||||||
|
var opts pkgManageOptions
|
||||||
|
var positional []string
|
||||||
|
|
||||||
|
for i := 0; i < len(args); i++ {
|
||||||
|
arg := args[i]
|
||||||
|
switch {
|
||||||
|
case arg == "--":
|
||||||
|
positional = append(positional, args[i+1:]...)
|
||||||
|
i = len(args)
|
||||||
|
case strings.HasPrefix(arg, "-") && arg != "-":
|
||||||
|
// Ignore unknown flags, pass through
|
||||||
|
positional = append(positional, arg)
|
||||||
|
default:
|
||||||
|
positional = append(positional, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.packages = positional
|
||||||
|
return opts, nil
|
||||||
|
}
|
||||||
@@ -513,6 +513,35 @@ func Builtin() *Registry {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ID: "git.time",
|
||||||
|
Group: "git",
|
||||||
|
Summary: "修改最后一次提交的时间并强推",
|
||||||
|
Action: "把本地最后一次提交的作者时间和提交时间改为用户指定的时间,然后强制推送到远程。" +
|
||||||
|
"时间格式必须是 yyyy-MM-dd HH:mm:ss(如 2026-11-05 22:30:00)。" +
|
||||||
|
"同时修改 Author Date 和 Committer Date,确保远程页面显示正确。" +
|
||||||
|
"默认只打印预览并以退出码 2 结束,必须显式加 --yes 才会改写并推送",
|
||||||
|
Usage: "<时间> [选项]",
|
||||||
|
MinArgs: 1,
|
||||||
|
RequiresYes: true,
|
||||||
|
Variants: []Variant{
|
||||||
|
{
|
||||||
|
Match: Match{OS: "linux"},
|
||||||
|
Builtin: "git.time",
|
||||||
|
Note: "Linux 内置实现(调用本机 git)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Match: Match{OS: "darwin"},
|
||||||
|
Builtin: "git.time",
|
||||||
|
Note: "macOS 内置实现(调用本机 git)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Match: Match{OS: "windows"},
|
||||||
|
Builtin: "git.time",
|
||||||
|
Note: "Windows 内置实现(调用本机 git)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ID: "pkg.list",
|
ID: "pkg.list",
|
||||||
Group: "pkg",
|
Group: "pkg",
|
||||||
@@ -937,6 +966,107 @@ func Builtin() *Registry {
|
|||||||
Usage: "[用户名]",
|
Usage: "[用户名]",
|
||||||
Variants: threePlatformBuiltins("user.info"),
|
Variants: threePlatformBuiltins("user.info"),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ----- pkg additions -----
|
||||||
|
{
|
||||||
|
ID: "pkg.install",
|
||||||
|
Group: "pkg",
|
||||||
|
Summary: "安装软件包",
|
||||||
|
Action: "安装指定的软件包。" +
|
||||||
|
"Linux: apt-get install(Debian/Ubuntu)/ dnf install(CentOS/RHEL)/ apk add(Alpine);" +
|
||||||
|
"macOS: brew install;Windows: winget install",
|
||||||
|
Usage: "<包名> [包名...]",
|
||||||
|
MinArgs: 1,
|
||||||
|
RequiresYes: true,
|
||||||
|
Variants: threePlatformBuiltins("pkg.install"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "pkg.remove",
|
||||||
|
Group: "pkg",
|
||||||
|
Summary: "卸载软件包",
|
||||||
|
Action: "卸载指定的软件包。" +
|
||||||
|
"Linux: apt-get remove(Debian/Ubuntu)/ dnf remove(CentOS/RHEL)/ apk del(Alpine);" +
|
||||||
|
"macOS: brew uninstall;Windows: winget uninstall",
|
||||||
|
Usage: "<包名> [包名...]",
|
||||||
|
MinArgs: 1,
|
||||||
|
RequiresYes: true,
|
||||||
|
Variants: threePlatformBuiltins("pkg.remove"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "pkg.search",
|
||||||
|
Group: "pkg",
|
||||||
|
Summary: "搜索软件包",
|
||||||
|
Action: "在软件源中搜索匹配关键字的软件包。" +
|
||||||
|
"Linux: apt-cache search(Debian/Ubuntu)/ dnf search(CentOS/RHEL)/ apk search(Alpine);" +
|
||||||
|
"macOS: brew search;Windows: winget search",
|
||||||
|
Usage: "<关键字>",
|
||||||
|
MinArgs: 1,
|
||||||
|
Variants: threePlatformBuiltins("pkg.search"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "pkg.info",
|
||||||
|
Group: "pkg",
|
||||||
|
Summary: "查看软件包详情",
|
||||||
|
Action: "显示指定软件包的详细信息(版本、描述、依赖等)。" +
|
||||||
|
"Linux: apt show / dpkg -s(Debian/Ubuntu)/ rpm -qi(CentOS/RHEL)/ apk info(Alpine);" +
|
||||||
|
"macOS: brew info;Windows: winget show",
|
||||||
|
Usage: "<包名>",
|
||||||
|
MinArgs: 1,
|
||||||
|
Variants: threePlatformBuiltins("pkg.info"),
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----- archive additions -----
|
||||||
|
{
|
||||||
|
ID: "archive.create",
|
||||||
|
Group: "archive",
|
||||||
|
Summary: "创建压缩文件",
|
||||||
|
Action: "将文件或目录压缩为归档文件。支持 tar.gz / tgz / tar / zip 格式,默认 tar.gz。" +
|
||||||
|
"--format 可指定格式,-o 可指定输出文件名",
|
||||||
|
Usage: "<路径> [--format 格式] [-o 输出文件]",
|
||||||
|
MinArgs: 1,
|
||||||
|
Variants: threePlatformBuiltins("archive.create"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "archive.list",
|
||||||
|
Group: "archive",
|
||||||
|
Summary: "列出压缩文件内容(不解压)",
|
||||||
|
Action: "查看压缩文件的内容列表,显示权限、大小、修改时间和文件名。" +
|
||||||
|
"支持 tar.gz / tgz / tar / zip 格式",
|
||||||
|
Usage: "<文件>",
|
||||||
|
MinArgs: 1,
|
||||||
|
Variants: threePlatformBuiltins("archive.list"),
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----- net additions (http) -----
|
||||||
|
{
|
||||||
|
ID: "net.http",
|
||||||
|
Group: "net",
|
||||||
|
Summary: "启动简单的 HTTP 文件服务器",
|
||||||
|
Action: "在当前目录或指定目录启动 HTTP 文件服务器,方便局域网内共享文件。" +
|
||||||
|
"默认端口 8000,-p 可指定端口;--upload 启用上传功能",
|
||||||
|
Usage: "[目录] [-p 端口] [--upload]",
|
||||||
|
Variants: threePlatformBuiltins("net.http"),
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----- disk additions -----
|
||||||
|
{
|
||||||
|
ID: "disk.inode",
|
||||||
|
Group: "disk",
|
||||||
|
Summary: "查看 inode 使用情况",
|
||||||
|
Action: "显示文件系统的 inode 使用统计。inode 耗尽会导致无法创建新文件,即使磁盘空间充足。" +
|
||||||
|
"Linux/macOS: df -i;Windows 不支持(NTFS 无 inode 概念)",
|
||||||
|
Usage: "[路径]",
|
||||||
|
Variants: threePlatformBuiltins("disk.inode"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "disk.check",
|
||||||
|
Group: "disk",
|
||||||
|
Summary: "一键磁盘健康检查",
|
||||||
|
Action: "综合检查磁盘健康状况:空间使用率、inode 使用率、大文件 TOP 5、大目录 TOP 5,并给出清理建议。" +
|
||||||
|
"一条命令完成磁盘排障",
|
||||||
|
Usage: "[路径] [--all]",
|
||||||
|
Variants: threePlatformBuiltins("disk.check"),
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func TestBuiltinIDsStable(t *testing.T) {
|
|||||||
"docker.prunebuilder", "docker.prunevolumes", "docker.clearall",
|
"docker.prunebuilder", "docker.prunevolumes", "docker.clearall",
|
||||||
"docker.compose.up", "docker.compose.down", "docker.compose.ps", "docker.compose.logs",
|
"docker.compose.up", "docker.compose.down", "docker.compose.ps", "docker.compose.logs",
|
||||||
"diagnose",
|
"diagnose",
|
||||||
"disk.usage", "net.pids", "net.ports", "proc.nohup", "proc.wait", "java.run", "git.retime", "pkg.list", "pkg.refreshindex",
|
"disk.usage", "net.pids", "net.ports", "proc.nohup", "proc.wait", "java.run", "git.retime", "git.time", "pkg.list", "pkg.refreshindex",
|
||||||
"user.list", "user.grant", "user.revoke",
|
"user.list", "user.grant", "user.revoke",
|
||||||
"file.findbig", "file.findrecent", "file.findname",
|
"file.findbig", "file.findrecent", "file.findname",
|
||||||
"svc.list", "svc.status", "svc.enable", "svc.disable", "svc.logs",
|
"svc.list", "svc.status", "svc.enable", "svc.disable", "svc.logs",
|
||||||
@@ -39,6 +39,10 @@ func TestBuiltinIDsStable(t *testing.T) {
|
|||||||
"proc.tree",
|
"proc.tree",
|
||||||
"file.empty",
|
"file.empty",
|
||||||
"user.info",
|
"user.info",
|
||||||
|
"pkg.install", "pkg.remove", "pkg.search", "pkg.info",
|
||||||
|
"archive.create", "archive.list",
|
||||||
|
"net.http",
|
||||||
|
"disk.inode", "disk.check",
|
||||||
}
|
}
|
||||||
if got := reg.IDs(); !reflect.DeepEqual(got, want) {
|
if got := reg.IDs(); !reflect.DeepEqual(got, want) {
|
||||||
t.Fatalf("IDs() =\n%v\nwant\n%v", got, want)
|
t.Fatalf("IDs() =\n%v\nwant\n%v", got, want)
|
||||||
|
|||||||
Reference in New Issue
Block a user