pi-mono/packages/coding-agent/src/core/project-trust.ts
Anders Bech Mellson ab0dc51fcf
fix(coding-agent): use APP_NAME in user-facing messages (#8067)
APP_NAME is derived from piConfig.name so a rebranded distribution shows
its own name, and roughly 90 call sites already use it. Twelve
user-facing strings hardcoded "pi" instead, so those messages named the
upstream binary regardless of piConfig.name:

- the trust project folder prompt
- "Restart pi for this to take effect." after a saved trust decision
- the "Start without extensions" hint on an extension load failure
- the invalid session file error
- the executable location in the self-update-unavailable notice
- the three auth command usage strings and the unknown auth command
  error, which are printed next to `${APP_NAME} --help` in main.ts and
  so contradicted it
- "is configured outside pi." in the provider setup dialog
- the uncaughtException line

Output is unchanged for pi itself: piConfig has no name, so APP_NAME
falls back to "pi".
2026-08-14 11:26:20 +02:00

96 lines
2.9 KiB
TypeScript

import { APP_NAME, 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 ${APP_NAME} to load ${CONFIG_DIR_NAME} settings and resources, install missing project packages, and execute project extensions.`;
}
async function selectProjectTrustOption(
cwd: string,
ctx: ProjectTrustContext,
): Promise<ProjectTrustOption | undefined> {
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<boolean> {
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;
}