feat(signal): auto-detect persistent storage for containerized deployments
Add setupSignalDataPersistence() to automatically symlink signal-cli data directory to persistent storage when running in containers. Priority order: 1. SIGNAL_CLI_DATA_DIR env var 2. /data/.local/share/signal-cli (Fly.io convention) 3. /var/lib/clawdbot/.local/share/signal-cli (alternative) 4. Default ~/.local/share/signal-cli (no symlink) This ensures Signal account data survives container restarts without requiring manual symlink setup or environment configuration. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
9daa846457
commit
d62b98c5c8
@ -1,4 +1,7 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { existsSync, mkdirSync, symlinkSync, lstatSync } from "node:fs";
|
||||
import { homedir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
|
||||
export type SignalDaemonOpts = {
|
||||
@ -18,6 +21,84 @@ export type SignalDaemonHandle = {
|
||||
stop: () => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup Signal data directory for containerized environments.
|
||||
* Auto-detects persistent storage and creates symlink if needed.
|
||||
*
|
||||
* Priority order:
|
||||
* 1. SIGNAL_CLI_DATA_DIR env var (if set)
|
||||
* 2. /data/.local/share/signal-cli (Fly.io convention)
|
||||
* 3. /var/lib/clawdbot/.local/share/signal-cli (alternative persistent location)
|
||||
* 4. Default: ~/.local/share/signal-cli (no symlink needed)
|
||||
*/
|
||||
export function setupSignalDataPersistence(runtime?: RuntimeEnv): void {
|
||||
const log = runtime?.log ?? (() => {});
|
||||
const defaultPath = join(homedir(), ".local", "share", "signal-cli");
|
||||
|
||||
// Check if already a symlink or regular directory exists
|
||||
if (existsSync(defaultPath)) {
|
||||
try {
|
||||
const stats = lstatSync(defaultPath);
|
||||
if (stats.isSymbolicLink()) {
|
||||
log(`signal-cli data already symlinked to persistent storage`);
|
||||
return;
|
||||
}
|
||||
// Regular directory exists, don't touch it
|
||||
return;
|
||||
} catch {
|
||||
// Ignore errors, will try to create below
|
||||
}
|
||||
}
|
||||
|
||||
// Determine persistent storage location
|
||||
let persistentPath: string | null = null;
|
||||
|
||||
// Priority 1: Environment variable
|
||||
if (process.env.SIGNAL_CLI_DATA_DIR) {
|
||||
persistentPath = process.env.SIGNAL_CLI_DATA_DIR;
|
||||
log(`signal-cli: using SIGNAL_CLI_DATA_DIR=${persistentPath}`);
|
||||
}
|
||||
// Priority 2: /data (Fly.io convention)
|
||||
else if (existsSync("/data")) {
|
||||
persistentPath = "/data/.local/share/signal-cli";
|
||||
}
|
||||
// Priority 3: /var/lib/clawdbot
|
||||
else if (existsSync("/var/lib/clawdbot")) {
|
||||
persistentPath = "/var/lib/clawdbot/.local/share/signal-cli";
|
||||
}
|
||||
|
||||
// No persistent storage detected, use default
|
||||
if (!persistentPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create persistent directory structure
|
||||
try {
|
||||
mkdirSync(persistentPath, { recursive: true });
|
||||
mkdirSync(join(persistentPath, "data"), { recursive: true });
|
||||
} catch (err) {
|
||||
runtime?.error?.(`signal-cli: failed to create persistent directory: ${String(err)}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create parent directory for symlink
|
||||
try {
|
||||
const parentDir = join(homedir(), ".local", "share");
|
||||
mkdirSync(parentDir, { recursive: true });
|
||||
} catch (err) {
|
||||
runtime?.error?.(`signal-cli: failed to create parent directory: ${String(err)}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create symlink
|
||||
try {
|
||||
symlinkSync(persistentPath, defaultPath);
|
||||
log(`signal-cli: linked ${defaultPath} -> ${persistentPath} for persistence`);
|
||||
} catch (err) {
|
||||
runtime?.error?.(`signal-cli: failed to create symlink: ${String(err)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function classifySignalCliLogLine(line: string): "log" | "error" | null {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) return null;
|
||||
@ -48,6 +129,9 @@ function buildDaemonArgs(opts: SignalDaemonOpts): string[] {
|
||||
}
|
||||
|
||||
export function spawnSignalDaemon(opts: SignalDaemonOpts): SignalDaemonHandle {
|
||||
// Setup persistent storage for containerized environments
|
||||
setupSignalDataPersistence(opts.runtime);
|
||||
|
||||
const args = buildDaemonArgs(opts);
|
||||
const child = spawn(opts.cliPath, args, {
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
|
||||
Loading…
Reference in New Issue
Block a user