✨ 新增 sctl 本地守护进程与 MCP 桥接 - #3
Open
CodFrm wants to merge 14 commits into
Open
Conversation
Replace per-client pairing/scope/revocation with a flat model: enrollment establishes the long-term key K once, and the CLI and every MCP agent inherit it over the enrolled channel. clientId becomes an audit-only label. - remove client store, cached MCP identity, per-client pairing/sync/revoke - rename `sctl pair` -> `sctl connect`; control /pair-ext -> /enroll - `sctl mcp` drops the pair subcommand; --name is now an audit label - mcpserver exposes all tools (no scope filter) - add an Origin whitelist (chrome-extension:// etc.) as a cheap web pre-filter; the handshake stays the real gate - source reads are no longer CLI-exempt (source-read policy gates both) - rewrite protocol.md / threat-model.md / architecture.md / README / AGENTS
…pair/client
Mirror the extension-side protocol change byte-for-byte: envelopeTypes becomes {session, bridge} and the flat-trust-dead pair.*/client.* types are removed. protocol.go's EnvelopeTypes field becomes a nested struct so Load() parses the new shape. protocolVersion unchanged (no wire-format change).
The SDK gains full support for protocol revision 2026-07-28: a stateless model with per-request _meta, server/discover replacing the initialize handshake, MRTR, subscriptions/listen, and cacheable list results. No source change is required. sctl serves MCP over stdio, so the v1.7.0 constraint that streamable HTTP only accepts 2026-07-28 when StreamableHTTPOptions.Stateless is set does not apply, and no API used by internal/client/mcpserver broke. Verified by driving the real binary over stdio with raw JSON-RPC, since the package tests put go-sdk on both ends and therefore cannot observe which revision is actually served: server/discover advertises 2026-07-28 and tools/list succeeds with no handshake, while a legacy initialize client still negotiates 2025-06-18 and gets the same tools.
…l.json Add scripts:edit:request scope and the scripts.edit.request (write, approval) / scripts.source.grep (reuses scripts:source:read, disclosure) actions to the bridge protocol mirror. Declarations only; no handling wired up yet.
…output) Fold `scripts list/info/source` into a single kubectl-style `get` command and make the leading `scripts|script|sc` resource word optional on get/delete/enable/disable. Replace the global --json bool with -o/--output (table default / json / source), remove `rm` (del is now delete's only alias), and wire --lines through to scripts.source.get for get <uuid> -o source.
… in RunE
The verb commands validated arg count through stripResourceWord but read the
raw args[0], so `sctl delete sc u1` dispatched uuid="sc" — deleting the wrong
script (or NOT_FOUND) instead of u1.
Arg-count errors now return ExitError{exitError}: a bare error is folded into
exit 1 by main, and 1 is reserved for "user rejected in the browser", so a
caller could not tell a typo from a rejection.
printScriptSource decodes code into a pointer so an empty window (--lines over
blank lines) prints nothing rather than falling back to dumping the result
envelope into a redirected .user.js.
The CLI tests grew real guards: the leaves that assert a rejection now stub the
daemon and pin the dispatched action and uuid, so they no longer pass merely
because an unreachable daemon also exits 3, and parseLinesFlag is covered
directly.
spawnServeProcess exec'd os.Executable() with "serve", assuming the running program is sctl. It is not whenever the client packages are linked into another binary. Under `go test` os.Executable() is the test binary, so every leaf that reached ensureDaemon without a live daemon re-ran the whole test package detached — and each of those re-runs spawned again. Running `go test ./...` grew to thousands of processes and exhausted the system's fork limit. The children also inherited SCTL_BRIDGE_ADDR, so they issued /control/call requests against the parent's stub server and overwrote the captured request other tests assert on, which is why TestGrepInputMapping and TestGrepOutput failed intermittently. With the guard in place internal/cli drops from 81s (failing) to 0.4s. `go run ./cmd/sctl` builds a temporary binary that is still named sctl, so auto-launch during development is unaffected.
`sctl edit <uuid>` requests a content-anchored edit: the edits travel alone, so the source is never read first and never uploaded. Edits come either as a JSON array via -f (- reads stdin) or as repeated --replace/--with pairs, whose values accept @path to read from a file and @@ to escape a literal leading @ — user script metadata lines start with @, so the escape is not hypothetical. --replace uses StringArray rather than StringSlice: the latter splits on commas and would tear an anchor like "f(a, b)" into two edits. `sctl grep <uuid> <query>` searches one script's source. The query is a literal substring unless -E is given; -i, -C and -m map to ignoreCase, contextLines and maxMatches, and are only sent when actually given so the defaults live on the extension side alone. Output follows grep -n, with truncation and skipped-line notices on stderr so stdout stays pipeable. No match is not an error: exit stays 0, because exit 1 already means the user rejected the request. The MCP side registers scripts_edit_request and scripts_source_grep and gives scripts_source_get its line-window fields, closing the tool-count assertion that went red when protocol.json declared the two actions. The grep tool description spells out that the default mode matches literally, since a model will otherwise assume the query is a pattern and be puzzled by `.*` finding nothing.
Command help and flag descriptions, stdout/stderr output, error and log messages, and the MCP tool descriptions and input schemas are now English. Code comments and Convey test titles stay Chinese; the test assertions on the affected CLI output are updated to match.
The command table still advertised `sctl scripts list / info / source` and a top-level `rm`, and the global-flag line still documented `--json` — all of them removed when the command surface went resource-oriented. docs/architecture.md carried the same stale diagram plus an internal/cli listing naming a scripts.go that no longer exists, and docs/verification.md's `./sctl scripts list` was an executable verification step that now hits "unknown command" instead of the exit 3 it claims to demonstrate. protocol.md gains the two new actions (scripts.source.grep, scripts.edit.request), the line window on scripts.source.get, and the constraints a client actually hits: literal-by-default grep, overlap-counted anchor uniqueness, the 100-edit batch cap. Also mirrors that cap into the MCP edit schema as maxItems so an agent learns the bound locally instead of from a rejected request. README is translated wholesale rather than left half-Chinese: its command table and flag section had to be rewritten anyway, and docs/ plus AGENTS.md are already English. Two stale facts found while checking the rest: `sctl status` reports no per-client pairing under flat trust, and the extension's audit component is external-access, not local-access.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
说明
将原
main分支中的 sctl 守护进程实现通过 PR 重新引入;仓库main已重置为仅包含简单 README 的干净基线。此 PR 替代因分支历史被重建而无法重新打开的 #2。
关联开发与讨论:
主要内容