Files
2026-07-13 12:52:40 +08:00

157 lines
4.6 KiB
TypeScript

import { execFileSync } from "node:child_process";
import {
existsSync,
mkdirSync,
readdirSync,
readFileSync,
writeFileSync,
} from "node:fs";
import { homedir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { resolveSessionDataDir } from "@cline/shared/storage";
import type { JsonRecord } from "./types";
// ---------------------------------------------------------------------------
// Workspace root
// ---------------------------------------------------------------------------
export function resolveWorkspaceRoot(launchCwd: string): string {
const cwd = resolve(launchCwd);
try {
const root = execFileSync("git", ["rev-parse", "--show-toplevel"], {
cwd,
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"],
})
.trim()
.replace(/[\\/]+$/, "");
return root || cwd;
} catch {
return cwd;
}
}
// ---------------------------------------------------------------------------
// Shared session data directories
// ---------------------------------------------------------------------------
export function sharedSessionDataDir(): string {
return process.env.CLINE_SESSION_DATA_DIR?.trim() || resolveSessionDataDir();
}
export function sharedSessionArtifactPath(
sessionId: string,
suffix: string,
): string {
return join(sharedSessionDataDir(), sessionId, `${sessionId}.${suffix}`);
}
export function sharedSessionMessagesPath(sessionId: string): string {
return sharedSessionArtifactPath(sessionId, "messages.json");
}
export function sharedSessionMessagesWritePath(sessionId: string): string {
return sharedSessionMessagesPath(sessionId);
}
// ---------------------------------------------------------------------------
// Tool approval directory (kept for file cleanup compatibility)
// ---------------------------------------------------------------------------
export function toolApprovalDir(): string {
return (
process.env.CLINE_TOOL_APPROVAL_DIR?.trim() ||
join(sharedSessionDataDir(), "tool-approvals")
);
}
// ---------------------------------------------------------------------------
// MCP settings
// ---------------------------------------------------------------------------
export function resolveMcpSettingsPath(): string {
return (
process.env.CLINE_MCP_SETTINGS_PATH?.trim() ||
join(homedir(), ".cline", "data", "settings", "cline_mcp_settings.json")
);
}
// ---------------------------------------------------------------------------
// Session log paths (kanban-style)
// ---------------------------------------------------------------------------
function kanbanDataRoot(): string {
return (
process.env.CLINE_KANBAN_DATA_DIR?.trim() ||
join(homedir(), ".cline", "apps", "kanban")
);
}
export function sessionLogPath(sessionId: string): string {
return join(kanbanDataRoot(), "sessions", `${sessionId}.jsonl`);
}
// ---------------------------------------------------------------------------
// Session ID helpers
// ---------------------------------------------------------------------------
export function rootSessionIdFrom(sessionId: string): string {
return sessionId.split("__")[0] || sessionId;
}
// ---------------------------------------------------------------------------
// Artifact discovery
// ---------------------------------------------------------------------------
export function findArtifactUnderDir(
dir: string,
fileName: string,
maxDepth: number,
): string | null {
if (!existsSync(dir)) {
return null;
}
const stack: Array<{ dir: string; depth: number }> = [{ dir, depth: 0 }];
while (stack.length > 0) {
const current = stack.pop();
if (!current) {
break;
}
for (const entry of readdirSync(current.dir, { withFileTypes: true })) {
const path = join(current.dir, entry.name);
if (entry.isFile() && entry.name === fileName) {
return path;
}
if (entry.isDirectory() && current.depth < maxDepth) {
stack.push({ dir: path, depth: current.depth + 1 });
}
}
}
return null;
}
// ---------------------------------------------------------------------------
// Session manifest read/write
// ---------------------------------------------------------------------------
export function readSessionManifest(sessionId: string): JsonRecord | null {
const path = join(sharedSessionDataDir(), sessionId, `${sessionId}.json`);
if (!existsSync(path)) {
return null;
}
try {
return JSON.parse(readFileSync(path, "utf8")) as JsonRecord;
} catch {
return null;
}
}
export function writeSessionManifest(
sessionId: string,
manifest: JsonRecord,
): void {
const path = join(sharedSessionDataDir(), sessionId, `${sessionId}.json`);
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, `${JSON.stringify(manifest, null, 2)}\n`);
}