fix(coding-agent): Clean up Path Handling (#4873)
This commit is contained in:
@@ -5,7 +5,6 @@
|
||||
|
||||
import * as fs from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import * as _bundledPiAgentCore from "@earendil-works/pi-agent-core";
|
||||
@@ -24,6 +23,7 @@ import { CONFIG_DIR_NAME, getAgentDir, isBunBinary } from "../../config.ts";
|
||||
// NOTE: This import works because loader.ts exports are NOT re-exported from index.ts,
|
||||
// avoiding a circular dependency. Extensions can import from @earendil-works/pi-coding-agent.
|
||||
import * as _bundledPiCodingAgent from "../../index.ts";
|
||||
import { resolvePath } from "../../utils/paths.ts";
|
||||
import { createEventBus, type EventBus } from "../event-bus.ts";
|
||||
import type { ExecOptions } from "../exec.ts";
|
||||
import { execCommand } from "../exec.ts";
|
||||
@@ -115,31 +115,6 @@ function getAliases(): Record<string, string> {
|
||||
return _aliases;
|
||||
}
|
||||
|
||||
const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
|
||||
|
||||
function normalizeUnicodeSpaces(str: string): string {
|
||||
return str.replace(UNICODE_SPACES, " ");
|
||||
}
|
||||
|
||||
function expandPath(p: string): string {
|
||||
const normalized = normalizeUnicodeSpaces(p);
|
||||
if (normalized.startsWith("~/")) {
|
||||
return path.join(os.homedir(), normalized.slice(2));
|
||||
}
|
||||
if (normalized.startsWith("~")) {
|
||||
return path.join(os.homedir(), normalized.slice(1));
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function resolvePath(extPath: string, cwd: string): string {
|
||||
const expanded = expandPath(extPath);
|
||||
if (path.isAbsolute(expanded)) {
|
||||
return expanded;
|
||||
}
|
||||
return path.resolve(cwd, expanded);
|
||||
}
|
||||
|
||||
type HandlerFn = (...args: unknown[]) => Promise<unknown>;
|
||||
|
||||
/**
|
||||
@@ -396,7 +371,7 @@ async function loadExtension(
|
||||
eventBus: EventBus,
|
||||
runtime: ExtensionRuntime,
|
||||
): Promise<{ extension: Extension | null; error: string | null }> {
|
||||
const resolvedPath = resolvePath(extensionPath, cwd);
|
||||
const resolvedPath = resolvePath(extensionPath, cwd, { normalizeUnicodeSpaces: true });
|
||||
|
||||
try {
|
||||
const factory = await loadExtensionModule(resolvedPath);
|
||||
@@ -426,7 +401,8 @@ export async function loadExtensionFromFactory(
|
||||
extensionPath = "<inline>",
|
||||
): Promise<Extension> {
|
||||
const extension = createExtension(extensionPath, extensionPath);
|
||||
const api = createExtensionAPI(extension, runtime, cwd, eventBus);
|
||||
const resolvedCwd = resolvePath(cwd);
|
||||
const api = createExtensionAPI(extension, runtime, resolvedCwd, eventBus);
|
||||
await factory(api);
|
||||
return extension;
|
||||
}
|
||||
@@ -437,11 +413,12 @@ export async function loadExtensionFromFactory(
|
||||
export async function loadExtensions(paths: string[], cwd: string, eventBus?: EventBus): Promise<LoadExtensionsResult> {
|
||||
const extensions: Extension[] = [];
|
||||
const errors: Array<{ path: string; error: string }> = [];
|
||||
const resolvedCwd = resolvePath(cwd);
|
||||
const resolvedEventBus = eventBus ?? createEventBus();
|
||||
const runtime = createExtensionRuntime();
|
||||
|
||||
for (const extPath of paths) {
|
||||
const { extension, error } = await loadExtension(extPath, cwd, resolvedEventBus, runtime);
|
||||
const { extension, error } = await loadExtension(extPath, resolvedCwd, resolvedEventBus, runtime);
|
||||
|
||||
if (error) {
|
||||
errors.push({ path: extPath, error });
|
||||
@@ -578,6 +555,8 @@ export async function discoverAndLoadExtensions(
|
||||
agentDir: string = getAgentDir(),
|
||||
eventBus?: EventBus,
|
||||
): Promise<LoadExtensionsResult> {
|
||||
const resolvedCwd = resolvePath(cwd);
|
||||
const resolvedAgentDir = resolvePath(agentDir);
|
||||
const allPaths: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
@@ -592,16 +571,16 @@ export async function discoverAndLoadExtensions(
|
||||
};
|
||||
|
||||
// 1. Project-local extensions: cwd/${CONFIG_DIR_NAME}/extensions/
|
||||
const localExtDir = path.join(cwd, CONFIG_DIR_NAME, "extensions");
|
||||
const localExtDir = path.join(resolvedCwd, CONFIG_DIR_NAME, "extensions");
|
||||
addPaths(discoverExtensionsInDir(localExtDir));
|
||||
|
||||
// 2. Global extensions: agentDir/extensions/
|
||||
const globalExtDir = path.join(agentDir, "extensions");
|
||||
const globalExtDir = path.join(resolvedAgentDir, "extensions");
|
||||
addPaths(discoverExtensionsInDir(globalExtDir));
|
||||
|
||||
// 3. Explicitly configured paths
|
||||
for (const p of configuredPaths) {
|
||||
const resolved = resolvePath(p, cwd);
|
||||
const resolved = resolvePath(p, resolvedCwd, { normalizeUnicodeSpaces: true });
|
||||
if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {
|
||||
// Check for package.json with pi manifest or index.ts
|
||||
const entries = resolveExtensionEntries(resolved);
|
||||
@@ -617,5 +596,5 @@ export async function discoverAndLoadExtensions(
|
||||
addPaths([resolved]);
|
||||
}
|
||||
|
||||
return loadExtensions(allPaths, cwd, eventBus);
|
||||
return loadExtensions(allPaths, resolvedCwd, eventBus);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user