chore: initialize my-pi extension bundle

This commit is contained in:
云服务部-叶林立
2026-08-18 12:30:48 +08:00
parent 7b7b332912
commit 0637139a16
62 changed files with 11055 additions and 1 deletions
@@ -0,0 +1,138 @@
import { advanceQuoteEscapeState, readShellChars, type QuoteEscapeState } from "./shell-quote-state.js";
interface WindowsBashCompatibilityResult {
command: string;
applied: string[];
}
interface LeadingCdSlashDParse {
rawPath: string;
operator: string;
tail: string;
}
const PYTHON_UTF8_ENV_PREFIX = "PYTHONIOENCODING=utf-8";
function normalizeWindowsPathForBash(rawPath: string): string {
const trimmed = rawPath.trim();
const unquoted =
(trimmed.startsWith('"') && trimmed.endsWith('"')) ||
(trimmed.startsWith("'") && trimmed.endsWith("'"))
? trimmed.slice(1, -1)
: trimmed;
return unquoted.replace(/\\/g, "/");
}
function quoteForBash(value: string): string {
const escaped = value.replace(/"/g, '\\"');
return `"${escaped}"`;
}
function parseLeadingCdSlashD(command: string): LeadingCdSlashDParse | null {
const prefixMatch = command.match(/^\s*cd\s+\/d\s+/i);
if (!prefixMatch) {
return null;
}
const pathStart = prefixMatch[0].length;
const state: QuoteEscapeState = { quote: null, escaped: false };
for (let index = pathStart; index < command.length; index += 1) {
const { character, nextCharacter } = readShellChars(command, index);
if (advanceQuoteEscapeState(state, character, "\"'")) {
continue;
}
if (character === "&" && nextCharacter === "&") {
return {
rawPath: command.slice(pathStart, index),
operator: "&&",
tail: command.slice(index + 2),
};
}
if (character === "|" && nextCharacter === "|") {
return {
rawPath: command.slice(pathStart, index),
operator: "||",
tail: command.slice(index + 2),
};
}
if (character === "|" || character === ";") {
return {
rawPath: command.slice(pathStart, index),
operator: character,
tail: command.slice(index + 1),
};
}
}
return {
rawPath: command.slice(pathStart),
operator: "",
tail: "",
};
}
function rewriteLeadingCdSlashD(command: string): { command: string; changed: boolean } {
const parsed = parseLeadingCdSlashD(command);
if (!parsed) {
return { command, changed: false };
}
const normalizedPath = quoteForBash(normalizeWindowsPathForBash(parsed.rawPath));
if (!parsed.operator) {
return {
command: `cd ${normalizedPath}`,
changed: true,
};
}
return {
command: `cd ${normalizedPath} ${parsed.operator} ${parsed.tail.trimStart()}`,
changed: true,
};
}
function ensurePythonUtf8(command: string): { command: string; changed: boolean } {
if (/\bPYTHONIOENCODING\s*=/.test(command)) {
return { command, changed: false };
}
if (!/(^|[;&|]\s*|&&\s*|\|\|\s*)python(?:3(?:\.\d+)?)?\b/i.test(command)) {
return { command, changed: false };
}
return {
command: `${PYTHON_UTF8_ENV_PREFIX} ${command}`,
changed: true,
};
}
export function applyWindowsBashCompatibilityFixes(
command: string,
platform: string = process.platform,
): WindowsBashCompatibilityResult {
if (platform !== "win32") {
return { command, applied: [] };
}
let nextCommand = command;
const applied: string[] = [];
const cdFix = rewriteLeadingCdSlashD(nextCommand);
if (cdFix.changed) {
nextCommand = cdFix.command;
applied.push("cd-/d");
}
const pythonFix = ensurePythonUtf8(nextCommand);
if (pythonFix.changed) {
nextCommand = pythonFix.command;
applied.push("python-utf8");
}
return { command: nextCommand, applied };
}