fix(coding-agent): load photon via dynamic import

This commit is contained in:
Mario Zechner 2026-01-17 21:32:23 +01:00
parent 157e4e51bf
commit 94bd7f69fd
4 changed files with 28 additions and 37 deletions

View file

@ -10,6 +10,10 @@
- Added `ctx.compact()` and `ctx.getContextUsage()` to extension contexts for programmatic compaction and context usage checks. - Added `ctx.compact()` and `ctx.getContextUsage()` to extension contexts for programmatic compaction and context usage checks.
- Added documentation for delete word forward and kill ring keybindings in interactive mode. ([#810](https://github.com/badlogic/pi-mono/pull/810) by [@Perlence](https://github.com/Perlence)) - Added documentation for delete word forward and kill ring keybindings in interactive mode. ([#810](https://github.com/badlogic/pi-mono/pull/810) by [@Perlence](https://github.com/Perlence))
### Fixed
- Fixed photon module failing to load in ESM context with "require is not defined" error ([#795](https://github.com/badlogic/pi-mono/pull/795) by [@dannote](https://github.com/dannote))
## [0.48.0] - 2026-01-16 ## [0.48.0] - 2026-01-16
### Added ### Added

View file

@ -1,4 +1,4 @@
import { getPhoton } from "./photon.js"; import { loadPhoton } from "./photon.js";
/** /**
* Convert image to PNG format for terminal display. * Convert image to PNG format for terminal display.
@ -13,14 +13,15 @@ export async function convertToPng(
return { data: base64Data, mimeType }; return { data: base64Data, mimeType };
} }
const photon = getPhoton(); const photon = await loadPhoton();
if (!photon) { if (!photon) {
// Photon not available, can't convert // Photon not available, can't convert
return null; return null;
} }
try { try {
const image = photon.PhotonImage.new_from_byteslice(new Uint8Array(Buffer.from(base64Data, "base64"))); const bytes = new Uint8Array(Buffer.from(base64Data, "base64"));
const image = photon.PhotonImage.new_from_byteslice(bytes);
try { try {
const pngBuffer = image.get_bytes(); const pngBuffer = image.get_bytes();
return { return {

View file

@ -1,5 +1,5 @@
import type { ImageContent } from "@mariozechner/pi-ai"; import type { ImageContent } from "@mariozechner/pi-ai";
import { getPhoton } from "./photon.js"; import { loadPhoton } from "./photon.js";
export interface ImageResizeOptions { export interface ImageResizeOptions {
maxWidth?: number; // Default: 2000 maxWidth?: number; // Default: 2000
@ -53,7 +53,7 @@ export async function resizeImage(img: ImageContent, options?: ImageResizeOption
const opts = { ...DEFAULT_OPTIONS, ...options }; const opts = { ...DEFAULT_OPTIONS, ...options };
const inputBuffer = Buffer.from(img.data, "base64"); const inputBuffer = Buffer.from(img.data, "base64");
const photon = getPhoton(); const photon = await loadPhoton();
if (!photon) { if (!photon) {
// Photon not available, return original image // Photon not available, return original image
return { return {

View file

@ -8,8 +8,8 @@
* The challenge: photon-node's CJS entry uses fs.readFileSync(__dirname + '/photon_rs_bg.wasm') * The challenge: photon-node's CJS entry uses fs.readFileSync(__dirname + '/photon_rs_bg.wasm')
* which bakes the build machine's absolute path into Bun compiled binaries. * which bakes the build machine's absolute path into Bun compiled binaries.
* *
* Solution: Lazy-load photon and gracefully handle failures. Image processing functions * Solution: Lazy-load photon via dynamic import and gracefully handle failures.
* already have fallbacks that return original images when photon isn't available. * Image processing functions have fallbacks that return original images when photon isn't available.
*/ */
// Re-export types from the main package // Re-export types from the main package
@ -17,43 +17,29 @@ export type { PhotonImage as PhotonImageType } from "@silvia-odwyer/photon-node"
// Lazy-loaded photon module // Lazy-loaded photon module
let photonModule: typeof import("@silvia-odwyer/photon-node") | null = null; let photonModule: typeof import("@silvia-odwyer/photon-node") | null = null;
let loadAttempted = false; let loadPromise: Promise<typeof import("@silvia-odwyer/photon-node") | null> | null = null;
let loadError: Error | null = null;
/** /**
* Get the photon module, loading it lazily on first access. * Load the photon module asynchronously.
* Returns null if loading fails (e.g., in broken Bun binary). * Returns cached module on subsequent calls.
*/ */
export function getPhoton(): typeof import("@silvia-odwyer/photon-node") | null { export async function loadPhoton(): Promise<typeof import("@silvia-odwyer/photon-node") | null> {
if (loadAttempted) { if (photonModule) {
return photonModule; return photonModule;
} }
loadAttempted = true; if (loadPromise) {
return loadPromise;
}
loadPromise = (async () => {
try { try {
// Dynamic require to defer loading until actually needed photonModule = await import("@silvia-odwyer/photon-node");
// This also allows the error to be caught gracefully } catch {
photonModule = require("@silvia-odwyer/photon-node");
} catch (e) {
loadError = e as Error;
photonModule = null; photonModule = null;
} }
return photonModule; return photonModule;
} })();
/** return loadPromise;
* Check if photon is available and working.
*/
export function isPhotonAvailable(): boolean {
return getPhoton() !== null;
}
/**
* Get the error that occurred during photon loading, if any.
*/
export function getPhotonLoadError(): Error | null {
getPhoton(); // Ensure load was attempted
return loadError;
} }