import fs from "node:fs";import os from "node:os";import path from "node:path";import {resolveEffectiveHomeDir,resolveHomeRelativePath,resolveRequiredHomeDir,} from "./infra/home-dir.js";import { isPlainObject } from "./infra/plain-object.js";import { formatTerminalLink } from "./terminal/terminal-link.js";export async function ensureDir(dir: string) {await fs.promises.mkdir(dir, { recursive: true });}/*** Check if a file or directory exists at the given path.*/export async function pathExists(targetPath: string): Promise<boolean> {try {await fs.promises.access(targetPath);return true;} catch {return false;}}export function clampNumber(value: number, min: number, max: number): number {return Math.max(min, Math.min(max, value));}export function clampInt(value: number, min: number, max: number): number {return clampNumber(Math.floor(value), min, max);}/** Alias for clampNumber (shorter, more common name) */export const clamp = clampNumber;/*** Escapes special regex characters in a string so it can be used in a RegExp constructor.*/export function escapeRegExp(value: string): string {return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");}/*** Safely parse JSON, returning null on error instead of throwing.*/export function safeParseJson<T>(raw: string): T | null {try {return JSON.parse(raw) as T;} catch {return null;}}export { formatTerminalLink, isPlainObject };/*** Type guard for Record<string, unknown> (less strict than isPlainObject).* Accepts any non-null object that isn't an array.*/export function isRecord(value: unknown): value is Record<string, unknown> {return typeof value === "object" && value !== null && !Array.isArray(value);}export function normalizeE164(number: string): string {const withoutPrefix = number.replace(/^[a-z][a-z0-9-]*:/i, "").trim();const digits = withoutPrefix.replace(/[^\d+]/g, "");if (digits.startsWith("+")) {return `+${digits.slice(1)}`;}return `+${digits}`;}export function sleep(ms: number) {return new Promise((resolve) => setTimeout(resolve, ms));}function isHighSurrogate(codeUnit: number): boolean {return codeUnit >= 0xd800 && codeUnit <= 0xdbff;}function isLowSurrogate(codeUnit: number): boolean {return codeUnit >= 0xdc00 && codeUnit <= 0xdfff;}export function sliceUtf16Safe(input: string, start: number, end?: number): string {const len = input.length;let from = start < 0 ? Math.max(len + start, 0) : Math.min(start, len);let to = end === undefined ? len : end < 0 ? Math.max(len + end, 0) : Math.min(end, len);if (to < from) {const tmp = from;from = to;to = tmp;}if (from > 0 && from < len) {const codeUnit = input.charCodeAt(from);if (isLowSurrogate(codeUnit) && isHighSurrogate(input.charCodeAt(from - 1))) {from += 1;}}if (to > 0 && to < len) {const codeUnit = input.charCodeAt(to - 1);if (isHighSurrogate(codeUnit) && isLowSurrogate(input.charCodeAt(to))) {to -= 1;}}return input.slice(from, to);}export function truncateUtf16Safe(input: string, maxLen: number): string {const limit = Math.max(0, Math.floor(maxLen));if (input.length <= limit) {return input;}return sliceUtf16Safe(input, 0, limit);}export function resolveUserPath(input: string,env: NodeJS.ProcessEnv = process.env,homedir: () => string = os.homedir,): string {if (!input) {return "";}return resolveHomeRelativePath(input, { env, homedir });}export function resolveConfigDir(env: NodeJS.ProcessEnv = process.env,homedir: () => string = os.homedir,): string {const override = env.OPENCLAW_STATE_DIR?.trim();if (override) {return resolveUserPath(override, env, homedir);}const newDir = path.join(resolveRequiredHomeDir(env, homedir), ".openclaw");try {const hasNew = fs.existsSync(newDir);if (hasNew) {return newDir;}} catch {// best-effort}return newDir;}export function resolveHomeDir(): string | undefined {return resolveEffectiveHomeDir(process.env, os.homedir);}function resolveHomeDisplayPrefix(): { home: string; prefix: string } | undefined {const home = resolveHomeDir();if (!home) {return undefined;}const explicitHome = process.env.OPENCLAW_HOME?.trim();if (explicitHome) {return { home, prefix: "$OPENCLAW_HOME" };}return { home, prefix: "~" };}export function shortenHomePath(input: string): string {if (!input) {return input;}const display = resolveHomeDisplayPrefix();if (!display) {return input;}const { home, prefix } = display;if (input === home) {return prefix;}if (input.startsWith(`${home}/`) || input.startsWith(`${home}\\`)) {return `${prefix}${input.slice(home.length)}`;}return input;}export function shortenHomeInString(input: string): string {if (!input) {return input;}const display = resolveHomeDisplayPrefix();if (!display) {return input;}return input.split(display.home).join(display.prefix);}export function displayPath(input: string): string {return shortenHomePath(input);}export function displayString(input: string): string {return shortenHomeInString(input);}// Configuration root; can be overridden via OPENCLAW_STATE_DIR.export const CONFIG_DIR = resolveConfigDir();
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。