Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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. |
Expand Down
4 changes: 3 additions & 1 deletion docs/README_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` 打印一次性配对码;一个码只能配对一个扩展,每个扩展各运行一次。
Expand Down Expand Up @@ -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` | 标签页、窗口和标签组。 |
Expand Down
6 changes: 5 additions & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
39 changes: 38 additions & 1 deletion docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <label>]` | Serve ScriptCat and sctl Browser tools over stdio MCP. |
| `sctl status` | Show daemon and extension connection status. |
Expand Down Expand Up @@ -127,6 +128,42 @@ Chrome's retention limit), and note on stderr when more remain.

`--since` and `--until` accept an RFC 3339 time or a duration ago such as `7d`, `12h`, or `30m`.

## Background service: `sctl service`

`sctl service install` registers `sctl serve` with the current user's service manager, so the daemon starts at login;
launchd and systemd also restart it after a crash. It needs no administrator rights and installs one service per user:

| Platform | Service manager | Definition |
|---|---|---|
| macOS | launchd LaunchAgent | `~/Library/LaunchAgents/com.scriptscat.sctl.plist` |
| Linux | systemd user instance | `~/.config/systemd/user/sctl.service` (under `$XDG_CONFIG_HOME` when set); runs while you are logged in |
| Windows | Task Scheduler, triggered at your logon | task `\sctl`; a console window flashes briefly when it starts. Task Scheduler's retry is not known to cover a daemon that crashes after starting: run `sctl service start` |

- **`install`** writes the definition, enables it at login, and starts it. The service runs the `sctl` binary you ran
`install` with (the one on `PATH` when it is the same file, so a Homebrew upgrade keeps working) together with the
`--data-dir` (as an absolute path, also when it comes from `SCTL_DATA_DIR`), `--listen-address`, and a non-default
`--log-level` in effect for that command, so pass the same ones you use with the other commands. Running it again
rewrites the definition and restarts the service, which is how to pick up a new binary, path, or flag. It refuses
when the service is not installed yet but a daemon answers at the address, even one using another data directory:
stop that daemon first, or the service keeps failing on the busy port.
- **`start`** and **`restart`** start the installed service; **`stop`** stops it until you start it again or log in
next. All three exit 3 when the service is not installed. `start` does nothing when a daemon is already reachable
at the address, whether it is the service or one you started yourself.
- **`install`**, **`start`**, and **`restart`** wait up to 10 seconds for the newly started daemon to become reachable
(recognized by the fresh control token it writes, so another daemon holding the port does not count) and exit 3,
naming the log directory, when it does not.
- **`uninstall`** stops the service and removes the definition; it succeeds when nothing is installed.
- **`status`** reports whether the service is installed, where its definition is, and whether a daemon is reachable
at the address. It exits 0 either way; add `-o json` for scripts.

On macOS, a process started by launchd cannot read `~/Documents`, `~/Desktop`, or `~/Downloads` without a privacy
permission and hangs there instead of starting, so keep the `sctl` binary and the data directory outside those folders
(the install script's `~/.local/bin` and Homebrew's prefix are fine).

Under the service, the daemon writes its log only to the files in `<data dir>/logs/`; the service manager receives
nothing but crash output (macOS: `<data dir>/logs/service.stderr.log`, Linux: the user journal). Use either the
service or `brew services`, not both: they run the same daemon on the same port.

## Page automation: `sctl page`

### Target tab and debugger attachment
Expand Down
4 changes: 2 additions & 2 deletions docs/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ sctl serve
```

`sctl serve` is the only process that owns the WebSocket connection to ScriptCat. CLI commands and `sctl mcp`
never start it automatically. For long-running use, configure the operating system's user service manager to
run this exact command; service-manager-specific installation is outside this guide.
never start it automatically. For long-running use, `sctl service install` runs it in the background as a user
service that starts at login; see [`cli.md`](./cli.md#background-service-sctl-service).

Before enrollment, this command should reach the daemon and report that no extension is connected:

Expand Down
Loading
Loading