mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:20:43 +00:00
[codex] Extract filesystem safety primitives (#77918)
* refactor: extract filesystem safety primitives * refactor: use fs-safe for file access helpers * refactor: reuse fs-safe for media reads * refactor: use fs-safe for image reads * refactor: reuse fs-safe in qqbot media opener * refactor: reuse fs-safe for local media checks * refactor: consume cleaner fs-safe api * refactor: align fs-safe json option names * fix: preserve fs-safe migration contracts * refactor: use fs-safe primitive subpaths * refactor: use grouped fs-safe subpaths * refactor: align fs-safe api usage * refactor: adapt private state store api * chore: refresh proof gate * refactor: follow fs-safe json api split * refactor: follow reduced fs-safe surface * build: default fs-safe python helper off * fix: preserve fs-safe plugin sdk aliases * refactor: consolidate fs-safe usage * refactor: unify fs-safe store usage * refactor: trim fs-safe temp workspace usage * refactor: hide low-level fs-safe primitives * build: use published fs-safe package * fix: preserve outbound recovery durability after rebase * chore: refresh pr checks
This commit is contained in:
committed by
GitHub
parent
61481eb34f
commit
538605ff44
@@ -1,6 +1,7 @@
|
||||
import crypto from "node:crypto";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { root as fsRoot } from "openclaw/plugin-sdk/security-runtime";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import type { PluginLogger } from "../api.js";
|
||||
import type { DiffArtifactContext, DiffArtifactMeta, DiffOutputFormat } from "./types.js";
|
||||
@@ -36,6 +37,7 @@ type StandaloneFileMeta = {
|
||||
};
|
||||
|
||||
type ArtifactMetaFileName = "meta.json" | "file-meta.json";
|
||||
type ArtifactRoot = Awaited<ReturnType<typeof fsRoot>>;
|
||||
|
||||
export class DiffArtifactStore {
|
||||
private readonly rootDir: string;
|
||||
@@ -76,8 +78,9 @@ export class DiffArtifactStore {
|
||||
...(params.context ? { context: params.context } : {}),
|
||||
};
|
||||
|
||||
await fs.mkdir(artifactDir, { recursive: true });
|
||||
await fs.writeFile(htmlPath, params.html, "utf8");
|
||||
const root = await this.artifactRoot();
|
||||
await root.mkdir(id);
|
||||
await root.write(path.posix.join(id, "viewer.html"), params.html);
|
||||
await this.writeMeta(meta);
|
||||
this.scheduleCleanup();
|
||||
return meta;
|
||||
@@ -104,7 +107,7 @@ export class DiffArtifactStore {
|
||||
throw new Error(`Diff artifact not found: ${id}`);
|
||||
}
|
||||
const htmlPath = this.normalizeStoredPath(meta.htmlPath, "htmlPath");
|
||||
return await fs.readFile(htmlPath, "utf8");
|
||||
return await (await this.artifactRoot()).readText(this.relativeStoredPath(htmlPath));
|
||||
}
|
||||
|
||||
async updateFilePath(id: string, filePath: string): Promise<DiffArtifactMeta> {
|
||||
@@ -151,7 +154,7 @@ export class DiffArtifactStore {
|
||||
...(params.context ? { context: params.context } : {}),
|
||||
};
|
||||
|
||||
await fs.mkdir(artifactDir, { recursive: true });
|
||||
await (await this.artifactRoot()).mkdir(id);
|
||||
await this.writeStandaloneMeta(meta);
|
||||
this.scheduleCleanup();
|
||||
return {
|
||||
@@ -212,6 +215,11 @@ export class DiffArtifactStore {
|
||||
await fs.mkdir(this.rootDir, { recursive: true });
|
||||
}
|
||||
|
||||
private async artifactRoot(): Promise<ArtifactRoot> {
|
||||
await this.ensureRoot();
|
||||
return await fsRoot(this.rootDir);
|
||||
}
|
||||
|
||||
private maybeCleanupExpired(): void {
|
||||
const now = Date.now();
|
||||
if (this.cleanupInFlight || now < this.nextCleanupAt) {
|
||||
@@ -283,16 +291,12 @@ export class DiffArtifactStore {
|
||||
}
|
||||
}
|
||||
|
||||
private metaFilePath(id: string, fileName: ArtifactMetaFileName): string {
|
||||
return path.join(this.artifactDir(id), fileName);
|
||||
}
|
||||
|
||||
private async writeJsonMeta(
|
||||
id: string,
|
||||
fileName: ArtifactMetaFileName,
|
||||
data: unknown,
|
||||
): Promise<void> {
|
||||
await fs.writeFile(this.metaFilePath(id, fileName), JSON.stringify(data, null, 2), "utf8");
|
||||
await (await this.artifactRoot()).writeJson(path.posix.join(id, fileName), data, { space: 2 });
|
||||
}
|
||||
|
||||
private async readJsonMeta(
|
||||
@@ -301,7 +305,7 @@ export class DiffArtifactStore {
|
||||
context: string,
|
||||
): Promise<unknown> {
|
||||
try {
|
||||
const raw = await fs.readFile(this.metaFilePath(id, fileName), "utf8");
|
||||
const raw = await (await this.artifactRoot()).readText(path.posix.join(id, fileName));
|
||||
return JSON.parse(raw) as unknown;
|
||||
} catch (error) {
|
||||
if (isFileNotFound(error)) {
|
||||
@@ -330,6 +334,11 @@ export class DiffArtifactStore {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private relativeStoredPath(storedPath: string): string {
|
||||
const relativePath = path.relative(this.rootDir, this.normalizeStoredPath(storedPath, "path"));
|
||||
return relativePath.split(path.sep).join(path.posix.sep);
|
||||
}
|
||||
|
||||
private assertWithinRoot(candidate: string, label = "path"): void {
|
||||
const relative = path.relative(this.rootDir, candidate);
|
||||
if (
|
||||
@@ -362,7 +371,8 @@ function isExpired(meta: { expiresAt: string }): boolean {
|
||||
}
|
||||
|
||||
function isFileNotFound(error: unknown): boolean {
|
||||
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
||||
const code = error instanceof Error && "code" in error ? error.code : undefined;
|
||||
return code === "ENOENT" || code === "not-found";
|
||||
}
|
||||
|
||||
function normalizeArtifactContext(value: unknown): DiffArtifactContext | undefined {
|
||||
|
||||
Reference in New Issue
Block a user