Merge pull request #181 from multica-ai/forrestchang/dev-config-isolation

feat(dev): isolate dev:local data and workspace directories
This commit is contained in:
Jiayuan Zhang 2026-02-15 01:27:31 +08:00 committed by GitHub
commit e08a1e8e89
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 120 additions and 10 deletions

View file

@ -97,3 +97,10 @@ RENDERER_VITE_API_URL=http://localhost:8080
# Read by core package via process.env.MULTICA_API_URL at runtime
# Production example: https://api.multica.ai
MULTICA_API_URL=http://localhost:8080
# SMC_DATA_DIR
# Root data directory override - Isolates dev data from production.
# When set, all data (sessions, credentials, profiles, app-state, etc.)
# is stored under this directory instead of ~/.super-multica.
# Supports ~ expansion. Set automatically by scripts/dev-local.sh.
# SMC_DATA_DIR=~/.super-multica-dev

View file

@ -13,12 +13,13 @@
"mu": "pnpm --filter @multica/cli dev",
"dev": "turbo build --filter=@multica/types --filter=@multica/utils --filter=@multica/core && concurrently -n types,utils,core,desktop -c blue,green,yellow,cyan \"pnpm --filter @multica/types dev\" \"pnpm --filter @multica/utils dev\" \"pnpm --filter @multica/core dev\" \"pnpm --filter @multica/desktop dev\"",
"dev:desktop": "pnpm --filter @multica/desktop dev",
"dev:desktop:reset": "rm -rf ~/.super-multica && echo '✓ Deleted ~/.super-multica - Fresh install state restored'",
"dev:desktop:reset": "rm -rf ~/.super-multica && rm -rf ~/.super-multica-dev && echo '✓ Deleted ~/.super-multica and ~/.super-multica-dev - Fresh install state restored'",
"dev:desktop:fresh": "pnpm dev:desktop:reset && pnpm dev:desktop",
"dev:desktop:onboarding": "pnpm --filter @multica/desktop dev:onboarding",
"dev:gateway": "pnpm --filter @multica/gateway dev",
"dev:web": "pnpm --filter @multica/web dev",
"dev:local": "bash scripts/dev-local.sh",
"dev:local:archive": "bash scripts/archive-dev-data.sh",
"dev:all": "concurrently \"pnpm dev:gateway\" \"pnpm dev:web\"",
"build": "turbo build",
"build:desktop": "pnpm --filter @multica/desktop build",

View file

@ -7,14 +7,11 @@
import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync, renameSync } from "fs";
import path from "path";
import { DATA_DIR } from "@multica/utils";
import type { CronJob, CronRunLogEntry } from "./types.js";
/** Default cron storage directory */
const DEFAULT_CRON_DIR = path.join(
process.env["HOME"] ?? ".",
".super-multica",
"cron",
);
const DEFAULT_CRON_DIR = path.join(DATA_DIR, "cron");
/** Store data structure */
type StoreData = {

View file

@ -0,0 +1,37 @@
import { describe, it, expect, afterEach } from "vitest";
import { join } from "node:path";
import { homedir } from "node:os";
import { resolveDataDir } from "./paths.js";
describe("resolveDataDir", () => {
const original = process.env.SMC_DATA_DIR;
afterEach(() => {
if (original === undefined) {
delete process.env.SMC_DATA_DIR;
} else {
process.env.SMC_DATA_DIR = original;
}
});
it("defaults to ~/.super-multica when SMC_DATA_DIR is not set", () => {
delete process.env.SMC_DATA_DIR;
expect(resolveDataDir()).toBe(join(homedir(), ".super-multica"));
});
it("uses absolute path from SMC_DATA_DIR", () => {
process.env.SMC_DATA_DIR = "/tmp/test-multica";
expect(resolveDataDir()).toBe("/tmp/test-multica");
});
it("expands ~ in SMC_DATA_DIR", () => {
process.env.SMC_DATA_DIR = "~/.super-multica-dev";
expect(resolveDataDir()).toBe(join(homedir(), ".super-multica-dev"));
});
it("handles ~ alone", () => {
process.env.SMC_DATA_DIR = "~";
expect(resolveDataDir()).toBe(homedir());
});
});

View file

@ -1,8 +1,23 @@
import { join } from "node:path";
import { homedir } from "node:os";
/** Root data directory: ~/.super-multica */
export const DATA_DIR = join(homedir(), ".super-multica");
/**
* Resolve the root data directory.
* Override with SMC_DATA_DIR env var (supports ~ expansion).
* Defaults to ~/.super-multica.
*/
export function resolveDataDir(): string {
const envDir = process.env.SMC_DATA_DIR;
if (envDir) {
return envDir.startsWith("~")
? join(homedir(), envDir.slice(1))
: envDir;
}
return join(homedir(), ".super-multica");
}
/** Root data directory (default: ~/.super-multica, override: SMC_DATA_DIR) */
export const DATA_DIR = resolveDataDir();
/** Cache directory for downloaded media files */
export const MEDIA_CACHE_DIR = join(DATA_DIR, "cache", "media");

42
scripts/archive-dev-data.sh Executable file
View file

@ -0,0 +1,42 @@
#!/usr/bin/env bash
#
# Archive and clean the dev environment data.
#
# Moves ~/.super-multica-dev and ~/Documents/Multica-dev into a
# timestamped archive directory for later debugging / analysis.
#
# Usage:
# pnpm dev:local:archive
#
# Archives are stored in: ~/.super-multica-dev-archives/<timestamp>/
set -euo pipefail
TIMESTAMP=$(date +"%Y%m%d-%H%M%S")
ARCHIVE_BASE="$HOME/.super-multica-dev-archives"
ARCHIVE_DIR="$ARCHIVE_BASE/$TIMESTAMP"
DEV_DATA="$HOME/.super-multica-dev"
DEV_WORKSPACE="$HOME/Documents/Multica-dev"
# Check if there's anything to archive
if [ ! -d "$DEV_DATA" ] && [ ! -d "$DEV_WORKSPACE" ]; then
echo "Nothing to archive — neither $DEV_DATA nor $DEV_WORKSPACE exists."
exit 0
fi
mkdir -p "$ARCHIVE_DIR"
if [ -d "$DEV_DATA" ]; then
mv "$DEV_DATA" "$ARCHIVE_DIR/data"
echo " Archived $DEV_DATA -> $ARCHIVE_DIR/data"
fi
if [ -d "$DEV_WORKSPACE" ]; then
mv "$DEV_WORKSPACE" "$ARCHIVE_DIR/workspace"
echo " Archived $DEV_WORKSPACE -> $ARCHIVE_DIR/workspace"
fi
echo ""
echo "Archived to: $ARCHIVE_DIR"
echo "Dev environment is now clean. Run 'pnpm dev:local' to start fresh."

View file

@ -36,6 +36,8 @@ echo "Starting local dev environment..."
echo " Gateway: http://localhost:4000 (Telegram long-polling mode)"
echo " Web: http://localhost:3000 (OAuth login)"
echo " Desktop: connecting to local Gateway + Web"
echo " Data dir: ~/.super-multica-dev (isolated from production)"
echo " Workspace: ~/Documents/Multica-dev (isolated from production)"
echo ""
# Build shared packages first
@ -49,6 +51,6 @@ exec pnpm concurrently \
"pnpm --filter @multica/types dev" \
"pnpm --filter @multica/utils dev" \
"pnpm --filter @multica/core dev" \
"PORT=4000 MULTICA_RUN_LOG=1 pnpm --filter @multica/gateway dev" \
"PORT=4000 SMC_DATA_DIR=~/.super-multica-dev MULTICA_WORKSPACE_DIR=~/Documents/Multica-dev MULTICA_RUN_LOG=1 pnpm --filter @multica/gateway dev" \
"pnpm --filter @multica/web dev" \
"GATEWAY_URL=http://localhost:4000 MAIN_VITE_WEB_URL=http://localhost:3000 MULTICA_RUN_LOG=1 pnpm --filter @multica/desktop dev"
"GATEWAY_URL=http://localhost:4000 MAIN_VITE_WEB_URL=http://localhost:3000 SMC_DATA_DIR=~/.super-multica-dev MULTICA_WORKSPACE_DIR=~/Documents/Multica-dev MULTICA_RUN_LOG=1 pnpm --filter @multica/desktop dev"

View file

@ -15,6 +15,15 @@ else
echo " $MULTICA_DATA_DIR does not exist, skipping"
fi
# Dev data directory (used by pnpm dev:local)
MULTICA_DEV_DIR="$HOME/.super-multica-dev"
if [ -d "$MULTICA_DEV_DIR" ]; then
echo " Removing $MULTICA_DEV_DIR"
rm -rf "$MULTICA_DEV_DIR"
else
echo " $MULTICA_DEV_DIR does not exist, skipping"
fi
# Electron app data (macOS)
if [[ "$OSTYPE" == "darwin"* ]]; then
ELECTRON_APP_DATA="$HOME/Library/Application Support/super-multica"