源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
export type AgentPatchChangeType = "created" | "modified" | "deleted";
|
||||
|
||||
export function normalizeAgentPatchText(text: string): string {
|
||||
if (!text) {
|
||||
return text;
|
||||
}
|
||||
const contentField = tryExtractContentField(text);
|
||||
if (contentField) {
|
||||
return normalizeAgentPatchText(contentField);
|
||||
}
|
||||
const expanded =
|
||||
!text.includes("\n") && text.includes("\\n")
|
||||
? text.replace(/\\n/g, "\n")
|
||||
: text;
|
||||
return expanded
|
||||
.replace(/\r\n/g, "\n")
|
||||
.split("\n")
|
||||
.map((line) =>
|
||||
line.trimStart() === "\\ No newline at end of file"
|
||||
? "\\ No newline at end of file"
|
||||
: line
|
||||
)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
export function extractAgentPatchPath(patchText: string | null): string | null {
|
||||
const normalized = normalizeAgentPatchText(patchText ?? "");
|
||||
if (!normalized.trim()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const applyPatchPath = extractApplyPatchPath(normalized);
|
||||
if (applyPatchPath) {
|
||||
return applyPatchPath;
|
||||
}
|
||||
|
||||
const changeType = inferAgentPatchChangeType(normalized);
|
||||
const gitHeader = normalized.match(/^diff --git a\/(.+?) b\/(.+)$/m);
|
||||
const oldPath = gitHeader?.[1]?.trim() ?? null;
|
||||
const newPath = gitHeader?.[2]?.trim() ?? null;
|
||||
if (oldPath || newPath) {
|
||||
return changeType === "deleted"
|
||||
? (nonNullPath(oldPath) ?? nonNullPath(newPath))
|
||||
: (nonNullPath(newPath) ?? nonNullPath(oldPath));
|
||||
}
|
||||
|
||||
if (changeType === "deleted") {
|
||||
return extractOldHeaderPath(normalized);
|
||||
}
|
||||
return extractNewHeaderPath(normalized);
|
||||
}
|
||||
|
||||
export function inferAgentPatchChangeType(
|
||||
patchText: string | null
|
||||
): AgentPatchChangeType {
|
||||
const normalized = normalizeAgentPatchText(patchText ?? "").trim();
|
||||
if (!normalized) {
|
||||
return "modified";
|
||||
}
|
||||
if (
|
||||
/^(\*\*\* Delete File:|deleted file mode\b|\+\+\+ \/dev\/null$)/m.test(
|
||||
normalized
|
||||
)
|
||||
) {
|
||||
return "deleted";
|
||||
}
|
||||
if (
|
||||
/^(\*\*\* Add File:|new file mode\b|--- \/dev\/null$)/m.test(normalized)
|
||||
) {
|
||||
return "created";
|
||||
}
|
||||
return "modified";
|
||||
}
|
||||
|
||||
function extractApplyPatchPath(text: string): string | null {
|
||||
const match = text.match(/^\*\*\* (?:Add|Delete|Update) File:\s+(.+?)\s*$/m);
|
||||
return match?.[1]?.trim() || null;
|
||||
}
|
||||
|
||||
function extractOldHeaderPath(text: string): string | null {
|
||||
const match = text.match(/^---\s+(?:a\/)?([^\n]+)$/m);
|
||||
return nonNullPath(match?.[1]?.trim() ?? null);
|
||||
}
|
||||
|
||||
function extractNewHeaderPath(text: string): string | null {
|
||||
const match = text.match(/^\+\+\+\s+(?:b\/)?([^\n]+)$/m);
|
||||
return nonNullPath(match?.[1]?.trim() ?? null);
|
||||
}
|
||||
|
||||
function nonNullPath(path: string | null): string | null {
|
||||
if (!path || path === "/dev/null" || path === "dev/null") {
|
||||
return null;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
function tryExtractContentField(text: string): string | null {
|
||||
const trimmed = text.trim();
|
||||
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed) as unknown;
|
||||
if (
|
||||
parsed &&
|
||||
typeof parsed === "object" &&
|
||||
typeof (parsed as { content?: unknown }).content === "string"
|
||||
) {
|
||||
return (parsed as { content: string }).content;
|
||||
}
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user