初始化仓库

This commit is contained in:
2026-06-02 23:14:41 +08:00
commit 0bc3f02670
520 changed files with 191097 additions and 0 deletions
+228
View File
@@ -0,0 +1,228 @@
package acp
import (
"bufio"
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"log/slog"
"sync"
"sync/atomic"
)
type rpcOutcome struct {
result json.RawMessage
err *rpcErrPayload
}
type rpcErrPayload struct {
Code int `json:"code"`
Message string `json:"message"`
}
func (e *rpcErrPayload) Error() string {
if e == nil {
return "json-rpc error"
}
return fmt.Sprintf("json-rpc %d: %s", e.Code, e.Message)
}
type rpcNotifyHandler func(method string, params json.RawMessage)
type rpcRequestHandler func(method string, id json.RawMessage, params json.RawMessage)
// transport implements newline-delimited JSON-RPC 2.0 over a pair of streams.
type transport struct {
in *bufio.Reader
out io.Writer
mu sync.Mutex
enc *json.Encoder
nextID atomic.Int64
pendingMu sync.Mutex
pending map[string]chan rpcOutcome
onNotif rpcNotifyHandler
onReq rpcRequestHandler
}
func newTransport(in io.Reader, out io.Writer, onNotif rpcNotifyHandler, onReq rpcRequestHandler) *transport {
return &transport{
in: bufio.NewReader(in),
out: out,
enc: json.NewEncoder(out),
pending: make(map[string]chan rpcOutcome),
onNotif: onNotif,
onReq: onReq,
}
}
func (t *transport) readLoop(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
default:
}
line, err := t.readLine()
if err != nil {
if err != io.EOF {
slog.Debug("acp: read error", "error", err)
}
t.cancelAll(fmt.Errorf("acp: read closed: %w", err))
return
}
if len(bytes.TrimSpace(line)) == 0 {
continue
}
t.dispatchLine(line)
}
}
func (t *transport) readLine() ([]byte, error) {
line, err := t.in.ReadBytes('\n')
if err != nil {
return line, err
}
return bytes.TrimSuffix(line, []byte("\r")), nil
}
func (t *transport) dispatchLine(line []byte) {
var env struct {
JSONRPC string `json:"jsonrpc"`
ID json.RawMessage `json:"id"`
Method string `json:"method"`
Params json.RawMessage `json:"params"`
Result json.RawMessage `json:"result"`
Error *rpcErrPayload `json:"error"`
}
if err := json.Unmarshal(line, &env); err != nil {
slog.Debug("acp: skip non-json line", "line", string(line))
return
}
if env.Method != "" {
if isJSONRPCIDNullOrAbsent(env.ID) {
if t.onNotif != nil {
t.onNotif(env.Method, env.Params)
}
return
}
if t.onReq != nil {
t.onReq(env.Method, env.ID, env.Params)
}
return
}
if !isJSONRPCIDNullOrAbsent(env.ID) {
t.completePending(env.ID, env.Result, env.Error)
}
}
func isJSONRPCIDNullOrAbsent(id json.RawMessage) bool {
if len(id) == 0 {
return true
}
return bytes.Equal(bytes.TrimSpace(id), []byte("null"))
}
func jsonIDKey(id json.RawMessage) string {
id = bytes.TrimSpace(id)
var n json.Number
if json.Unmarshal(id, &n) == nil {
return string(n)
}
var s string
if json.Unmarshal(id, &s) == nil {
return s
}
return string(id)
}
func (t *transport) completePending(id json.RawMessage, result json.RawMessage, rpcErr *rpcErrPayload) {
key := jsonIDKey(id)
t.pendingMu.Lock()
ch, ok := t.pending[key]
delete(t.pending, key)
t.pendingMu.Unlock()
if !ok {
slog.Debug("acp: unmatched rpc response", "id", key)
return
}
select {
case ch <- rpcOutcome{result: result, err: rpcErr}:
default:
}
}
func (t *transport) cancelAll(err error) {
t.pendingMu.Lock()
defer t.pendingMu.Unlock()
msg := err.Error()
for k, ch := range t.pending {
select {
case ch <- rpcOutcome{err: &rpcErrPayload{Code: -32000, Message: msg}}:
default:
}
delete(t.pending, k)
}
}
func (t *transport) call(ctx context.Context, method string, params any) (json.RawMessage, error) {
id := t.nextID.Add(1)
key := fmt.Sprintf("%d", id)
ch := make(chan rpcOutcome, 1)
t.pendingMu.Lock()
t.pending[key] = ch
t.pendingMu.Unlock()
req := map[string]any{
"jsonrpc": "2.0",
"id": id,
"method": method,
"params": params,
}
if err := t.writeJSON(req); err != nil {
t.pendingMu.Lock()
delete(t.pending, key)
t.pendingMu.Unlock()
return nil, fmt.Errorf("acp: write %s: %w", method, err)
}
select {
case <-ctx.Done():
t.pendingMu.Lock()
delete(t.pending, key)
t.pendingMu.Unlock()
return nil, ctx.Err()
case out := <-ch:
if out.err != nil {
return nil, out.err
}
return out.result, nil
}
}
func (t *transport) writeJSON(v any) error {
t.mu.Lock()
defer t.mu.Unlock()
return t.enc.Encode(v)
}
type rpcResponseMsg struct {
JSONRPC string `json:"jsonrpc"`
ID json.RawMessage `json:"id"`
Result any `json:"result,omitempty"`
Error *rpcErrPayload `json:"error,omitempty"`
}
func (t *transport) respondSuccess(id json.RawMessage, result any) error {
return t.writeJSON(rpcResponseMsg{JSONRPC: "2.0", ID: id, Result: result})
}
func (t *transport) respondError(id json.RawMessage, code int, message string) error {
return t.writeJSON(rpcResponseMsg{
JSONRPC: "2.0",
ID: id,
Error: &rpcErrPayload{Code: code, Message: message},
})
}