Skip to content

Add ESP32-S3 companion support and harden Command Centre config/file handling - #1

Merged
artifact-opensource merged 9 commits into
mainfrom
copilot/add-esp32-s3-directory
Aug 22, 2026
Merged

Add ESP32-S3 companion support and harden Command Centre config/file handling#1
artifact-opensource merged 9 commits into
mainfrom
copilot/add-esp32-s3-directory

Conversation

Copilot AI commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

This change adds the ESP32-S3 companion scaffold and wires the Command Centre to manage shared firmware, RGB profiles, and related storage paths. It also removes unintended config writes and tightens managed-path handling so the new flows behave predictably.

  • Command Centre surface area

    • Adds API/UI support for provider selection, shared storage, secondary firmware staging, and RGB profile selection.
    • Exposes a firmware catalog spanning shared assets, staged secondary assets, and the local ESP32-S3 project.
  • Config and provider behavior

    • Normalizes shared_dir and secondary_firmware_dir consistently under managed roots.
    • Removes save-on-read behavior from HTTP handlers and server startup.
    • Canonicalizes the legacy gemini alias to the google provider to avoid duplicate registry entries in the UI.
  • Filesystem safety

    • Resolves firmware actions through managed asset catalogs instead of trusting arbitrary request paths.
    • Revalidates source/destination paths against configured roots before copy/create operations.
  • ESP32-S3 companion scaffold

    • Adds the native ESP-IDF scaffold, partition layout, RGB profile definitions, and host-simulation path.
    • Keeps GPIO reporting consistent by deriving status output from the shared GPIO constant.
  • Regression coverage

    • Adds tests for shared/secondary directory normalization.
    • Adds tests that verify Command Centre reads and firmware actions do not persist config unexpectedly.
func (c *Config) Set(key, value string) {
	switch key {
	case "provider":
		info := provider.LookupProvider(value)
		if info != nil {
			c.Provider = info.ID // canonicalizes "gemini" -> "google"
			c.BaseURL = info.BaseURL
		} else {
			c.Provider = value
		}
	}
	c.normalize()
}

Copilot AI and others added 9 commits August 21, 2026 20:07
Co-authored-by: artifact-opensource <270091407+artifact-opensource@users.noreply.github.com>
Co-authored-by: artifact-opensource <270091407+artifact-opensource@users.noreply.github.com>
Co-authored-by: artifact-opensource <270091407+artifact-opensource@users.noreply.github.com>
Co-authored-by: artifact-opensource <270091407+artifact-opensource@users.noreply.github.com>
Co-authored-by: artifact-opensource <270091407+artifact-opensource@users.noreply.github.com>
Co-authored-by: artifact-opensource <270091407+artifact-opensource@users.noreply.github.com>
Co-authored-by: artifact-opensource <270091407+artifact-opensource@users.noreply.github.com>
Co-authored-by: artifact-opensource <270091407+artifact-opensource@users.noreply.github.com>
Co-authored-by: artifact-opensource <270091407+artifact-opensource@users.noreply.github.com>
Copilot AI changed the title Add ESP32-S3 companion scaffold and command centre support Add ESP32-S3 companion support and harden Command Centre config/file handling Aug 22, 2026
@artifact-opensource
artifact-opensource marked this pull request as ready for review August 22, 2026 08:04
Copilot AI lite review requested due to automatic review settings August 22, 2026 08:04
@artifact-opensource
artifact-opensource merged commit bdc749e into main Aug 22, 2026
2 checks passed
@artifact-opensource
artifact-opensource deleted the copilot/add-esp32-s3-directory branch August 22, 2026 08:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The Command Centre currently exposes sensitive config fields to the browser and has a couple of concrete correctness/security issues (XSS risk, stale /shared root, and path-prefix logic).

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds an ESP32-S3 companion scaffold and introduces a new “Command Centre” surface in the Android daemon to manage providers, shared storage, firmware staging, and RGB profiles, alongside config normalization and regression tests to prevent unintended persistence.

Changes:

  • Added esp32_s3/ companion scaffold (ESP-IDF project files, partition layout, Lua RGB profiles, host simulation).
  • Added Command Centre UI/API endpoints plus firmware catalog + install/flash actions, and exposed /shared/ browsing.
  • Hardened config/provider behavior (directory normalization, provider alias canonicalization) and added regression tests.
File summaries
File Description
README.md Documents ESP32-S3 companion and Command Centre component; updates provider list.
esp32_s3/README.md New documentation for ESP32-S3 scaffold, layout, and host simulation.
esp32_s3/partitions.csv Defines partitions including spore_assets for staged firmware/assets.
esp32_s3/main/spore_esp32_s3.c New companion firmware + host sim, Lua-backed RGB profile loading, status snapshot scaffold.
esp32_s3/main/rgb_profiles.lua Default RGB activity profiles/patterns.
esp32_s3/main/CMakeLists.txt ESP-IDF component registration for companion firmware.
esp32_s3/CMakeLists.txt ESP-IDF project scaffold entry.
android/README.md Mentions Command Centre and adds provider setup examples.
android/provider/provider.go Expands/adjusts provider registry; canonicalizes geminigoogle.
android/main.go Updates CLI help to include Command Centre and expanded providers.
android/go.mod Marks nhooyr.io/websocket as a direct dependency.
android/daemon/webchat.go Adds a navigation link to /command-centre.
android/daemon/daemon.go Registers Command Centre routes and /shared/ file browsing.
android/daemon/command_centre.go New Command Centre API/UI, firmware catalog + install/flash actions, storage/provider controls.
android/daemon/command_centre_test.go New tests for Command Centre behavior and persistence expectations.
android/core/config.go Adds shared/storage fields and normalizes managed directories; provider canonicalization.
android/core/config_test.go Adds tests for shared/secondary dir normalization and provider alias canonicalization.
Review details
  • Files reviewed: 17/17 changed files
  • Comments generated: 8
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread android/core/config.go
case "shared_dir":
oldShared := c.SharedDir
c.SharedDir = value
if c.SecondaryFirmwareDir == "" || (oldShared != "" && strings.HasPrefix(filepath.Clean(c.SecondaryFirmwareDir), filepath.Clean(oldShared))) {
Comment thread android/daemon/daemon.go
mux.HandleFunc("/api/sessions/", h.sessionByID)
mux.HandleFunc("/api/command-centre", h.commandCentreAPI)
mux.HandleFunc("/api/command-centre/firmware", h.commandCentreFirmware)
mux.Handle("/shared/", http.StripPrefix("/shared/", http.FileServer(http.Dir(cfg.SharedDir))))
Comment on lines +207 to +210
projectDir := detectESP32Project()
configView := *cfg
configView.APIKey = ""
return commandCentreState{
Comment on lines +170 to +176
case "install", "":
target, err := joinUnderRoot(cfg.SecondaryFirmwareDir, filepath.Base(source))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := copyFile(sourceRoot, source, cfg.SecondaryFirmwareDir, target); err != nil {
Comment on lines +635 to +638
document.getElementById('sharedFiles').innerHTML=(state.shared_files||[]).map(function(f){return '<div class="item"><strong>'+f.name+'</strong><small>'+f.path+'</small></div>';}).join('') || '<div class="muted">Shared directory is empty.</div>';
document.getElementById('firmware').innerHTML=(state.firmware_catalog||[]).map(function(f){return '<div class="item"><strong>'+f.name+'</strong><small>'+f.path+'</small><div class="actions"><button data-source="'+esc(f.source)+'" onclick="installFirmware(this.dataset.source)">Install</button><button class="ghost" data-source="'+esc(f.source)+'" onclick="flashFirmware(this.dataset.source)">Flash</button></div></div>';}).join('') || '<div class="muted">No firmware or Lua/MicroPython assets detected yet.</div>';
}
function esc(v){return String(v).replace(/'/g,"&#39;")}
Comment on lines +6 to +14

#if __has_include(<lua.h>)
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#define SPORE_HAS_LUA 1
#else
#define SPORE_HAS_LUA 0
#endif
Comment on lines +82 to +84
lua_getfield(L, -1, "pattern");
snprintf(out->pattern, sizeof(out->pattern), "%s", lua_tostring(L, -1));
lua_pop(L, 1);
#if SPORE_HAS_HTTPD
static esp_err_t status_handler(httpd_req_t *req) {
char body[96];
snprintf(body, sizeof(body), "{\"service\":\"spore-command-centre\",\"gpio_rgb\":%d,\"espnow\":true}", SPORE_RGB_GPIO);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants