import { CONFIG_DIR_NAME } from "../config.ts"; import { emitProjectTrustEvent } from "./extensions/runner.ts"; import type { LoadExtensionsResult, ProjectTrustContext } from "./extensions/types.ts"; import type { DefaultProjectTrust } from "./settings-manager.ts"; import { getProjectTrustOptions, hasTrustRequiringProjectResources, type ProjectTrustOption, type ProjectTrustStore, } from "./trust-manager.ts"; export type AppMode = "interactive" | "print" | "json" | "rpc"; export interface ResolveProjectTrustedOptions { cwd: string; trustStore: ProjectTrustStore; trustOverride?: boolean; defaultProjectTrust?: DefaultProjectTrust; extensionsResult?: LoadExtensionsResult; projectTrustContext: ProjectTrustContext; onExtensionError?: (message: string) => void; } function formatProjectTrustPrompt(cwd: string): string { return `Trust project folder?\n${cwd}\n\nThis allows pi to load ${CONFIG_DIR_NAME} settings and resources, install missing project packages, and execute project extensions.`; } async function selectProjectTrustOption( cwd: string, ctx: ProjectTrustContext, ): Promise { const options = getProjectTrustOptions(cwd, { includeSessionOnly: true }); const selected = await ctx.ui.select( formatProjectTrustPrompt(cwd), options.map((option) => option.label), ); return options.find((option) => option.label === selected); } function saveProjectTrustPromptResult(trustStore: ProjectTrustStore, result: ProjectTrustOption): void { if (result.updates.length > 0) { trustStore.setMany(result.updates); } } export async function resolveProjectTrusted(options: ResolveProjectTrustedOptions): Promise { if (options.trustOverride !== undefined) { return options.trustOverride; } if (!hasTrustRequiringProjectResources(options.cwd)) { return true; } if (options.extensionsResult) { const { result, errors } = await emitProjectTrustEvent( options.extensionsResult, { type: "project_trust", cwd: options.cwd }, options.projectTrustContext, ); for (const error of errors) { options.onExtensionError?.(`Extension "${error.extensionPath}" project_trust error: ${error.error}`); } if (result) { const trusted = result.trusted === "yes"; if (result.remember === true) { options.trustStore.set(options.cwd, trusted); } return trusted; } } const decision = options.trustStore.get(options.cwd); if (decision !== null) { return decision; } switch (options.defaultProjectTrust ?? "ask") { case "always": return true; case "never": return false; case "ask": break; } if (!options.projectTrustContext.hasUI) { return false; } const selected = await selectProjectTrustOption(options.cwd, options.projectTrustContext); if (selected !== undefined) { saveProjectTrustPromptResult(options.trustStore, selected); return selected.trusted; } return false; }