1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2// SPDX-FileCopyrightText: Petr Baudis <pasky@ucw.cz>
3//
4// SPDX-License-Identifier: MIT
5
6import * as fs from "node:fs";
7import * as os from "node:os";
8import * as path from "node:path";
9
10export function getSessionsRoot(sessionFile: string | undefined): string | undefined {
11 if (!sessionFile) return undefined;
12 const normalized = sessionFile.replace(/\\/g, "/");
13 const marker = "/sessions/";
14 const idx = normalized.indexOf(marker);
15 if (idx === -1) {
16 return path.dirname(path.resolve(sessionFile));
17 }
18 return normalized.slice(0, idx + marker.length - 1);
19}
20
21export function getFallbackSessionsRoot(): string | undefined {
22 const configuredDir = process.env.PI_CODING_AGENT_DIR;
23 const candidate = configuredDir
24 ? path.resolve(configuredDir, "sessions")
25 : path.resolve(os.homedir(), ".pi", "agent", "sessions");
26 return fs.existsSync(candidate) ? candidate : undefined;
27}
28
29export function normalizeSessionPath(sessionPath: string, sessionsRoot: string | undefined): string {
30 if (path.isAbsolute(sessionPath)) return path.resolve(sessionPath);
31 if (sessionsRoot) return path.resolve(sessionsRoot, sessionPath);
32 return path.resolve(sessionPath);
33}
34
35export function sessionPathAllowed(candidate: string, sessionsRoot: string | undefined): boolean {
36 if (!sessionsRoot) return false; // fail closed when root unknown
37 const root = path.resolve(sessionsRoot);
38 const resolved = path.resolve(candidate);
39 return resolved === root || resolved.startsWith(`${root}${path.sep}`);
40}