paperclip / server /src /services /agent-runtime-localization.ts
cjovs's picture
Deploy Paperclip CN to Hugging Face Space
96e86e5
import os from "node:os";
import {
DEFAULT_UI_LOCALE,
type UiLocale,
} from "@penclipai/shared";
type ResolveRuntimeLocalizationPromptInput = {
locale: UiLocale;
platform?: NodeJS.Platform;
shell?: string | null;
env?: NodeJS.ProcessEnv;
osRelease?: string | null;
};
type RuntimeEnvironmentDescriptor = {
labelZh: string;
labelEn: string;
};
function readNonEmptyString(value: unknown): string | null {
return typeof value === "string" && value.trim().length > 0 ? value : null;
}
function parseSupportedUiLocale(value: unknown): UiLocale | null {
const candidate = readNonEmptyString(value);
if (!candidate) return null;
const normalized = candidate.trim().toLowerCase();
if (normalized.startsWith("zh")) return "zh-CN";
if (normalized.startsWith("en")) return "en";
return null;
}
function stripExecutableName(value: string | null | undefined): string | null {
const trimmed = value?.trim();
if (!trimmed) return null;
const parts = trimmed.split(/[\\/]/g);
return parts[parts.length - 1] || trimmed;
}
function isPowerShellShell(shell: string | null, env: NodeJS.ProcessEnv): boolean {
if (shell && /(^|[\\/])(pwsh|powershell)(\.exe)?$/i.test(shell)) {
return true;
}
return Boolean(env.POWERSHELL_DISTRIBUTION_CHANNEL || env.PSExecutionPolicyPreference);
}
function isCmdShell(shell: string | null): boolean {
return Boolean(shell && /(^|[\\/])cmd(\.exe)?$/i.test(shell));
}
function isWslRuntime(platform: NodeJS.Platform, env: NodeJS.ProcessEnv, osRelease: string | null): boolean {
if (platform !== "linux") return false;
return Boolean(
env.WSL_DISTRO_NAME
|| env.WSL_INTEROP
|| (osRelease && /microsoft/i.test(osRelease)),
);
}
function resolveRuntimeEnvironment(
input: ResolveRuntimeLocalizationPromptInput,
): RuntimeEnvironmentDescriptor {
const platform = input.platform ?? process.platform;
const env = input.env ?? process.env;
const shell = input.shell?.trim()
|| env.SHELL?.trim()
|| env.ComSpec?.trim()
|| env.COMSPEC?.trim()
|| null;
const shellName = stripExecutableName(shell);
const osRelease = input.osRelease ?? (platform === "linux" ? os.release() : null);
if (isWslRuntime(platform, env, osRelease)) {
const wslShell = shellName ?? "sh";
return {
labelZh: `WSL ${wslShell}`,
labelEn: `WSL ${wslShell}`,
};
}
if (platform === "win32") {
if (isPowerShellShell(shell, env)) {
return {
labelZh: "Windows PowerShell",
labelEn: "Windows PowerShell",
};
}
if (isCmdShell(shell)) {
return {
labelZh: "Windows cmd.exe",
labelEn: "Windows cmd.exe",
};
}
if (shellName) {
return {
labelZh: `Windows shell (${shellName})`,
labelEn: `Windows shell (${shellName})`,
};
}
return {
labelZh: "Windows",
labelEn: "Windows",
};
}
if (shellName) {
return {
labelZh: `${shellName} on ${platform}`,
labelEn: `${shellName} on ${platform}`,
};
}
return {
labelZh: platform,
labelEn: platform,
};
}
function buildZhCnRuntimeLocalizationPrompt(environment: RuntimeEnvironmentDescriptor): string {
return [
"## 语言与运行时契约",
"- 输出契约:除非用户本轮明确要求其他语言,所有面向用户的自然语言输出必须使用简体中文;代码、命令、路径、API 字段名和日志原文保持原样。",
`- 宿主环境:${environment.labelZh}。`,
"- CLI 契约:执行 Paperclip 命令一律使用 `penclip ...`;仅在逐字引用用户文本、日志或历史文档时保留 `paperclipai ...`。",
"- API 契约:优先使用 `penclip` CLI 完成 Paperclip 操作;只有在 CLI 无法覆盖时,才直接调用 API,并确保带上必需的认证与运行标头。",
].join("\n");
}
function buildEnRuntimeLocalizationPrompt(environment: RuntimeEnvironmentDescriptor): string {
return [
"## Language and Runtime Contract",
"- Output contract: unless the current user request explicitly asks for another language, all user-facing natural-language output must be in English. Keep code, commands, file paths, API field names, and raw logs verbatim.",
`- Host runtime: ${environment.labelEn}.`,
"- CLI contract: use `penclip ...` for Paperclip commands; keep `paperclipai ...` only in verbatim quotes from user text, logs, or historical docs.",
"- API contract: prefer the `penclip` CLI for Paperclip operations; only call the HTTP API directly when the CLI cannot cover the action, and always include the required auth and run headers.",
].join("\n");
}
export function readRuntimeUiLocaleFromContextSnapshot(
contextSnapshot: Record<string, unknown> | null | undefined,
): UiLocale | null {
return parseSupportedUiLocale(contextSnapshot?.runtimeUiLocale);
}
export function resolveEffectiveRuntimeUiLocale(input: {
requestedUiLocale?: unknown;
runtimeUiLocale?: unknown;
runtimeDefaultLocale?: unknown;
}): UiLocale {
return (
parseSupportedUiLocale(input.requestedUiLocale) ??
parseSupportedUiLocale(input.runtimeUiLocale) ??
parseSupportedUiLocale(input.runtimeDefaultLocale) ??
DEFAULT_UI_LOCALE
);
}
export function resolveEffectiveRuntimeUiLocaleForContextSnapshot(
contextSnapshot: Record<string, unknown> | null | undefined,
runtimeDefaultLocale?: unknown,
): UiLocale {
return resolveEffectiveRuntimeUiLocale({
requestedUiLocale: contextSnapshot?.requestedUiLocale,
runtimeUiLocale: contextSnapshot?.runtimeUiLocale,
runtimeDefaultLocale,
});
}
export function resolveRuntimeLocalizationPrompt(
input: ResolveRuntimeLocalizationPromptInput,
): string {
const environment = resolveRuntimeEnvironment(input);
return input.locale === "en"
? buildEnRuntimeLocalizationPrompt(environment)
: buildZhCnRuntimeLocalizationPrompt(environment);
}