Files
oh-my-opencode/src/tools/council-archive/council-finalize-helpers.ts
T

57 lines
2.2 KiB
TypeScript

import { readFile, writeFile, rename, unlink } from "node:fs/promises"
import { join, isAbsolute, resolve, relative } from "node:path"
import { log } from "../../shared/logger"
export const TASK_ID_PATTERN = /^[a-zA-Z0-9_-]+$/
export function slugify(text: string): string {
return text
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "")
}
export function toPosixPath(pathValue: string): string {
return pathValue.replace(/\\/g, "/")
}
export function extractAgentFromFrontmatter(content: string): string | null {
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/)
if (!fmMatch) return null
const agentLine = fmMatch[1].match(/^agent:\s*(.+)$/m)
return agentLine ? agentLine[1].trim() : null
}
export function isPathEscaping(expectedRoot: string, targetPath: string): boolean {
const rel = relative(expectedRoot, targetPath)
return rel === ".." || rel.startsWith("../") || rel.startsWith("..\\") || isAbsolute(rel)
}
export async function movePromptFile(
promptFilePath: string,
base: string,
absArchiveDir: string,
relArchiveDir: string,
): Promise<string | undefined> {
try {
const promptFilename = "council-prompt.md"
const absPromptSrc = isAbsolute(promptFilePath) ? promptFilePath : resolve(base, promptFilePath)
const expectedPromptRoot = join(base, ".sisyphus", "tmp")
if (isPathEscaping(expectedPromptRoot, absPromptSrc)) {
log("[council-finalize] Rejected prompt_file outside .sisyphus/tmp/", { promptFile: promptFilePath })
return undefined
}
const absPromptDest = join(absArchiveDir, promptFilename)
await rename(absPromptSrc, absPromptDest).catch(async (renameErr) => {
log("[council-finalize] Rename failed, falling back to copy", { promptFile: promptFilePath, error: String(renameErr) })
const content = await readFile(absPromptSrc, "utf-8")
await writeFile(absPromptDest, content, "utf-8")
await unlink(absPromptSrc).catch((err) => { log("[council-finalize] Failed to delete prompt source file", { error: String(err) }) })
})
return toPosixPath(join(relArchiveDir, promptFilename))
} catch (err) {
log("[council-finalize] Failed to move prompt file", { promptFile: promptFilePath, error: String(err) })
return undefined
}
}