Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,4 @@ apps/streams/data/

# Generated by setup.sh
Caddyfile
superset-dev-data/
103 changes: 100 additions & 3 deletions .superset/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<EOF
Usage: .superset/setup.sh [options]

Options:
-f, --force Reset superset-dev-data/ before seeding local DB
-h, --help Show this help message
EOF
}

parse_args() {
while [ $# -gt 0 ]; do
case "$1" in
-f|--force)
FORCE_OVERWRITE_DATA=1
shift
;;
-h|--help)
print_usage
exit 0
;;
*)
error "Unknown argument: $1"
print_usage
return 1
;;
esac
done

return 0
}

# Track step failure
step_failed() {
FAILED_STEPS+=("$1")
Expand Down Expand Up @@ -332,6 +365,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
Expand Down Expand Up @@ -476,7 +510,65 @@ 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"
local force_overwrite="$FORCE_OVERWRITE_DATA"

if [ "$force_overwrite" = "1" ] && [ -d "$dev_data_dir" ]; then
warn "Force overwrite enabled — removing existing $dev_data_dir/"
if ! rm -rf "$dev_data_dir"; then
error "Failed to remove existing $dev_data_dir/"
return 1
fi
fi

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
Comment on lines +521 to +533
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

--force with no source DB deletes superset-dev-data/ without recreating it.

When --force is set but $HOME/.superset/local.db doesn't exist, the directory is removed (line 523) and then the function returns early at line 532 without calling mkdir -p (line 541). Downstream code or the app expecting superset-dev-data/ to exist could fail.

Consider ensuring the directory is recreated even when skipping the seed:

Proposed fix
   if [ ! -f "$source_db" ]; then
     warn "No source local.db found at $source_db — skipping (app will create a fresh one)"
+    mkdir -p "$dev_data_dir"
+    chmod 700 "$dev_data_dir"
     step_skipped "Seed local DB (no source DB)"
     return 0
   fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if [ "$force_overwrite" = "1" ] && [ -d "$dev_data_dir" ]; then
warn "Force overwrite enabled — removing existing $dev_data_dir/"
if ! rm -rf "$dev_data_dir"; then
error "Failed to remove existing $dev_data_dir/"
return 1
fi
fi
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
if [ "$force_overwrite" = "1" ] && [ -d "$dev_data_dir" ]; then
warn "Force overwrite enabled — removing existing $dev_data_dir/"
if ! rm -rf "$dev_data_dir"; then
error "Failed to remove existing $dev_data_dir/"
return 1
fi
fi
if [ ! -f "$source_db" ]; then
warn "No source local.db found at $source_db — skipping (app will create a fresh one)"
mkdir -p "$dev_data_dir"
chmod 700 "$dev_data_dir"
step_skipped "Seed local DB (no source DB)"
return 0
fi
🤖 Prompt for AI Agents
In @.superset/setup.sh around lines 521 - 533, When --force removes
$dev_data_dir but no $source_db exists, the script returns early without
recreating the directory; ensure $dev_data_dir is recreated before returning
when skipping seed. Update the branch that handles [ ! -f "$source_db" ] to run
mkdir -p "$dev_data_dir" (or call the existing directory-creation logic) after
the warning/step_skipped and before return 0 so that variables/consumers
expecting dev_data_dir (variables: force_overwrite, dev_data_dir, source_db)
still find the directory even when seeding is skipped.


if [ -f "$dest_db" ] && [ "$force_overwrite" != "1" ]; then
warn "Destination DB already exists at $dest_db — skipping seed (use -f/--force)"
step_skipped "Seed local DB (already exists)"
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() {
if ! parse_args "$@"; then
return 1
fi

echo "🚀 Setting up Superset workspace..."
echo ""

Expand All @@ -495,17 +587,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
Expand Down
67 changes: 67 additions & 0 deletions .superset/teardown.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,44 @@ NC='\033[0m'
# Step tracking
declare -a FAILED_STEPS=()
declare -a SKIPPED_STEPS=()
REMOVE_DEV_DATA=0

error() { echo -e "${RED}✗${NC} $1"; }
success() { echo -e "${GREEN}✓${NC} $1"; }
warn() { echo -e "${YELLOW}!${NC} $1"; }

print_usage() {
cat <<EOF
Usage: .superset/teardown.sh [options]

Options:
-f, --force Remove superset-dev-data/ in current workspace
-h, --help Show this help message
EOF
}

parse_args() {
while [ $# -gt 0 ]; do
case "$1" in
-f|--force)
REMOVE_DEV_DATA=1
shift
;;
-h|--help)
print_usage
exit 0
;;
*)
error "Unknown argument: $1"
print_usage
return 1
;;
esac
done

return 0
}

# Track step failure
step_failed() {
FAILED_STEPS+=("$1")
Expand Down Expand Up @@ -209,7 +242,36 @@ step_delete_neon_branch() {
return 0
}

step_remove_dev_data() {
local dev_data_dir="superset-dev-data"

if [ "$REMOVE_DEV_DATA" != "1" ]; then
step_skipped "Remove superset-dev-data (flag not set)"
return 0
fi

echo "🗑️ Removing $dev_data_dir/..."

if [ ! -d "$dev_data_dir" ]; then
warn "$dev_data_dir/ not found, skipping"
step_skipped "Remove superset-dev-data (not found)"
return 0
fi

if ! rm -rf "$dev_data_dir"; then
error "Failed to remove $dev_data_dir/"
return 1
fi

success "Removed $dev_data_dir/"
return 0
}

main() {
if ! parse_args "$@"; then
return 1
fi

echo "🧹 Tearing down Superset workspace..."
echo ""

Expand All @@ -236,6 +298,11 @@ main() {
step_failed "Delete Neon branch"
fi

# Step 6: Remove superset-dev-data (optional)
if ! step_remove_dev_data; then
step_failed "Remove superset-dev-data"
fi

# Print summary and exit with appropriate code
print_summary
}
Expand Down
6 changes: 5 additions & 1 deletion apps/desktop/src/main/lib/app-environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { homedir } from "node:os";
import { join } from "node:path";
import { SUPERSET_DIR_NAME } from "shared/constants";

export const SUPERSET_HOME_DIR = join(homedir(), SUPERSET_DIR_NAME);
const SUPERSET_HOME_DIR_ENV = "SUPERSET_HOME_DIR";

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;
export const SUPERSET_SENSITIVE_FILE_MODE = 0o600;
Expand Down
24 changes: 24 additions & 0 deletions apps/desktop/src/main/lib/notifications/map-event-type.ts
Original file line number Diff line number Diff line change
@@ -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;
}
2 changes: 1 addition & 1 deletion apps/desktop/src/main/lib/notifications/server.test.ts
Original file line number Diff line number Diff line change
@@ -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", () => {
Expand Down
31 changes: 1 addition & 30 deletions apps/desktop/src/main/lib/notifications/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
Expand Down