codexHome.ts 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243
  1. import { mkdir } from 'node:fs/promises';
  2. import { join, resolve, sep } from 'node:path';
  3. import { getDataDir } from 'ee-core/ps';
  4. /** Codex 的 HOME:配置(config.toml)、skills、会话记录都落在这里 */
  5. export function getCodexHome(): string {
  6. return join(getDataDir(), 'codex-home');
  7. }
  8. /** 用户自装 skill 的目录;内置 skill 在同级的 .system/.curated/.experimental 下,不允许删改 */
  9. export function getSkillsDir(): string {
  10. return join(getCodexHome(), 'skills');
  11. }
  12. /** 我方自己的运行数据目录,刻意放在 CODEX_HOME 之外,避免被 Codex 当成项目内容扫描 */
  13. export function getCodexDataDir(): string {
  14. return join(getDataDir(), 'codex-data');
  15. }
  16. export function getEventsDir(): string {
  17. return join(getCodexDataDir(), 'events');
  18. }
  19. export async function ensureCodexHome(): Promise<string> {
  20. const codexHome = getCodexHome();
  21. // 0700:目录里有 config.toml 与用户装的 skill,不给同机其它账户读
  22. await mkdir(codexHome, { recursive: true, mode: 0o700 });
  23. await mkdir(getSkillsDir(), { recursive: true });
  24. await mkdir(getEventsDir(), { recursive: true });
  25. return codexHome;
  26. }
  27. /** 目录名合法性:小写连字符,最长 64 */
  28. export const SKILL_NAME_PATTERN = /^[a-z0-9]+(-[a-z0-9]+){0,9}$/u;
  29. export const MAX_SKILL_NAME_LENGTH = 64;
  30. /** 路径包含判断,用于拦住 skill 安装/删除时的越界与 `..` 穿越 */
  31. export function isInsideDir(parentDir: string, target: string): boolean {
  32. const parent = resolve(parentDir);
  33. const child = resolve(target);
  34. if (child === parent) return false;
  35. return child.startsWith(parent.endsWith(sep) ? parent : parent + sep);
  36. }