From d46dc90f2e97c201998fcd47290901bcd4cbbf6f Mon Sep 17 00:00:00 2001 From: LM Date: Fri, 23 Oct 2026 14:21:52 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(service):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=20sctl=20service=EF=BC=8C=E6=8A=8A=20serve=20=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E4=B8=BA=E7=94=A8=E6=88=B7=E7=BA=A7=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sctl serve 只能前台运行,文档让用户自己配置服务管理器却不提供工具,手写定义还容易 和 CLI 的数据目录、监听地址对不上。 - sctl service install|uninstall|start|stop|restart|status:macOS launchd LaunchAgent、 Linux systemd --user、Windows 登录触发的计划任务,不需要管理员权限 - install 固化当前生效的 --data-dir(绝对路径)、--listen-address、非默认 --log-level; 二进制优先用 PATH 里的同一文件,Homebrew 升级后不失效 - 隐藏参数 serve --service:日志只写文件;Windows 上脱离独占的控制台 - 等 daemon 时以控制令牌变化识别新进程;安装前和 start 前遇到已在运行的 daemon 时不启动服务 - schtasks / launchctl 只把非零退出当作“不存在”,命令本身无法运行时如实报错 - spec:docs/specs/2026-10-23-service.md --- README.md | 4 +- docs/README_zh-CN.md | 4 +- docs/architecture.md | 6 +- docs/cli.md | 39 ++- docs/mcp.md | 4 +- docs/specs/2026-10-23-service.md | 114 +++++++ internal/cli/cli.go | 9 +- internal/cli/console_other.go | 6 + internal/cli/console_windows.go | 25 ++ internal/cli/serve.go | 15 +- internal/cli/service.go | 265 ++++++++++++++++ internal/cli/service_test.go | 274 ++++++++++++++++ internal/client/control/client.go | 24 +- internal/client/service/launchd.go | 188 +++++++++++ internal/client/service/schtasks.go | 203 ++++++++++++ internal/client/service/service.go | 161 ++++++++++ internal/client/service/service_test.go | 400 ++++++++++++++++++++++++ internal/client/service/systemd.go | 100 ++++++ internal/pkg/logging/logging.go | 18 +- internal/pkg/logging/logging_test.go | 69 ++++ skills/sctl/SKILL.md | 4 +- 21 files changed, 1911 insertions(+), 21 deletions(-) create mode 100644 docs/specs/2026-10-23-service.md create mode 100644 internal/cli/console_other.go create mode 100644 internal/cli/console_windows.go create mode 100644 internal/cli/service.go create mode 100644 internal/cli/service_test.go create mode 100644 internal/client/service/launchd.go create mode 100644 internal/client/service/schtasks.go create mode 100644 internal/client/service/service.go create mode 100644 internal/client/service/service_test.go create mode 100644 internal/client/service/systemd.go create mode 100644 internal/pkg/logging/logging_test.go diff --git a/README.md b/README.md index 76910e0..1cb76b8 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,8 @@ The install script verifies the release archive's sha256 and installs `sctl` int 1. **Start the daemon** and leave it running: ```bash - sctl serve + sctl serve # in a terminal, or + sctl service install # in the background, starting at login ``` 2. **Pair the extensions.** `sctl connect` prints a one-time code; each code pairs one extension, so run it once @@ -119,6 +120,7 @@ browser's logins; see [`docs/cli.md`](./docs/cli.md#endpoint-for-playwright-and- | Group | What it does | |---|---| | `sctl serve` / `connect` / `status` / `mcp` | Run the daemon, pair an extension, show connection status, serve MCP over stdio. | +| `sctl service` | Run the daemon in the background as a user service (launchd, systemd, Task Scheduler). | | `sctl get` / `grep` / `install` / `edit` / `enable` / `disable` / `delete` | Manage ScriptCat userscripts. | | `sctl browsers` | List paired sctl Browser instances, or forget one. | | `sctl tabs` / `windows` / `groups` | Tabs, windows, and tab groups. | diff --git a/docs/README_zh-CN.md b/docs/README_zh-CN.md index 5ccdd8d..8dad2b8 100644 --- a/docs/README_zh-CN.md +++ b/docs/README_zh-CN.md @@ -58,7 +58,8 @@ irm https://raw.githubusercontent.com/scriptscat/sctl/main/scripts/install.ps1 | 1. **启动 daemon** 并保持运行: ```bash - sctl serve + sctl serve # 在终端里运行,或者 + sctl service install # 在后台运行,登录后自动启动 ``` 2. **配对扩展。** `sctl connect` 打印一次性配对码;一个码只能配对一个扩展,每个扩展各运行一次。 @@ -109,6 +110,7 @@ ln -s "$PWD/skills/sctl" ~/.claude/skills/sctl | 命令组 | 用途 | |---|---| | `sctl serve` / `connect` / `status` / `mcp` | 运行 daemon、配对扩展、查看连接状态、通过 stdio 提供 MCP。 | +| `sctl service` | 以用户服务的形式在后台运行 daemon(launchd、systemd、任务计划程序)。 | | `sctl get` / `grep` / `install` / `edit` / `enable` / `disable` / `delete` | 管理 ScriptCat 用户脚本。 | | `sctl browsers` | 列出已配对的 sctl Browser 实例,或忘记其中一个。 | | `sctl tabs` / `windows` / `groups` | 标签页、窗口和标签组。 | diff --git a/docs/architecture.md b/docs/architecture.md index ce616b7..ca5a564 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -18,7 +18,9 @@ CLI verbs (sctl get / edit / install / browsers / tabs / windows / page / debug `/control/*` HTTP/JSON control API on the daemon's listener — same port as the extensions' WS surface, separate path, authenticated with the control token described in [threat-model.md](./threat-model.md#4-the-control-channel-internal-local-connection-in-detail). Frontends never start `serve`: run it -explicitly in the foreground or let an external system service manager own its lifecycle. +explicitly in the foreground or let the user's system service manager own its lifecycle — `sctl service` +(`internal/client/service`) writes that service definition and drives the manager's own commands, without +talking to the daemon. The daemon accepts two kinds of extension connection at once: exactly one active ScriptCat connection (a new one replaces the previous one), and any number of paired `sctl Browser` instances connecting simultaneously, @@ -140,6 +142,7 @@ cmd/sctl/main.go # cobra entry point (unwraps ExitError → os.Exit) internal/cli/ # subcommand definitions; spans both sides, hence top level cli.go # root command, global flags (-o/--output, --log-level), output helpers serve.go # bootstraps the cago app and mounts the daemon Component + service.go # sctl service install|uninstall|start|stop|restart|status mcp.go # sctl mcp (serves all tools; --name is an audit label) connect.go status.go version.go get.go grep.go # read verbs @@ -175,6 +178,7 @@ internal/daemon/ # ── sctl serve side ── internal/client/ # ── sctl mcp / CLI verb side ── control/ # control API client, shared DTOs, control token mcpserver/ # go-sdk stdio MCP server: all tools (flat trust), progress while waiting + service/ # user service definitions (launchd, systemd --user, Task Scheduler) and their commands internal/pkg/ # ── shared by both sides ── protocol/ # protocol.json authority plus generated language and business-schema artifacts diff --git a/docs/cli.md b/docs/cli.md index 98089f4..d773f32 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -11,7 +11,8 @@ command line can use the skill in [`../skills/sctl/`](../skills/sctl/SKILL.md). | Command | Purpose | |---|---| -| `sctl serve` | Run the local bridge daemon. | +| `sctl serve` | Run the local bridge daemon in the foreground. | +| `sctl service install\|uninstall\|start\|stop\|restart\|status` | Run `sctl serve` in the background as a user service; see [Background service](#background-service-sctl-service). | | `sctl connect` | Open a one-time enrollment window for ScriptCat or sctl Browser. | | `sctl mcp [--name