feat(cron): add command to show job details

Add new CLI command `cc-connect cron info <id>` that displays full JSON
details of a scheduled task. This helps users inspect all configuration
fields of a cron job that aren't visible in the compact `cron list` output.

Changes:
- Add `/cron/info` API endpoint in core/api.go
- Add `runCronInfo` CLI handler in cmd/cc-connect/cron.go
- Update help text to include the new command

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
术士木星andClaude Opus 4.6 authored and GitHub committed 2026-03-24 15:30:25 +08:00
1 parent 2700b5b027
commit 38ece2a2d2
2 files changed
+122

No files matched your search

+96
View File
@@ -26,6 +26,8 @@ func runCron(args []string) {
runCronList(args[1:])
case "edit":
runCronEdit(args[1:])
case "info":
runCronInfo(args[1:])
case "del", "delete", "rm", "remove":
runCronDel(args[1:])
case "--help", "-h", "help":
@@ -313,6 +315,98 @@ func runCronDel(args []string) {
fmt.Printf("Cron job %s deleted.\n", id)
}
func runCronInfo(args []string) {
var dataDir, id, field string
for i := 0; i < len(args); i++ {
switch args[i] {
case "--data-dir":
if i+1 < len(args) {
i++
dataDir = args[i]
}
default:
if id == "" {
id = args[i]
} else if field == "" {
field = args[i]
}
}
}
if id == "" {
fmt.Fprintln(os.Stderr, "Error: job ID is required")
fmt.Fprintln(os.Stderr, "Usage: cc-connect cron info <id> [field]")
fmt.Fprintln(os.Stderr, "Use 'cc-connect cron list' to see all task IDs.")
os.Exit(1)
}
sockPath := resolveSocketPath(dataDir)
if _, err := os.Stat(sockPath); os.IsNotExist(err) {
fmt.Fprintf(os.Stderr, "Error: cc-connect is not running (socket not found: %s)\n", sockPath)
os.Exit(1)
}
client := &http.Client{
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", sockPath)
},
},
}
resp, err := client.Get("http://unix/cron/info?id=" + id)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode == http.StatusNotFound {
fmt.Fprintf(os.Stderr, "Error: job '%s' not found\n", id)
os.Exit(1)
}
if resp.StatusCode != http.StatusOK {
fmt.Fprintf(os.Stderr, "Error: %s\n", strings.TrimSpace(string(body)))
os.Exit(1)
}
// If field specified, extract and print only that field
if field != "" {
var result map[string]any
if err := json.Unmarshal(body, &result); err != nil {
fmt.Fprintf(os.Stderr, "Error: invalid JSON response: %v\n", err)
os.Exit(1)
}
val, ok := result[field]
if !ok {
fmt.Fprintf(os.Stderr, "Error: field '%s' not found\n", field)
fmt.Fprintln(os.Stderr, "Available fields:")
for k := range result {
fmt.Fprintf(os.Stderr, " - %s\n", k)
}
os.Exit(1)
}
// Output field value (string directly, otherwise JSON formatted)
if s, ok := val.(string); ok {
fmt.Println(s)
} else {
data, _ := json.MarshalIndent(val, "", " ")
fmt.Println(string(data))
}
return
}
// Pretty-print full JSON
var prettyJSON bytes.Buffer
if err := json.Indent(&prettyJSON, body, "", " "); err != nil {
fmt.Fprintf(os.Stderr, "Error: invalid JSON response: %v\n", err)
os.Exit(1)
}
fmt.Println(prettyJSON.String())
}
func runCronEdit(args []string) {
var dataDir string
var id, field string
@@ -424,6 +518,8 @@ Commands:
add Create a new scheduled task
list List all scheduled tasks
edit Edit a scheduled task field
info <id> [field] Show detailed info of a scheduled task
(optionally filter to a single field)
del <id> Delete a scheduled task
Run 'cc-connect cron <command> --help' for details.`)
+26
View File
@@ -65,6 +65,7 @@ func NewAPIServer(dataDir string) (*APIServer, error) {
s.mux.HandleFunc("/sessions", s.handleSessions)
s.mux.HandleFunc("/cron/add", s.handleCronAdd)
s.mux.HandleFunc("/cron/list", s.handleCronList)
s.mux.HandleFunc("/cron/info", s.handleCronInfo)
s.mux.HandleFunc("/cron/edit", s.handleCronEdit)
s.mux.HandleFunc("/cron/del", s.handleCronDel)
s.mux.HandleFunc("/relay/send", s.handleRelaySend)
@@ -351,6 +352,31 @@ func (s *APIServer) handleCronDel(w http.ResponseWriter, r *http.Request) {
}
}
func (s *APIServer) handleCronInfo(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "GET only", http.StatusMethodNotAllowed)
return
}
if s.cron == nil {
http.Error(w, "cron scheduler not available", http.StatusServiceUnavailable)
return
}
id := r.URL.Query().Get("id")
if id == "" {
http.Error(w, "id is required", http.StatusBadRequest)
return
}
job := s.cron.store.Get(id)
if job == nil {
http.Error(w, fmt.Sprintf("job %q not found", id), http.StatusNotFound)
return
}
apiJSON(w, http.StatusOK, job)
}
func (s *APIServer) handleCronEdit(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "POST only", http.StatusMethodNotAllowed)