Skip to content
Merged
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
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,20 @@ cd neo-code
go run ./cmd/neocode
```

Gateway 独立进程(Step 1 骨架):
Gateway 子命令(Step 1 骨架):

```bash
go run ./cmd/neocode-gateway
go run ./cmd/neocode gateway

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Command migration docs are consistent with the implemented neocode gateway subcommand and url-dispatch skeleton behavior.

```

URL Scheme 派发骨架命令(EPIC-GW-02A):

```bash
go run ./cmd/neocode url-dispatch --url "neocode://review?path=README.md"
```

> `url-dispatch` 当前仅提供参数与命令骨架,实际派发能力在 EPIC-GW-02 实现。

设置 API Key 示例(按你使用的 provider 选择):

```bash
Expand Down
87 changes: 0 additions & 87 deletions cmd/neocode-gateway/main.go

This file was deleted.

89 changes: 0 additions & 89 deletions cmd/neocode-gateway/main_test.go

This file was deleted.

166 changes: 166 additions & 0 deletions internal/cli/gateway_commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package cli

import (
"context"
"errors"
"fmt"
"log"
"net/url"
"os"
"os/signal"
"strings"
"syscall"

"github.com/spf13/cobra"

"neo-code/internal/gateway"
)

const (
defaultGatewayLogLevel = "info"
)

var (
runGatewayCommand = defaultGatewayCommandRunner
runURLDispatchCommand = defaultURLDispatchCommandRunner
newGatewayServer = defaultNewGatewayServer
)

type gatewayCommandOptions struct {
ListenAddress string
LogLevel string
}

type urlDispatchCommandOptions struct {
URL string
ListenAddress string
}

type gatewayServer interface {
ListenAddress() string
Serve(ctx context.Context, runtimePort gateway.RuntimePort) error
Close(ctx context.Context) error
}

// newGatewayCommand 创建并返回网关子命令,负责启动本地 Gateway 进程。
func newGatewayCommand() *cobra.Command {
options := &gatewayCommandOptions{}

cmd := &cobra.Command{
Use: "gateway",
Short: "Start local gateway server",
SilenceUsage: true,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
normalizedLogLevel, err := normalizeGatewayLogLevel(options.LogLevel)
if err != nil {
return err
}

return runGatewayCommand(cmd.Context(), gatewayCommandOptions{
ListenAddress: strings.TrimSpace(options.ListenAddress),
LogLevel: normalizedLogLevel,
})
},
}

cmd.Flags().StringVar(&options.ListenAddress, "listen", "", "gateway listen address (optional override)")
cmd.Flags().StringVar(&options.LogLevel, "log-level", defaultGatewayLogLevel, "gateway log level: debug|info|warn|error")

return cmd
}

// normalizeGatewayLogLevel 对网关日志级别做归一化并校验合法值。
func normalizeGatewayLogLevel(logLevel string) (string, error) {
normalized := strings.ToLower(strings.TrimSpace(logLevel))
switch normalized {
case "debug", "info", "warn", "error":
return normalized, nil
default:
return "", fmt.Errorf("invalid --log-level %q: must be debug|info|warn|error", logLevel)
}
}

// defaultGatewayCommandRunner 使用网关服务骨架启动本地 IPC 监听并处理信号退出。
func defaultGatewayCommandRunner(ctx context.Context, options gatewayCommandOptions) error {
logger := log.New(os.Stderr, "neocode-gateway: ", log.LstdFlags)
logger.Printf("starting gateway (log-level=%s)", options.LogLevel)

signalContext, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM)
defer stop()

server, err := newGatewayServer(gateway.ServerOptions{
ListenAddress: options.ListenAddress,
Logger: logger,
})
if err != nil {
return err
}
defer func() {
_ = server.Close(context.Background())
}()

logger.Printf("gateway listen address: %s", server.ListenAddress())
return server.Serve(signalContext, nil)
}

// defaultNewGatewayServer 创建默认网关服务实例,供命令层启动流程调用。
func defaultNewGatewayServer(options gateway.ServerOptions) (gatewayServer, error) {
return gateway.NewServer(options)
}

// newURLDispatchCommand 创建 URL Scheme 派发子命令骨架,仅做参数收敛与调用转发。
func newURLDispatchCommand() *cobra.Command {
options := &urlDispatchCommandOptions{}

cmd := &cobra.Command{
Use: "url-dispatch [url]",
Short: "Dispatch a neocode:// URL to gateway (skeleton)",
SilenceUsage: true,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
urlValue := strings.TrimSpace(options.URL)
Comment thread
pionxe marked this conversation as resolved.
if urlValue == "" && len(args) == 1 {
urlValue = strings.TrimSpace(args[0])
}
if urlValue == "" {
return errors.New("missing required --url or positional <url>")
}
normalizedURL, err := normalizeDispatchURL(urlValue)
if err != nil {
return err
}

return runURLDispatchCommand(cmd.Context(), urlDispatchCommandOptions{
URL: normalizedURL,
ListenAddress: strings.TrimSpace(options.ListenAddress),
})
},
}

cmd.Flags().StringVar(&options.URL, "url", "", "neocode:// URL to dispatch")
cmd.Flags().StringVar(&options.ListenAddress, "listen", "", "gateway listen address override (reserved for EPIC-GW-02)")

return cmd
}

// defaultURLDispatchCommandRunner 提供 url-dispatch 的默认骨架行为,明确告知后续步骤接管实现。
func defaultURLDispatchCommandRunner(_ context.Context, _ urlDispatchCommandOptions) error {
return errors.New("url-dispatch is not implemented yet (planned in EPIC-GW-02)")
}

// normalizeDispatchURL 校验并标准化 url-dispatch 输入,确保只接受 neocode scheme。
func normalizeDispatchURL(rawURL string) (string, error) {
parsed, err := url.Parse(strings.TrimSpace(rawURL))
if err != nil {
return "", fmt.Errorf("invalid --url %q: %w", rawURL, err)
}
if !strings.EqualFold(strings.TrimSpace(parsed.Scheme), "neocode") {
return "", fmt.Errorf("invalid --url scheme %q: must be neocode", parsed.Scheme)
}
if strings.TrimSpace(parsed.Host) == "" {
return "", errors.New("invalid --url: missing action host")
}

return parsed.String(), nil
}
4 changes: 4 additions & 0 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ func NewRootCommand() *cobra.Command {

cmd.PersistentFlags().String("workdir", "", "工作目录(覆盖本次运行工作区)")
_ = settings.BindPFlag("workdir", cmd.PersistentFlags().Lookup("workdir"))
cmd.AddCommand(
newGatewayCommand(),
newURLDispatchCommand(),
)

return cmd
}
Expand Down
Loading
Loading