157 lines
4.6 KiB
TypeScript
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`);
|
|
}
|