@@ -0,0 +1,89 @@
|
||||
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")
|
||||
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 created {
|
||||
fmt.Println("Neue Terminal-Identität erzeugt:", *identityPath)
|
||||
}
|
||||
fmt.Println("NEURAL HUNT SHELL")
|
||||
fmt.Println("Identity:", api.cid)
|
||||
fmt.Println("Server :", strings.TrimRight(*base, "/"))
|
||||
fmt.Println("Hinweis : Dieselbe Identity darf nicht gleichzeitig im Browser verbunden sein.")
|
||||
|
||||
a := newApp(api, *identityPath, *passphrase, *maxNodes, *quiet, *nonInteractive)
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user