From 510f80c545d27f96b82763846082608a41bda644 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Fri, 13 Feb 2026 23:54:21 -0800 Subject: [PATCH 1/4] First iteration --- .gitignore | 1 + .superset/setup.sh | 50 ++++++++++++++++++-- apps/desktop/src/main/lib/app-environment.ts | 39 ++++++++++++++- 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 8099e28231f..c3779e9d503 100644 --- a/.gitignore +++ b/.gitignore @@ -84,3 +84,4 @@ apps/streams/data/ # Generated by setup.sh Caddyfile +superset-dev-data/ diff --git a/.superset/setup.sh b/.superset/setup.sh index 53dc3adaa5d..645cbf23807 100755 --- a/.superset/setup.sh +++ b/.superset/setup.sh @@ -476,6 +476,45 @@ PORTSJSON return 0 } +step_seed_local_db() { + echo "๐Ÿ’พ Seeding local DB into superset-dev-data/..." + + local source_db="$HOME/.superset/local.db" + local dev_data_dir="superset-dev-data" + local dest_db="$dev_data_dir/local.db" + + if [ ! -f "$source_db" ]; then + warn "No source local.db found at $source_db โ€” skipping (app will create a fresh one)" + step_skipped "Seed local DB (no source DB)" + return 0 + fi + + mkdir -p "$dev_data_dir" + chmod 700 "$dev_data_dir" + + # Copy all SQLite files so WAL data isn't lost when source is held open. + for ext in "" "-shm" "-wal"; do + local source_file="${source_db}${ext}" + local dest_file="${dest_db}${ext}" + + if [ -f "$source_file" ]; then + if ! cp "$source_file" "$dest_file"; then + error "Failed to copy $source_file to $dest_file" + return 1 + fi + chmod 600 "$dest_file" + fi + done + + # Checkpoint the copy's WAL (no lock contention since nothing else has it open). + if command -v sqlite3 &> /dev/null; then + sqlite3 "$dest_db" "PRAGMA wal_checkpoint(TRUNCATE);" &> /dev/null || true + fi + + success "Local DB seeded from $source_db" + return 0 +} + main() { echo "๐Ÿš€ Setting up Superset workspace..." echo "" @@ -495,17 +534,22 @@ main() { step_failed "Install dependencies" fi - # Step 4: Setup Neon branch + # Step 4: Seed local DB into superset-dev-data/ + if ! step_seed_local_db; then + step_failed "Seed local DB" + fi + + # Step 5: Setup Neon branch if ! step_setup_neon_branch; then step_failed "Setup Neon branch" fi - # Step 5: Start Electric SQL + # Step 6: Start Electric SQL if ! step_start_electric; then step_failed "Start Electric SQL" fi - # Step 6: Write .env file + # Step 7: Write .env file if ! step_write_env; then step_failed "Write .env file" fi diff --git a/apps/desktop/src/main/lib/app-environment.ts b/apps/desktop/src/main/lib/app-environment.ts index 978cbe5bafa..cf7f6351269 100644 --- a/apps/desktop/src/main/lib/app-environment.ts +++ b/apps/desktop/src/main/lib/app-environment.ts @@ -1,9 +1,44 @@ import { chmodSync, existsSync, mkdirSync } from "node:fs"; import { homedir } from "node:os"; -import { join } from "node:path"; +import { dirname, join } from "node:path"; import { SUPERSET_DIR_NAME } from "shared/constants"; -export const SUPERSET_HOME_DIR = join(homedir(), SUPERSET_DIR_NAME); +const DEV_DATA_DIR_NAME = "superset-dev-data"; +const SUPERSET_HOME_DIR_ENV = "SUPERSET_HOME_DIR"; + +function findWorktreeRoot(startDir: string): string | null { + let currentDir = startDir; + + while (true) { + if (existsSync(join(currentDir, ".git"))) { + return currentDir; + } + + const parentDir = dirname(currentDir); + if (parentDir === currentDir) { + return null; + } + + currentDir = parentDir; + } +} + +function resolveSupersetHomeDir(): string { + const envSupersetHomeDir = process.env[SUPERSET_HOME_DIR_ENV]; + if (envSupersetHomeDir) { + return envSupersetHomeDir; + } + + if (process.env.NODE_ENV === "development") { + const worktreeRoot = findWorktreeRoot(process.cwd()); + return join(worktreeRoot ?? process.cwd(), DEV_DATA_DIR_NAME); + } + + return join(homedir(), SUPERSET_DIR_NAME); +} + +export const SUPERSET_HOME_DIR = resolveSupersetHomeDir(); +process.env[SUPERSET_HOME_DIR_ENV] = SUPERSET_HOME_DIR; export const SUPERSET_HOME_DIR_MODE = 0o700; export const SUPERSET_SENSITIVE_FILE_MODE = 0o600; From 4d7713503793480e8e0d77f9c9b9ffbe74eb60bc Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Fri, 13 Feb 2026 23:57:11 -0800 Subject: [PATCH 2/4] Cleaner --- .superset/setup.sh | 1 + apps/desktop/src/main/lib/app-environment.ts | 37 ++------------------ 2 files changed, 4 insertions(+), 34 deletions(-) diff --git a/.superset/setup.sh b/.superset/setup.sh index 645cbf23807..e60d270a785 100755 --- a/.superset/setup.sh +++ b/.superset/setup.sh @@ -332,6 +332,7 @@ step_write_env() { echo "" echo "# Workspace Identity" echo "SUPERSET_WORKSPACE_NAME=${WORKSPACE_NAME:-$(basename "$PWD")}" + echo "SUPERSET_HOME_DIR=$PWD/superset-dev-data" echo "" echo "# Workspace Database (Neon Branch)" if [ -n "${BRANCH_ID:-}" ]; then diff --git a/apps/desktop/src/main/lib/app-environment.ts b/apps/desktop/src/main/lib/app-environment.ts index cf7f6351269..945043f352e 100644 --- a/apps/desktop/src/main/lib/app-environment.ts +++ b/apps/desktop/src/main/lib/app-environment.ts @@ -1,43 +1,12 @@ import { chmodSync, existsSync, mkdirSync } from "node:fs"; import { homedir } from "node:os"; -import { dirname, join } from "node:path"; +import { join } from "node:path"; import { SUPERSET_DIR_NAME } from "shared/constants"; -const DEV_DATA_DIR_NAME = "superset-dev-data"; const SUPERSET_HOME_DIR_ENV = "SUPERSET_HOME_DIR"; -function findWorktreeRoot(startDir: string): string | null { - let currentDir = startDir; - - while (true) { - if (existsSync(join(currentDir, ".git"))) { - return currentDir; - } - - const parentDir = dirname(currentDir); - if (parentDir === currentDir) { - return null; - } - - currentDir = parentDir; - } -} - -function resolveSupersetHomeDir(): string { - const envSupersetHomeDir = process.env[SUPERSET_HOME_DIR_ENV]; - if (envSupersetHomeDir) { - return envSupersetHomeDir; - } - - if (process.env.NODE_ENV === "development") { - const worktreeRoot = findWorktreeRoot(process.cwd()); - return join(worktreeRoot ?? process.cwd(), DEV_DATA_DIR_NAME); - } - - return join(homedir(), SUPERSET_DIR_NAME); -} - -export const SUPERSET_HOME_DIR = resolveSupersetHomeDir(); +export const SUPERSET_HOME_DIR = + process.env[SUPERSET_HOME_DIR_ENV] || join(homedir(), SUPERSET_DIR_NAME); process.env[SUPERSET_HOME_DIR_ENV] = SUPERSET_HOME_DIR; export const SUPERSET_HOME_DIR_MODE = 0o700; From 4522318b106233d0f5079b71f3ba6ee900486207 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sat, 14 Feb 2026 00:04:10 -0800 Subject: [PATCH 3/4] Add force flag --- .superset/setup.sh | 52 +++++++++++++++++++++++++++++++++ .superset/teardown.sh | 67 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/.superset/setup.sh b/.superset/setup.sh index e60d270a785..d714cb12dba 100755 --- a/.superset/setup.sh +++ b/.superset/setup.sh @@ -10,11 +10,44 @@ NC='\033[0m' # Step tracking declare -a FAILED_STEPS=() declare -a SKIPPED_STEPS=() +FORCE_OVERWRITE_DATA=0 error() { echo -e "${RED}โœ—${NC} $1"; } success() { echo -e "${GREEN}โœ“${NC} $1"; } warn() { echo -e "${YELLOW}!${NC} $1"; } +print_usage() { + cat < Date: Sat, 14 Feb 2026 00:16:11 -0800 Subject: [PATCH 4/4] fix(desktop): isolate notification event mapping for tests --- .../main/lib/notifications/map-event-type.ts | 24 ++++++++++++++ .../src/main/lib/notifications/server.test.ts | 2 +- .../src/main/lib/notifications/server.ts | 31 +------------------ 3 files changed, 26 insertions(+), 31 deletions(-) create mode 100644 apps/desktop/src/main/lib/notifications/map-event-type.ts diff --git a/apps/desktop/src/main/lib/notifications/map-event-type.ts b/apps/desktop/src/main/lib/notifications/map-event-type.ts new file mode 100644 index 00000000000..8d01eae3757 --- /dev/null +++ b/apps/desktop/src/main/lib/notifications/map-event-type.ts @@ -0,0 +1,24 @@ +/** + * Maps incoming event types to canonical lifecycle events. + * Handles variations from different agent CLIs. + * + * Returns null for unknown events so callers can ignore safely + * for forward compatibility. + */ +export function mapEventType( + eventType: string | undefined, +): "Start" | "Stop" | "PermissionRequest" | null { + if (!eventType) { + return null; + } + if (eventType === "Start" || eventType === "UserPromptSubmit") { + return "Start"; + } + if (eventType === "PermissionRequest") { + return "PermissionRequest"; + } + if (eventType === "Stop" || eventType === "agent-turn-complete") { + return "Stop"; + } + return null; +} diff --git a/apps/desktop/src/main/lib/notifications/server.test.ts b/apps/desktop/src/main/lib/notifications/server.test.ts index 94e095508e9..c83562d2a11 100644 --- a/apps/desktop/src/main/lib/notifications/server.test.ts +++ b/apps/desktop/src/main/lib/notifications/server.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "bun:test"; -import { mapEventType } from "./server"; +import { mapEventType } from "./map-event-type"; describe("notifications/server", () => { describe("mapEventType", () => { diff --git a/apps/desktop/src/main/lib/notifications/server.ts b/apps/desktop/src/main/lib/notifications/server.ts index 7f4e849d50b..54e54b8d542 100644 --- a/apps/desktop/src/main/lib/notifications/server.ts +++ b/apps/desktop/src/main/lib/notifications/server.ts @@ -5,6 +5,7 @@ import { env } from "shared/env.shared"; import type { AgentLifecycleEvent } from "shared/notification-types"; import { appState } from "../app-state"; import { HOOK_PROTOCOL_VERSION } from "../terminal/env"; +import { mapEventType } from "./map-event-type"; // Re-export types for backwards compatibility export type { @@ -36,36 +37,6 @@ app.use((req, res, next) => { next(); }); -/** - * Maps incoming event types to canonical lifecycle events. - * Handles variations from different agent CLIs. - * - * Returns null for unknown events - caller should ignore these gracefully - * to maintain forward compatibility with newer hook versions. - * - * Note: We no longer default missing eventType to "Stop" to prevent - * parse failures from being treated as completions. - * - * @internal Exported for testing - */ -export function mapEventType( - eventType: string | undefined, -): "Start" | "Stop" | "PermissionRequest" | null { - if (!eventType) { - return null; // Missing eventType should be ignored, not treated as Stop - } - if (eventType === "Start" || eventType === "UserPromptSubmit") { - return "Start"; - } - if (eventType === "PermissionRequest") { - return "PermissionRequest"; - } - if (eventType === "Stop" || eventType === "agent-turn-complete") { - return "Stop"; - } - return null; // Unknown events are ignored for forward compatibility -} - /** * Resolves paneId from tabId or workspaceId using synced tabs state. * Falls back to focused pane in active tab.