@oai404iao/pi-codex-imagegen 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,73 @@
1
+ import { copyFile, mkdir, writeFile } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ import { randomUUID } from "node:crypto";
4
+ import { dirname, extname, isAbsolute, join, resolve } from "node:path";
5
+ import type { CodexMinimalToolsSettings } from "@oai404iao/pi-codex-runtime/internal/settings";
6
+
7
+ export interface SavedImageInfo {
8
+ path: string;
9
+ latestPath?: string;
10
+ mimeType: string;
11
+ format: string;
12
+ bytes: number;
13
+ }
14
+
15
+ export function projectRoot(cwd: string): string {
16
+ let current = resolve(cwd);
17
+ while (true) {
18
+ if (existsSync(join(current, ".git")) || existsSync(join(current, ".pi"))) return current;
19
+ const parent = dirname(current);
20
+ if (parent === current) return resolve(cwd);
21
+ current = parent;
22
+ }
23
+ }
24
+
25
+ export function imageOutputDir(cwd: string, settings: Pick<CodexMinimalToolsSettings, "imageOutputDir">): string {
26
+ const configured = settings.imageOutputDir || ".pi/openai-codex-images";
27
+ return isAbsolute(configured) ? resolve(configured) : resolve(projectRoot(cwd), configured);
28
+ }
29
+
30
+ export function imageFormatToMime(format: string | undefined): string {
31
+ const normalized = (format || "png").toLowerCase().replace(/^\./, "");
32
+ if (normalized === "jpg" || normalized === "jpeg") return "image/jpeg";
33
+ if (normalized === "webp") return "image/webp";
34
+ return "image/png";
35
+ }
36
+
37
+ export function extensionForMime(mimeType: string): string {
38
+ if (mimeType === "image/jpeg") return "jpeg";
39
+ if (mimeType === "image/webp") return "webp";
40
+ return "png";
41
+ }
42
+
43
+ export function inferImageFormat(value: unknown, fallback = "png"): string {
44
+ if (typeof value !== "string") return fallback;
45
+ const lower = value.toLowerCase();
46
+ if (lower.includes("jpeg") || lower.includes("jpg")) return "jpeg";
47
+ if (lower.includes("webp")) return "webp";
48
+ if (lower.includes("png")) return "png";
49
+ const ext = extname(lower).replace(/^\./, "");
50
+ return ext || fallback;
51
+ }
52
+
53
+ export async function saveBase64Image(options: {
54
+ base64: string;
55
+ callId?: string;
56
+ cwd: string;
57
+ format?: string;
58
+ responseId?: string;
59
+ settings: Pick<CodexMinimalToolsSettings, "imageOutputDir">;
60
+ }): Promise<SavedImageInfo> {
61
+ const format = inferImageFormat(options.format, "png");
62
+ const mimeType = imageFormatToMime(format);
63
+ const ext = extensionForMime(mimeType);
64
+ const dir = imageOutputDir(options.cwd, options.settings);
65
+ await mkdir(dir, { recursive: true });
66
+ const unique = randomUUID().slice(0, 8);
67
+ const filePath = join(dir, `${new Date().toISOString().replace(/[:.]/g, "-")}-${unique}.${ext}`);
68
+ const data = Buffer.from(options.base64, "base64");
69
+ await writeFile(filePath, data, { mode: 0o600 });
70
+ const latestPath = join(dir, `latest.${ext}`);
71
+ await copyFile(filePath, latestPath);
72
+ return { bytes: data.byteLength, format: ext, latestPath, mimeType, path: filePath };
73
+ }