mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-15 18:30:40 +00:00
checkForUpdates() previously fetched the latest version from GitHub on every single CLI invocation, blocking for up to 10s on slow/offline connections. Now it writes a timestamp to ~/.config/spawn/.update-checked after a successful check and skips the network call if the cache is less than 1 hour old. Co-authored-by: Claude <claude@anthropic.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: L <6723574+louisgv@users.noreply.github.com>
99 lines
3.6 KiB
TypeScript
99 lines
3.6 KiB
TypeScript
// shared/paths.ts — Centralized filesystem path resolution
|
|
//
|
|
// All path helpers live here. Production code imports from this module;
|
|
// no other module should call homedir() or construct spawn-specific paths directly.
|
|
|
|
import { homedir, tmpdir } from "node:os";
|
|
import { isAbsolute, join, resolve } from "node:path";
|
|
|
|
/** Return the user's home directory, preferring $HOME over os.homedir(). */
|
|
export function getUserHome(): string {
|
|
return process.env.HOME || homedir();
|
|
}
|
|
|
|
/** Returns the directory for spawn data, respecting SPAWN_HOME env var.
|
|
* SPAWN_HOME must be an absolute path if set; relative paths are rejected
|
|
* to prevent unintended file writes. */
|
|
export function getSpawnDir(): string {
|
|
const spawnHome = process.env.SPAWN_HOME;
|
|
if (!spawnHome) {
|
|
return join(getUserHome(), ".spawn");
|
|
}
|
|
// Require absolute path to prevent path traversal via relative paths
|
|
if (!isAbsolute(spawnHome)) {
|
|
throw new Error(
|
|
`SPAWN_HOME must be an absolute path (got "${spawnHome}").\n` + "Example: export SPAWN_HOME=/home/user/.spawn",
|
|
);
|
|
}
|
|
// Resolve to canonical form (collapses .. segments)
|
|
const resolved = resolve(spawnHome);
|
|
|
|
// SECURITY: Prevent path traversal to system directories
|
|
// Even though the path is absolute, resolve() can normalize paths like
|
|
// /tmp/../../root/.spawn to /root/.spawn, potentially allowing unauthorized
|
|
// file writes to sensitive directories.
|
|
const userHome = getUserHome();
|
|
if (!resolved.startsWith(userHome + "/") && resolved !== userHome) {
|
|
throw new Error("SPAWN_HOME must be within your home directory.\n" + `Got: ${resolved}\n` + `Home: ${userHome}`);
|
|
}
|
|
|
|
return resolved;
|
|
}
|
|
|
|
/** Path to the spawn history file. */
|
|
export function getHistoryPath(): string {
|
|
return join(getSpawnDir(), "history.json");
|
|
}
|
|
|
|
/**
|
|
* Return the path to the per-cloud config file: ~/.config/spawn/{cloud}.json
|
|
* Shared by all cloud modules to avoid repeating the same path construction.
|
|
*/
|
|
export function getSpawnCloudConfigPath(cloud: string): string {
|
|
return join(getUserHome(), ".config", "spawn", `${cloud}.json`);
|
|
}
|
|
|
|
/** Return the path to the spawn preferences file: ~/.config/spawn/preferences.json */
|
|
export function getSpawnPreferencesPath(): string {
|
|
return join(getUserHome(), ".config", "spawn", "preferences.json");
|
|
}
|
|
|
|
/** Return the cache directory for spawn, respecting XDG_CACHE_HOME. */
|
|
export function getCacheDir(): string {
|
|
return join(process.env.XDG_CACHE_HOME || join(getUserHome(), ".cache"), "spawn");
|
|
}
|
|
|
|
/** Return the path to the cached manifest file. */
|
|
export function getCacheFile(): string {
|
|
return join(getCacheDir(), "manifest.json");
|
|
}
|
|
|
|
/** Return the path to the update-failed sentinel file. */
|
|
export function getUpdateFailedPath(): string {
|
|
return join(getUserHome(), ".config", "spawn", ".update-failed");
|
|
}
|
|
|
|
/** Return the path to the last-successful-update-check sentinel file. */
|
|
export function getUpdateCheckedPath(): string {
|
|
return join(getUserHome(), ".config", "spawn", ".update-checked");
|
|
}
|
|
|
|
/** Return the path to the user's ~/.ssh directory. */
|
|
export function getSshDir(): string {
|
|
return join(getUserHome(), ".ssh");
|
|
}
|
|
|
|
/** Return the system temp directory (wraps os.tmpdir()). */
|
|
export function getTmpDir(): string {
|
|
return tmpdir();
|
|
}
|
|
|
|
/**
|
|
* Shell RC marker comments used by install.sh and uninstall.ts.
|
|
* Keep in sync with sh/cli/install.sh — both files use these exact strings.
|
|
*/
|
|
export const RC_MARKER_START = "# >>> spawn >>>";
|
|
export const RC_MARKER_END = "# <<< spawn <<<";
|
|
|
|
/** Legacy single-line marker written by installer versions before start/end markers. */
|
|
export const RC_MARKER_LEGACY = "# Added by spawn installer";
|