Files
neural-hunt/cmd/client/main.go
groot 28e125ffe3
All checks were successful
release-tag / release-image (push) Successful in 4m50s
RC-13
2026-08-13 12:40:51 +02:00

98 lines
3.2 KiB
Go

package main
import (
"context"
"flag"
"fmt"
"log"
"os"
"os/signal"
"strings"
"syscall"
)
func main() {
base := flag.String("url", envOr("NEURALHUNT_URL", "http://127.0.0.1:8080"), "Neural Hunt server URL")
identityPath := flag.String("identity", envOr("NEURALHUNT_IDENTITY", defaultIdentityPath()), "persistent terminal identity file")
taskSelector := flag.String("task", "", "initial task: number, ID prefix or display name")
maxNodes := flag.Int("max-nodes", 500, "maximum target-field working set")
nonInteractive := flag.Bool("non-interactive", false, "run unattended without command prompt")
quiet := flag.Bool("quiet", false, "suppress connection/status chatter")
beaconPath := flag.String("beacon-path", envOr("NEURALHUNT_BEACON_PATH", "auto"), "Beacon Hunt path: auto, pulse, flux or orbit")
importPath := flag.String("import", "", "import browser/terminal identity JSON before login")
exportPath := flag.String("export", "", "export current identity in browser-compatible encrypted format and exit")
passphrase := flag.String("passphrase", os.Getenv("NEURALHUNT_IDENTITY_PASSPHRASE"), "identity import/export passphrase (prefer environment variable)")
flag.Parse()
if strings.TrimSpace(*importPath) != "" {
id, err := readIdentityImport(*importPath, *passphrase)
if err != nil {
log.Fatal("identity import: ", err)
}
if err := saveIdentity(*identityPath, id); err != nil {
log.Fatal("save imported identity: ", err)
}
fmt.Println("Identity importiert nach", *identityPath)
}
id, key, created, err := loadOrCreateIdentity(*identityPath)
if err != nil {
log.Fatal(err)
}
if *exportPath != "" {
if err := exportBrowserIdentity(*exportPath, *passphrase, id); err != nil {
log.Fatal("identity export: ", err)
}
fmt.Println("Browser-kompatibler verschlüsselter Export:", *exportPath)
return
}
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
api := newAPI(*base, id, key)
if err := api.login(ctx); err != nil {
log.Fatal(err)
}
if err := api.registerHostedWorker(ctx); err != nil {
log.Fatal(err)
}
go api.hostedWorkerLeaseLoop(ctx, stop)
if created {
fmt.Println("Neue Terminal-Identität erzeugt:", *identityPath)
}
fmt.Println("NEURAL HUNT SHELL")
fmt.Println("Client-ID :", api.clientID())
fmt.Println("Identity-Datei:", *identityPath)
fmt.Println("Server :", strings.TrimRight(*base, "/"))
fmt.Println("Backup : identity export <datei> (verschlüsselt, im Browser importierbar)")
fmt.Println("Hinweis : Dieselbe Identity darf nicht gleichzeitig im Browser verbunden sein.")
a := newApp(ctx, api, *identityPath, *passphrase, *maxNodes, *quiet, *nonInteractive)
a.beaconPathMode = strings.ToLower(strings.TrimSpace(*beaconPath))
initial, err := selectInitialTask(ctx, a, *taskSelector, !*nonInteractive)
if err != nil {
log.Fatal(err)
}
if err := a.startTask(ctx, initial); err != nil {
log.Fatal(err)
}
go a.watcher(ctx)
if *nonInteractive {
<-ctx.Done()
a.stopSession()
return
}
if err := a.commandLoop(ctx, os.Stdin); err != nil && ctx.Err() == nil {
log.Println(err)
}
a.stopSession()
}
func envOr(name, fallback string) string {
if v := strings.TrimSpace(os.Getenv(name)); v != "" {
return v
}
return fallback
}