File size: 5,920 Bytes
96e86e5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | 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);
}
|