This commit is contained in:
Darren.Zeng 2026-04-14 18:27:29 +08:00 committed by GitHub
commit 28db4957ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 132 additions and 4 deletions

35
.editorconfig Normal file
View file

@ -0,0 +1,35 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
# Default settings for all files
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
# Go files
[*.go]
indent_style = tab
indent_size = 4
# JSON, YAML, and Markdown files
[*.{json,yml,yaml,md}]
indent_style = space
indent_size = 2
# JavaScript/TypeScript files
[*.{js,ts,tsx}]
indent_style = space
indent_size = 2
# Shell scripts
[*.sh]
indent_style = space
indent_size = 2
# Makefile
[Makefile]
indent_style = tab

21
.gitignore vendored
View file

@ -69,3 +69,24 @@ web/backend/dist/*
docker/data
.omc/
# Temporary files
*.tmp
*.temp
*.log
# Local development
local/
local.*
# Node modules for frontend
web/frontend/node_modules/
web/frontend/dist/
web/frontend/.cache/
# Python virtual environments
venv/
env/
ENV/
__pycache__/
*.py[cod]

View file

@ -5,7 +5,7 @@
<h3>$10 Hardware · 10MB RAM · ms Boot · Let's Go, PicoClaw!</h3>
<p>
<img src="https://img.shields.io/badge/Go-1.25+-00ADD8?style=flat&logo=go&logoColor=white" alt="Go">
<img src="https://img.shields.io/badge/Go-1.23+-00ADD8?style=flat&logo=go&logoColor=white" alt="Go">
<img src="https://img.shields.io/badge/Arch-x86__64%2C%20ARM64%2C%20MIPS%2C%20RISC--V%2C%20LoongArch-blue" alt="Hardware">
<img src="https://img.shields.io/badge/license-MIT-green" alt="License">
<br>

View file

@ -2,12 +2,14 @@ package gateway
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/sipeed/picoclaw/cmd/picoclaw/internal"
"github.com/sipeed/picoclaw/pkg/gateway"
"github.com/sipeed/picoclaw/pkg/logger"
"github.com/sipeed/picoclaw/pkg/pid"
"github.com/sipeed/picoclaw/pkg/utils"
)
@ -48,5 +50,42 @@ func NewGatewayCommand() *cobra.Command {
"Continue starting even when no default model is configured",
)
// Add stop subcommand
cmd.AddCommand(newGatewayStopCommand())
return cmd
}
func newGatewayStopCommand() *cobra.Command {
return &cobra.Command{
Use: "stop",
Short: "Stop the running picoclaw gateway",
Long: "Stop the running picoclaw gateway by reading its PID file and sending a termination signal.",
Args: cobra.NoArgs,
RunE: func(_ *cobra.Command, _ []string) error {
homePath := internal.GetPicoclawHome()
// Read PID file and check if process is running
data := pid.ReadPidFileWithCheck(homePath)
if data == nil {
fmt.Println("gateway is not running")
return nil
}
// Find the process
process, err := os.FindProcess(data.PID)
if err != nil {
return fmt.Errorf("failed to find gateway process (PID: %d): %w", data.PID, err)
}
// Send termination signal
fmt.Printf("stopping gateway (PID: %d)...\n", data.PID)
if err := stopProcess(process); err != nil {
return fmt.Errorf("failed to stop gateway (PID: %d): %w", data.PID, err)
}
fmt.Println("gateway stopped successfully")
return nil
},
}
}

View file

@ -0,0 +1,13 @@
//go:build !windows
package gateway
import (
"os"
"syscall"
)
// stopProcess sends SIGTERM to the process for graceful shutdown on Unix-like systems.
func stopProcess(process *os.Process) error {
return process.Signal(syscall.SIGTERM)
}

View file

@ -0,0 +1,12 @@
//go:build windows
package gateway
import (
"os"
)
// stopProcess kills the process on Windows (SIGTERM is not supported).
func stopProcess(process *os.Process) error {
return process.Kill()
}

View file

@ -5,6 +5,7 @@ import (
"crypto/rand"
"encoding/binary"
"encoding/hex"
"fmt"
"regexp"
"strconv"
"strings"
@ -128,9 +129,16 @@ func NewBaseChannel(
// currently defaults to accepting messages from ANYONE. To explicitly
// acknowledge and permit this (e.g. for a public bot), use ["*"].
if len(bc.allowList) == 0 {
logger.WarnCF("channels", "SECURITY: Channel allows EVERYONE (allow_from is empty)", map[string]any{
"channel": bc.name,
"hint": "Set allow_from to your ID, or use '*' to explicitly acknowledge open access.",
logger.WarnCF("channels", fmt.Sprintf("SECURITY: Channel '%s' allows EVERYONE (allow_from is empty). This is a potential security risk.", bc.name), map[string]any{
"channel": bc.name,
"channelID": bc.name,
"hint": "Set allow_from to your ID, or use ['*'] to explicitly acknowledge open access. See: https://github.com/sipeed/picoclaw/blob/main/docs/configuration.md",
})
} else {
logger.InfoCF("channels", fmt.Sprintf("Channel '%s' allow_from configured (%d entries)", bc.name, len(bc.allowList)), map[string]any{
"channel": bc.name,
"allow_list": bc.allowList,
"count": len(bc.allowList),
})
}